Android:实现应用版本更新

〇、前言

应用版本更新作为Android应用的基础功能是每个应用都必须具有的,这个功能实现起来也有多种方式。前段时间我们项目改版,重新梳理了应用更新的逻辑,功能本身是比较简单的,但是各种可能的异常情况还是挺多的,特此进行记录。

一、使用OkHttp实现应用版本更新

主要有三个方法:首先检测是否有新的版本,然后进行APK文件下载,最后安装新的APK。

检测新版本的方法如下:

    /**
     * 检测是否有新的版本,如有则进行下载更新:
     * 1. 请求服务器, 获取数据;2. 解析json数据;3. 判断是否有更新;4. 弹出升级弹窗或直接进入主页面
     */
    private void checkVersion() {
        showLaunchInfo("正在检测是否有新版本...", false);
        String checkUrl = "http://localhost:8080/AndroidAPK/AndroidUpdate.json";

        OkHttpClient okHttpClient = new OkHttpClient();//创建 OkHttpClient 对象
        Request request = new Request.Builder().url(checkUrl).build();//创建 Request
        okHttpClient.newCall(request).enqueue(new Callback() {//发送请求
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                Log.w(TAG, "onFailure: e = " + e.getMessage());
                mProcess = 30;
                showLaunchInfo("新版本检测失败,请检查网络!", true);
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) {
                try {
                    Log.w(TAG, "onResponse: response = " + response);
                    mProcess = 30;
                    final ResponseBody responseBody = response.body();
                    if (response.isSuccessful() && responseBody != null) {
                        final String responseString = responseBody.string();
                        Log.w(TAG, "onResponse: responseString = " + responseString);
                        //解析json
                        final JSONObject jo = new JSONObject(responseString);
                        final String versionName = jo.getString("VersionName");
                        final int versionCode = jo.getInt("VersionCode");
                        final String versionDes = jo.getString("VersionDes");
                        final String versionUrl = jo.getString("VersionUrl");
                        //本地版本号和服务器进行比对, 如果小于服务器, 说明有更新
                        if (BuildConfig.VERSION_CODE < versionCode) {
                            //本地版本小于服务器版本,存在新版本
                            showLaunchInfo("检测到新版本!", false);
                            progressBar.setProgress(mProcess);
                            //有更新, 弹出升级对话框
                            showUpdateDialog(versionDes, versionUrl);
                        } else {
                            showLaunchInfo("该版本已是最新版本,正在初始化项目...", true);
                        }
                    } else {
                        showLaunchInfo("新版本检测失败,请检查服务!", true);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    showLaunchInfo("新版本检测出现异常,请检查服务!", true);
                }
            }
        });
    }

下载APK文件的方法如下:

    private void downloadNewApk(String apkName) {
        showLaunchInfo("检测到新版本,正在下载...", false);
        final File downloadDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
        if (downloadDir != null && downloadDir.exists() && downloadDir.isDirectory()) {
            //删除(/storage/emulated/0/Android/data/包名/files/Download)文件夹下的所有文件,避免一直下载文件堆积
            File[] files = downloadDir.listFiles();
            if (files != null) {
                for (final File file : files) {
                    if (file != null && file.exists() && file.isFile()) {
                        boolean delete = file.delete();
                    }
                }
            }
        }
        //显示进度条
        final ProgressDialog dialog = new ProgressDialog(this);
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//水平方向进度条, 可以显示进度
        dialog.setTitle("正在下载新版本...");
        dialog.setCancelable(false);
        dialog.show();
        //APK文件路径
        final String url = "http://localhost:7090/AndroidAPK/" + apkName;
        Request request = new Request.Builder().url(url).build();
        new OkHttpClient().newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                e.printStackTrace();
                String strFailure = "新版本APK下载失败";
                showLaunchInfo(strFailure, false);
                showFailureDialog(strFailure, apkName);
                dialog.dismiss();
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) {
                try {
                    final ResponseBody responseBody = response.body();
                    if (response.isSuccessful() && responseBody != null) {
                        final long total = responseBody.contentLength();
                        final InputStream is = responseBody.byteStream();
                        final File file = new File(downloadDir, apkName);
                        final FileOutputStream fos = new FileOutputStream(file);

                        int len;
                        final byte[] buf = new byte[2048];
                        long sum = 0L;
                        while ((len = is.read(buf)) != -1) {
                            fos.write(buf, 0, len);
                            sum += len;
                            float downloadProgress = (sum * 100F / total);
                            dialog.setProgress((int) downloadProgress);//下载中,更新进度
                            progressBar.setProgress(mProcess + (int) (downloadProgress * 0.7));
                        }
                        fos.flush();
                        responseBody.close();
                        is.close();
                        fos.close();
                        installAPKByFile(file);//下载完成,开始安装
                    } else {
                        String strFailure = "新版本APK获取失败";
                        showLaunchInfo(strFailure, false);
                        showFailureDialog(strFailure, apkName);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    String strException = "新版本APK下载安装出现异常";
                    showLaunchInfo(strException, false);
                    showFailureDialog(strException, apkName);
                } finally {
                    /*正常应该在finally中进行关流操作,以避免异常情况时没有关闭IO流,导致内存泄露
                     *因为本场景下异常情况可能性较小,为了代码可读性直接在正常下载结束后关流
                     */
                    dialog.dismiss();//dialog消失
                }
            }
        });
    }

安装新APK的方法如下:

    /**
     * 7.0以上系统APK安装
     */
    private void installAPKByFile(File file) {
        showLaunchInfo("新版本下载成功,正在安装中...", false);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        //intent.putExtra("pwd", "soft_2694349");//根据密码判断升级文件是否允许更新
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        Uri uri = FileProvider.getUriForFile(this, "com.lxb.demo0325.fileProvider", file);
        //intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        startActivityForResult(intent, REQUEST_INSTALL);
    }

如上,代码并不难,主要是各种可能的异常判断,为了代码的完整性,我将整个应用版本更新Demo进行了上传:https://github.com/beita08/AppUpdate

其中,服务端配置的更新文件,可以是Json形式的:

{
    "VersionName":"3.1.1.2",
    "VersionCode":545,
    "VersionDes":"发现新版本, 赶紧更新吧!",
    "VersionUrl":"ApkName"
}

参考资料:https://www.cnblogs.com/xiaoxiaoqingyi/p/7003241.html

二、使用三方服务进行应用版本更新

应用版本更新作为通用的统一功能也可以使用三方提供的服务进行,最常用的就是腾讯的bugly。

腾讯Bugly是腾讯为开发者提供的一种三方服务,主要包含有:异常上报、运营统计、应用升级等三大功能,对于我们可以使用其应用升级和异常上报功能,官网地址为:https://bugly.qq.com/v2/。首先使用Bugly功能需要在客户端集成其相关SDK:

bugly功能介绍:

1、应用升级:

将我们更新的apk文件上传至bugly后台,设置下发策略(其中可选强制升级和下发上限等,也可填写更新说明),客户端检测到bugly后台有新版本就会进行下载。

应用升级官网介绍连接:https://bugly.qq.com/docs/introduction/app-upgrade-introduction/?v=20180709165613

2、异常上报

客户端集成后,对于线上用户使用过程中的出现的异常问题,会汇总至bugly后台自动进行汇总分类,便于分析用户端出现的问题。(此日志在用户端设备上),bugly的推出初衷就是汇总异常信息。

异常上报官网介绍连接:https://bugly.qq.com/docs/introduction/bugly-introduction/?v=20181014122344#_3

bugly优缺点分析:

优点:

1、应用升级使用第三方服务后不需要占用我们自己的服务器资源,升级并发量也有保障。

2、异常上报可以监测用户端异常情况,这部分我们平时自己较难检测。

缺点:

1、信息安全问题:客户端需集成相关SDK并且需要将我们的APK文件上传至三方服务器上;

2、更新应用时平板终端需要连接bugly后台外网;

3、我们各个地区的apk文件需要进行区分,避免下载时串线(都从bugly后台下载)。

 

 

猜你喜欢

转载自blog.csdn.net/beita08/article/details/115251372