Android 使用 DownloadManager 實現APP應用內更新升級
阿新 • • 發佈:2019-01-05
具體實現思路:
我們通過downloaderManager來下載apk,並且本地儲存downManager.enqueue(request)返回的id值,並且通過這個id獲取apk的下載檔案路徑和下載的狀態,並且通過狀態來更新通知欄的顯示。
第一次下載成功,彈出安裝介面
如果使用者沒有點選安裝,而是按了返回鍵,在某個時候,又再次使用了我們的APP
如果下載成功,則判斷本地的apk的包名是否和當前程式是相同的,並且本地apk的版本號大於當前程式的版本,如果都滿足則直接啟動安裝程式。
- 通過BroadcastReceiver來監聽是否下載完成
檔案下載管理的實現,包括建立request和加入佇列下載,通過返回的id來獲取下載路徑和下載狀態。
public class FileDownloadManager { private DownloadManager downloadManager; private Context context; private static FileDownloadManager instance; private FileDownloadManager(Context context) { downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); this.context = context.getApplicationContext(); } public static FileDownloadManager getInstance(Context context) { if (instance == null) { instance = new FileDownloadManager(context); } return instance; } /** * @param uri * @param title* @param description * @return download id */ public long startDownload(String uri, String title, String description,String appName) { DownloadManager.Request req = new DownloadManager.Request(Uri.parse(uri)); 。 //設定用於下載時的網路型別,預設任何網路都可以,提供:NETWORK_BLUETOOTH、NETWORL_MOBILE、NETWORK_WIFI req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); //設定漫遊狀態下是否可以下載 //req.setAllowedOverRoaming(false); //VISIBILTY_HIDDEN: Notification:將不會顯示,如果設定該屬性的話,必須要新增許可權 。 Android.permission.DOWNLOAD_WITHOUT_NOTIFICATION. //VISIBILITY_VISIBLE: Notification顯示,但是隻是在下載任務執行的過程中顯示,下載完成自動消失。(預設值) // VISIBILITY_VISIBLE_NOTIFY_COMPLETED : Notification顯示,下載進行時,和完成之後都會顯示。 //VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION :只有當任務完成時,Notification才會顯示。 req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //設定檔案的儲存的位置[三種方式] //第一種 //file:///storage/emulated/0/Android/data/your-package/files/Download/update.apk //這個檔案是應用專屬,軟體解除安裝後,下載的檔案也將全部刪除 req.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, appName+".apk"); //第二種 //file:///storage/emulated/0/Download/update.apk //req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "update.apk"); //第三種 自定義檔案路徑 //req.setDestinationUri() // 設定一些基本顯示資訊 req.setTitle(title); req.setDescription(description); //req.setMimeType("application/vnd.android.package-archive"); return downloadManager.enqueue(req);//非同步 //dm.openDownloadedFile() } /** * 獲取檔案儲存的路徑 * * @param downloadId an ID for the download, unique across the system. * This ID is used to make future calls related to this download. * @return file path * @see FileDownloadManager#getDownloadUri(long) */ public String getDownloadPath(long downloadId) { DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor c = downloadManager.query(query); if (c != null) { try { if (c.moveToFirst()) { return c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI)); } } finally { c.close(); } } return null; } /** * 獲取儲存檔案的地址 * * @param downloadId an ID for the download, unique across the system. * This ID is used to make future calls related to this download. * @see FileDownloadManager#getDownloadPath(long) */ public Uri getDownloadUri(long downloadId) { return downloadManager.getUriForDownloadedFile(downloadId); } public DownloadManager getDownloadManager() { return downloadManager; } /** * 獲取下載狀態 * * @param downloadId an ID for the download, unique across the system. * This ID is used to make future calls related to this download. * @return int * @see DownloadManager#STATUS_PENDING * @see DownloadManager#STATUS_PAUSED * @see DownloadManager#STATUS_RUNNING * @see DownloadManager#STATUS_SUCCESSFUL * @see DownloadManager#STATUS_FAILED */ public int getDownloadStatus(long downloadId) { DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor c = downloadManager.query(query); if (c != null) { try { if (c.moveToFirst()) { return c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); } } finally { c.close(); } } return -1; } }
app的檢測安裝的實現:
public class DownLoadApk { public static final String TAG = DownLoadApk.class.getSimpleName(); public static void download(Context context, String url, String title,final String appName) { // 獲取儲存ID SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); long downloadId =sp.getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L); if (downloadId != -1L) { FileDownloadManager fdm = FileDownloadManager.getInstance(context); int status = fdm.getDownloadStatus(downloadId); if (status == DownloadManager.STATUS_SUCCESSFUL) { //啟動更新介面 Uri uri = fdm.getDownloadUri(downloadId); if (uri != null) { if (compare(getApkInfo(context, uri.getPath()), context)) { startInstall(context, uri); return; } else { fdm.getDownloadManager().remove(downloadId); } } start(context, url, title,appName); } else if (status == DownloadManager.STATUS_FAILED) { start(context, url, title,appName); } else { Log.d(TAG, "apk is already downloading"); } } else { start(context, url, title,appName); } } private static void start(Context context, String url, String title,String appName) { long id = FileDownloadManager.getInstance(context).startDownload(url, title, "下載完成後點選開啟",appName); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); sp.edit().putLong(DownloadManager.EXTRA_DOWNLOAD_ID,id).commit(); Log.d(TAG, "apk start download " + id); } public static void startInstall(Context context, Uri uri) { Intent install = new Intent(Intent.ACTION_VIEW); install.setDataAndType(uri, "application/vnd.android.package-archive"); install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install); } /** * 獲取apk程式資訊[packageName,versionName...] * * @param context Context * @param path apk path */ private static PackageInfo getApkInfo(Context context, String path) { PackageManager pm = context.getPackageManager(); PackageInfo info = pm.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES); if (info != null) { return info; } return null; } /** * 下載的apk和當前程式版本比較 * * @param apkInfo apk file's packageInfo * @param context Context * @return 如果當前應用版本小於apk的版本則返回true */ private static boolean compare(PackageInfo apkInfo, Context context) { if (apkInfo == null) { return false; } String localPackage = context.getPackageName(); if (apkInfo.packageName.equals(localPackage)) { try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(localPackage, 0); if (apkInfo.versionCode > packageInfo.versionCode) { return true; } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } return false; } }
上面的程式碼可知:我們通過獲取當前app的資訊來比較是否需要下載和是否立即安裝。第一次下載把downloadId儲存到本地,使用者下次進來的時候,取出儲存的downloadId,然後通過downloadId來獲取下載的狀態資訊。如果下載失敗,則重新下載並且把downloadId存起來。如果下載成功,則判斷本地的apk的包名是否和當前程式是相同的,並且本地apk的版本號大於當前程式的版本
,如果都滿足則直接啟動安裝程式。
監聽app是否安裝完成
public class ApkInstallReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)){ long downloadApkId =intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); installApk(context, downloadApkId); } } /** * 安裝apk */ private void installApk(Context context,long downloadApkId) { // 獲取儲存ID SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); long downId =sp.getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L); if(downloadApkId == downId){ DownloadManager downManager= (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); Uri downloadFileUri = downManager.getUriForDownloadedFile(downloadApkId); if (downloadFileUri != null) { Intent install= new Intent(Intent.ACTION_VIEW); install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive"); install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install); }else{ Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show(); } } } }
DownloadManager下載完成後會發出一個廣播android.intent.action.DOWNLOAD_COMPLETE
新建一個廣播接收者即可:
清單配置:
先新增網路下載的許可權:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
再新增靜態廣播:
<receiver android:name=".ApkInstallReceiver"> <intent-filter> <action android:name="android.intent.action.DOWNLOAD_COMPLETE" /> </intent-filter> </receiver>