当前位置: 首页 > article >正文

Android 检查更新

首先服务类

public class UpdateService extends Service {
    private static final String NOTIFY_CHANNEL_ID = "com.jianke.api.UpdateService";
    public static final String BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK = "BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK";
    public static final String BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK_SUCCESS = "BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK_SUCCESS";

    public static boolean isRunning = false; //是否正在运行
    public static final String URL = "url"; //Tag
    public static final String ICON = "icon"; //Tag
    public static final String MD5 = "md5"; //Tag
    private NotificationCompat.Builder builder;
    private Handler handler;//Handler对象
    private int lastPercent = 0;
    private NotificationManager notificationManager;//Class to notify the user of events that happen.
    private AuthInstallApkBroadcastReceiver mAuthInstallApkBroadcastReceiver;
    private String fileName = String.valueOf(System.currentTimeMillis());
    private UpdateListener updateListener;

    private class AuthInstallApkBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            installApk();
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mAuthInstallApkBroadcastReceiver = new AuthInstallApkBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter(UpdateService.BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK_SUCCESS);
        LocalBroadcastManager.getInstance(this).registerReceiver(mAuthInstallApkBroadcastReceiver, intentFilter);
    }

    @Override
    public void onDestroy() {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mAuthInstallApkBroadcastReceiver);
        updateListener = null;
        super.onDestroy();
        isRunning = false;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new UpdateServiceBinder();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        isRunning = true;
        if (intent == null) {
            return super.onStartCommand(intent, flags, startId);
        }
        String action = intent.getAction();

        if (!TextUtils.isEmpty(action)) {
            Toast.makeText(getApplicationContext(), "action is null", Toast.LENGTH_SHORT).show();
        } else {
            String url = intent.getStringExtra(URL);
            final String md5 = intent.getStringExtra(MD5);
            int icon = intent.getIntExtra(ICON, android.R.drawable.sym_def_app_icon);
            if (TextUtils.isEmpty(url)) {
//                if (BuildConfig.DEBUG) {
                    throw new RuntimeException("获取APK更新地址失败");
//                }
//                return super.onStartCommand(intent, flags, startId);
            } else {
                startUpdate(url, icon);
            }
            handler = new Handler(getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    if (builder != null) {
                        switch (msg.what) {
                            case 1:
                                builder.setProgress(100, (Integer) msg.obj, false);
                                builder.setContentText((Integer) msg.obj + "%");
                                if (notificationManager == null || builder == null) {
                                    return;
                                }
                                notificationManager
                                        .notify(R.id.update_notification_id, builder.build());
                                break;
                            case 2:
                                notificationManager.cancel(R.id.update_notification_id);

                                getFileMD5(md5);

                                break;


                            case 3:  //校验md5 结果处理 以及安装

                                String fileHash = (String) msg.obj;
                                LogUtils.d("md5===" + fileHash);
                                // 校验hash 忽略 md5大小写
                                if (msg.arg1 == 1 && (TextUtils.isEmpty(md5) || (!TextUtils.isEmpty(fileHash) && fileHash.equalsIgnoreCase(md5)))) {
                                    isRunning = false;
                                    if(updateListener != null) updateListener.onMd5Checked(getApkPath());
                                    installApk();

                                } else {
                                    isRunning = false;
                                    if(updateListener != null) updateListener.onError("文件错误");
                                    ToastUtil.setToast("文件错误");
                                    stopSelf();
                                }

                                break;

                            case 4:
                                isRunning = false;
                                if(updateListener != null) updateListener.onError(msg.obj + "");
                                ToastUtil.setToast(msg.obj + "");
                                notificationManager.cancel(R.id.update_notification_id);
                                stopSelf();
                                break;

                            default:
                                break;
                        }
                    }
                }
            };
        }

        return super.onStartCommand(intent, flags, startId);
    }

    public void setUpdateListener(UpdateListener updateListener) {
        this.updateListener = updateListener;
    }

    /**
     * 校验文件md5
     *
     * @param md5
     */
    private void getFileMD5(String md5) {

        if (!TextUtils.isEmpty(md5)) {

            new Thread(new Runnable() {   //300M 耗时 ≈ 3S
                @Override
                public void run() {
                    try {

                        File file = new File(getApkPath());

                        String fileHash = MD5Utils.digestMD5(file);

                        sendCheckFileMsg(true, fileHash);

                    } catch (Exception e) {
                        e.printStackTrace();
                        sendCheckFileMsg(false, "");
                    }

                }
            }).start();

        } else {  //md5 空算成功

            sendCheckFileMsg(true, "");
        }
    }

    private void sendCheckFileMsg(boolean success, String hash) {

        Message msg = Message.obtain();
        msg.what = 3;
        msg.arg1 = success ? 1 : 0;
        msg.obj = hash;
        handler.sendMessage(msg);

    }

    private void installApk() {
        File file = new File(getApkPath());
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !getPackageManager().canRequestPackageInstalls()) {
            ToastUtil.setToast("请授权安装应用");
            requestAutoInstallApk();
            return;
        }

        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".updateprovider", file);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(file),
                    "application/vnd.android.package-archive");
            intent.addCategory("android.intent.category.DEFAULT");
        }
        startActivity(intent);
        stopSelf();
    }

    private void requestAutoInstallApk() {
        isRunning = false;
        LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK));
    }

    /**
     * 开始下载
     *
     * @param url apk的url
     */
    private void startUpdate(String url, int icon) {
        createNotification(icon); //创建通知栏进度
        startDownLoad(url);
    }

    /**
     * 创建通知栏进度
     */
    private void createNotification(int icon) {
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    NOTIFY_CHANNEL_ID,
                    "TAG",
                    NotificationManager.IMPORTANCE_DEFAULT

            );
            channel.enableLights(true);
            channel.setShowBadge(true);
            channel.setDescription("update version");
            notificationManager.createNotificationChannel(channel);
        }
        notificationManager.cancel(R.id.update_notification_id);
        builder = new NotificationCompat.Builder(getApplicationContext(), NOTIFY_CHANNEL_ID);
        builder.setSmallIcon(icon).setContentTitle("正在下载 ...")
                .setContentText("0%");
        builder.setPriority(2 | Notification.DEFAULT_ALL);
        builder.setProgress(100, 0, false);
        builder.setOnlyAlertOnce(true);
        builder.setOngoing(true);

        if (notificationManager == null) {
            return;
        }
        notificationManager.notify(R.id.update_notification_id, builder.build());
    }

    /**
     * 开始下载
     *
     * @param url
     */
    private void startDownLoad(String url) {
        DownLoadManager.getInstance().downLoad(this, url, new DownLoadManager.DownLoadListener() {

            @Override
            public void onStartLoading(long totalSize) {
                // Do nothing because of auto-generated
                if(updateListener != null) updateListener.onStart(totalSize);
            }

            @Override
            public void onLoading(long currentSize, float percent, float speed) {

                int tempPercent = (int) (percent * 100);
                if (tempPercent >= 0 && lastPercent != tempPercent) {  //避免频繁调用通知

                    Message msg = Message.obtain();
                    msg.what = 1;
                    msg.obj = tempPercent;
                    handler.sendMessage(msg);

                    lastPercent = tempPercent;
                    if(updateListener != null) updateListener.onLoading(currentSize, percent, speed);
                }
            }

            @Override
            public void onLoadingFinish(long totalSize) {
                Message msg = Message.obtain();
                msg.what = 2;
                handler.sendMessage(msg);
                if(updateListener != null) updateListener.onLoadingFinish(totalSize);
            }

            @Override
            public void onFailure(String error) {

                Message msg = Message.obtain();
                msg.what = 4;
                msg.obj = error;
                handler.sendMessage(msg);
            }
        }, new File(getApkPath()));
    }

    /**
     * 获取apk下载路径
     *
     * @return
     */
    private String getApkPath() {
        boolean old = Build.VERSION.SDK_INT < Build.VERSION_CODES.N;
        return old ?
                getFileStreamPath(fileName + ".apk").getAbsolutePath()
                : getAppRootDir() + fileName + ".apk";
    }

    /**
     * 获取sdcard的绝对路径
     *
     * @return
     */
    public String getSDcardDir() {
        String sdcardPath = null;
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            sdcardPath = Environment.getExternalStorageDirectory().getAbsolutePath();
        } else {
            sdcardPath = getApplicationContext().getFilesDir().getAbsolutePath();
        }

        return sdcardPath;
    }

    /**
     * 获取应用跟目录
     *
     * @return
     */
    public String getAppRootDir() {
        return getFilesDir().getAbsolutePath() + "/";
    }

    public class UpdateServiceBinder extends Binder {

        public UpdateService getService(){
            return UpdateService.this;
        }
    }
}
UpdateListener
public interface UpdateListener {
    /**
     * @description 开始升级
     * @param totalSize 安装包体积
     */
    void onStart(long totalSize);

    /**
     * @description 正在下载
     */
    void onLoading(long currentSize, float percent, float speed);

    /**
     * @description 下载完成
     * @param totalSize 安装包体积
     */
    void onLoadingFinish(long totalSize);

    /**
     * @description md5校验成功
     */
    void onMd5Checked(String path);

    /**
     * @description 升级失败
     * @param error
     */
    void onError(String error);
}
DownLoadManager
public class DownLoadManager {

    private static final String MAIN = "main"; //Tag
    private static DownLoadManager instance = new DownLoadManager(); //单例对象

    /**
     * 对外公布的单例对象
     *
     * @return
     */
    public static DownLoadManager getInstance() {
        return instance;
    }

    /**
     * 下载
     *
     * @param uri        url
     * @param listener   下载DownLoadListener监听对象
     * @param targetFile 目标文件
     */
    public void downLoad(final Context context, final String uri, final DownLoadListener listener,
                         final File targetFile) {
        if (MAIN.equalsIgnoreCase(Thread.currentThread().getName())) {
            new Thread() {
                @Override
                public void run() {
                    downloadNewThread(context, uri, listener, targetFile);
                }

                ;
            }.start();
        } else {
            downloadNewThread(context, uri, listener, targetFile);
        }
    }

    /**
     * 新开一个线程执行下载操作
     *
     * @param uri
     * @param listener
     * @param targetFile
     */
    private void downloadNewThread(Context context, String uri, DownLoadListener listener,
                                   File targetFile) {

        FileOutputStream fileOutputStream = null;
        InputStream inputStream = null;
        //
        try {
            URL url = new URL(uri);
            //获取连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(60 * 1000);
            connection.setReadTimeout(60 * 1000);
            connection.setRequestProperty("Connection", "Keep-Alive");
            connection.setRequestProperty("Charset", "UTF-8");
            connection.setDoInput(true);
            connection.setUseCaches(false);
            //打开连接
            connection.connect();
            //获取内容长度
            int contentLength = connection.getContentLength();

            if (listener != null) {
                listener.onStartLoading(contentLength);
            }

            File parent = targetFile.getParentFile();
            if (!parent.exists()) {
                parent.mkdirs();
            } else if (!parent.isDirectory()) {
                if (parent.delete()) {
                    parent.mkdirs();
                }
            }

            //输入流
            inputStream = connection.getInputStream();

            //输出流
            boolean old = Build.VERSION.SDK_INT < Build.VERSION_CODES.N;
            fileOutputStream = old ?
                    context.openFileOutput(targetFile.getName(), Context.MODE_WORLD_READABLE)
                    : new FileOutputStream(targetFile);

            byte[] bytes = new byte[1024];
            long totalReaded = 0;
            int temp_Len;
            long currentTime = System.currentTimeMillis();
            while ((temp_Len = inputStream.read(bytes)) != -1) {
                totalReaded += temp_Len;
//                Log.i("XXXX", "run: totalReaded:" + totalReaded);
//                long progress = totalReaded * 100 / contentLength;
//                Log.i("XXXX", "run: progress:" + progress);
                fileOutputStream.write(bytes, 0, temp_Len);

                if (listener != null) {
                    listener.onLoading(totalReaded, ((float) totalReaded) / contentLength,
                            ((float) temp_Len) / System.currentTimeMillis()
                                    - currentTime);
                }
                currentTime = System.currentTimeMillis();
            }

            if (listener != null) {
                listener.onLoadingFinish(contentLength);
            }

        } catch (Exception e) {
            e.printStackTrace();
            if (listener != null) {
                listener.onFailure(e.getMessage());
            }

        } finally {
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
                if (inputStream != null) {
                    inputStream.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 声明DownLoadListener监听器
     */
    public interface DownLoadListener {
        /**
         * 开始下载
         *
         * @param totalSize
         */
        void onStartLoading(long totalSize);

        /**
         * 下载中
         *
         * @param currentSize byte
         * @param percent
         * @param speed       byte/second
         */
        void onLoading(long currentSize, float percent, float speed);

        /**
         * 下载完成
         *
         * @param totalSize
         */
        void onLoadingFinish(long totalSize);

        /**
         * 下载失败
         *
         * @param error
         */
        void onFailure(String error);
    }
}

使用

 var intent = Intent(this@MainActivity, UpdateService::class.java).apply {
                    putExtra(UpdateService.URL, it.downloadUrl)
                    putExtra(UpdateService.ICON, R.drawable.ic_launcher)
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    this@MainActivity.startForegroundService(intent)
                } else {
                    this@MainActivity.startService(intent)
                }


http://www.kler.cn/a/577059.html

相关文章:

  • 如果布隆过滤器挂了,里边存的数据全丢失了,怎么恢复呢?
  • Jmeter进行http接口测试详解
  • Java 大视界 -- Java 大数据在智能家居能源管理与节能优化中的应用(120)
  • C语言基础系列【20】内存管理
  • Pico 4 Enterprise(企业版)与Unity的交互-打包运行及UI交互篇
  • C++25--lambda表达式
  • 51 单片机中断控制寄存器 TCON
  • Qt 实现会转动风扇效果
  • 探秘 C 语言:编程世界的基石与传奇
  • 【统计至简】【入门测试1】给定数据矩阵X,如何求其质心、中心化数据、标准化数据、格拉姆矩阵、协方差矩阵、相关系数矩阵
  • 在Windows系统上安装和配置Redis服务
  • [c语言日寄]结构体:内存对齐
  • 【贪心算法1】
  • OkHttp 连接池模块原理深度剖析
  • 软考高项笔记 1.1.1 信息
  • 详解常用集合和映射中的线程安全问题
  • 元组(Tuple)详解——c#
  • Android Studio 创建项目同步失败
  • Oxidized收集H3C交换机网络配置报错,not matching configured prompt (?-mix:^(<CD>)$)
  • 三、零基础学习TypeScript——JavaScript和TypeScript数据类型区别