標籤:connect 圖片 func response ret exist ora current sdn
App自己主動更新的步驟可分為三步:
- 檢查更新(假設有更新進行第2步,否則返回)
- 下載新版的APK安裝包
- 安裝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項目配置裡的versionCode和versionName(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功能介紹及使用示範範例
【Android】Android程式自己主動更新