zoukankan      html  css  js  c++  java
  • 【Android】Android程序自己主动更新

    App自己主动更新的步骤可分为三步:

    1. 检查更新(假设有更新进行第2步,否则返回)
    2. 下载新版的APK安装包
    3. 安装APK

    以下对这三步进行解释。当中会穿插相应代码。App自己主动更新的这三步所有被封装到了一个单独的Updater类中,能够直接拿来使用,我会在文章最后贴出源代码github地址。

    Updater 使用演示样例

    通过单一的类Updater能够方便的实现自己主动检查更新、下载安装包和自己主动安装。能够监听下载进度,能够自己定义更新提示等。保存路径能够自由书写,假设路径中某个文件夹不存在会自己主动创建。流式API接口易于使用。以下是使用演示样例。一行代码搞定自己主动更新:

    String savePath = Environment.getExternalStorageDirectory() 
                        + "/whinc/download/whinc.apk";
    String updateUrl = "http://192.168.1.168:8000/update.xml";
    Updater.with(mContext)
            .downloadListener(mListener)
            .update(updateUrl)
            .save(savePath)
            .create()
            .checkUpdate();

    这里写图片描写叙述

    第一步:检查更新

    这一步须要服务端的配合。服务端存放一个XML格式的配置文件(也能够用JSON或其它格式)提供给client检查更新。update.xml 格式例如以下:

    <?xml version="1.0" encoding="utf-8"?>
    <info>
        <version>
            <code>4</code>
            <name>1.0.4</name>
        </version>
        <url>http://192.168.1.168:8000/test.apk</url>
        <description>更新 - 吧啦吧啦;修复 - 吧啦吧啦;添加 - 巴拉巴拉巴</description>
    </info>
    • <version>标签指定服务端的版本号号和版本号名称,该版本号号和版本号名称相应Android项目配置里的versionCodeversionName(Eclipse ADT项目可在 AndroidManifest.xml中的标签中找到。Android Studio项目在module的build.gradle中的defaultConfig中找到)。
    • <url>标签指定APK的下载地址,
    • <description>标签指定更新内容。

    client通过 HTTP 请求服务端的 update.xml文件。然后解析 update.xml,比較服务端的版本号号与本地版本号号,假设服务端版本号号大于本地版本号号说明有更新,则依据 update.xml中指定的APK下载地址下载最新的APK,以下将会具体说明。

    以下是检查更新的代码:

        /**
         * 检查 App 版本号号
         *
         * @return 假设有新版本号返回true。否则返回false
         */
        private boolean checkVersion() {
            URL url;
            HttpURLConnection httpConn = null;
            try {
                url = new URL(mCheckUpdateUrl);
                httpConn = (HttpURLConnection) url.openConnection();
                httpConn.setConnectTimeout(200000);
                httpConn.setReadTimeout(200000);
                httpConn.setUseCaches(false);       // disable cache for current http connection
                httpConn.connect();
                if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    InputStream inputStream = httpConn.getInputStream();
                    // 解析 XML 数据
                    if (!parseXml(inputStream)) {
                        return false;
                    }
                    // 比較本地版本号号与服务器版本号号
                    PackageInfo packageInfo = mContext.getPackageManager()
                            .getPackageInfo(mContext.getPackageName(), 0);
                    if (packageInfo.versionCode < mRemoteVersionCode) {
                        return true;
                    }
                } else {
                    return false;
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            } finally {
                httpConn.disconnect();
            }
    
            return false;
        }

    首先创建HTTPURLConnection訪问服务端update.xml文件,然后解析服务端返回的update.xml文件,并保存版本号信息、APK下载地址和更新日志。解析完后通过获取当前client的版本号号与服务端版本号号比較。假设服务端版本号号更大,说明服务端有更新的版本号。checkVersion() 方法返回true,否则返回false。

    以下时检查更新的代码。须要注意的是。Android中不同意在主线程(UI线程)中发起网络请求,所以checkVersion()的调用须要放在非主线程中。实现异步请求的方式有多种,这里我使用 AsyncTask。

        public void checkUpdate() {
            new AsyncTask<Void, Void, Boolean>() {
    
                @Override
                protected Boolean doInBackground(Void... params) {
                    boolean hasNewVersion = checkVersion();
                    return hasNewVersion;
                }
    
                @Override
                protected void onPostExecute(Boolean hasNewVersion) {
                    super.onPostExecute(hasNewVersion);
    
                    if (mCheckUpdateListener == null
                            || !mCheckUpdateListener.onCompleted(hasNewVersion, mRemoteVersionCode,
                            mRemoteVersionName, mUpdateLog, mApkDownloadUrl)) {
                        if (hasNewVersion) {
                            showUpdateDialog();
                        }
                    }
                }
            }.execute();
        }

    下载新版的APK安装包

    showUpdateDialog()调用后显示更新提示对话框,在对话框确认button点击事件中,首先创建DownloadManager.Request对象,然后设置该对象的各种属性例如以下载保存路径、通知栏标题等,最后将该下载请求放到系统服务DownloadManager的下载队列中。交给系统去处理下载逻辑。 为了监听下载完毕事件,代码里注冊了广播DownloadManager.ACTION_DOWNLOAD_COMPLETE。下载进度通过注冊ContentObserver来监听。

        /**
         * 显示更新对话框
         */
        private void showUpdateDialog() {
            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
            builder.setTitle(mTitle);
            builder.setMessage(mUpdateLog);
            builder.setPositiveButton(mDialogOkBtnTxt, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
    
                    // 后台下载
                    mDownloadMgr = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
                    DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mApkDownloadUrl));
                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                        // 假设保存路径包括子文件夹,须要先递归创建文件夹
                        if (!createDirIfAbsent(mSavePath)) {
                            Log.e("TAG", "apk save path can not be created:" + mSavePath);
                            return;
                        }
    
                        request.setDestinationUri(Uri.fromFile(new File(mSavePath)));
                        request.setTitle(mNotificationTitle);
                        request.setTitle(mNotificationMessage);
                        // 注冊广播,监听下载完毕事件
                        mContext.registerReceiver(mCompleteReceiver,
                                new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
                        // 注冊监听下载进度
                        mContext.getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"),
                                true, mContentObserver);
                        mDownloadId = mDownloadMgr.enqueue(request);
                    } else {
                        Log.e("TAG", "can not access external storage!");
                        return;
                    }
                    Toast.makeText(mContext, "正在后台下载...", Toast.LENGTH_SHORT).show();
                }
            });
            builder.setNegativeButton(mDialogCancelBtnTxt, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });
            builder.create().show();
        }
    
        /**
         * 假设參数 path 指定的路径中的文件夹不存在就创建指定文件夹
         *
         * @param path 绝对路径(包括文件名称,比如 '/sdcard/storage/download/test.apk')
         * @return 假设成功创建文件夹返回true,否则返回false
         */
        private boolean createDirIfAbsent(String path) {
            String[] array = path.trim().split(File.separator);
            List<String> dirNames = Arrays.asList(array).subList(1, array.length - 1);
            StringBuilder pathBuilder = new StringBuilder(File.separator);
            for (String d : dirNames) {
                pathBuilder.append(d);
                File f = new File(pathBuilder.toString());
                if (!f.exists() && !f.mkdir()) {
                    return false;
                }
                pathBuilder.append(File.separator);
            }
            return true;
        }
    

    安装APK

    一旦Apk下载完毕就会收到广播消息,此时能够运行安装APK的动作,只是要先通过下载Id推断该广播事件是否是由于我们的APK下载完毕发出的,由于系统可能同一时候有多个下载任务,通过下载id区分。

            mCompleteReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                    if (downloadId == mDownloadId) {
                        installApk();
                        release();
                    }
                }
            };

    以下是 installApk() 方法,首先通过下载Id从DownloadManager中检索到下载的APK存储路径,然后通过Intent安装下载的APK,代码很easy。注意,Intent设置标识为Intent.FLAG_ACTIVITY_NEW_TASK。否则不能正常启动安装程序。

        /**
         * 替换安装当前App。注意:签名一致
         */
        private void installApk() {
            // 获取下载的 APK 地址
            Uri apkUri = mDownloadMgr.getUriForDownloadedFile(mDownloadId);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            mContext.startActivity(intent);
        }

    github 源代码

    whinc/Android-UpdateManager

    比較好的參考资料:

    DownloadManager | Android Developers
    Android系统下载管理DownloadManager功能介绍及使用演示样例

  • 相关阅读:
    今天写一篇随想,也当是回顾过去,展望未来吧。
    推荐 Word、EXCEL必备工具箱
    elasticsearch Routing 路由详解
    Python学习之字典
    ES 分片和副本数 调整及数据写入、重建索引调优
    fastJson JSON.parseObject()丢失字符串原本顺序
    Python 列表(详)
    pycharm常用快捷键
    Python学习笔记二(列表)
    python学习笔记二
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7073310.html
Copyright © 2011-2022 走看看