AndroidAndroid程序自己主动更新

仓廪实则知礼节,衣食足则知荣辱。这篇文章主要讲述AndroidAndroid程序自己主动更新相关的知识,希望能为你提供帮助。
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();

AndroidAndroid程序自己主动更新

文章图片

第一步:检查更新
这一步须要服务端的配合。服务端存放一个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,以下将会具体说明。
【AndroidAndroid程序自己主动更新】以下是检查更新的代码:
/** * 检查 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功能介绍及使用演示样例



    推荐阅读