DownloadManager使用(適配7.0,8.0)
簡介
感覺距離上次寫文章已經過去很久很久啦,主要是一直沒找到什麼合適寫的內容,而且抽空研究了一下Android以外的技術,寫文章的事情就落下了。
最近要做一個app線上更新的功能,本想著自己寫會不會有點小麻煩,因為涉及到一些7.0的apk檔案下載後的獲取,8.0的安裝未知應用的許可權,通知欄的設定什麼的。然後突然發現有自帶的DownloadManager可以用,現在想說真香~。
一
8.0安裝未知應用許可權申請

8.0安裝未知應用許可權申請
if (Build.VERSION.SDK_INT >= 26) { boolean b = getPackageManager().canRequestPackageInstalls(); if (b) { //下載的具體方法 downloadAPK(); } else { //申請許可權 Uri packageURI = Uri.parse("package:" + getPackageName()); Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI); startActivityForResult(intent, REQUEST_CODE_UNKNOWN_APP); } } else { downloadAPK(); }
申請許可權之後回撥
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); //安裝位未知應用的許可權申請成功 if (requestCode == REQUEST_CODE_UNKNOWN_APP) { LogUtil.d(resultCode + ""); downloadAPK(); } }
就是一個很常規的許可權申請,downloadAPK是實際下載的方法,待會介紹。但是寫到後面發現DownloadManager已經幫我把這部分的許可權做好了,在下載完apk之後會去申請許可權。所以第一步相當於沒用。。。
二
downloadAPK的實現
private void downloadAPK() { if (downloadId != 0) { //防止重複下載 downloadManagerUtil.clearCurrentTask(downloadId); } downloadId = downloadManagerUtil.download(mDownloadUrl, "test.apk", "下載完成後,點選安裝"); }
主要呼叫DownloadManager的工具類DownloadManagerUtil
public class DownloadManagerUtil { private Context mContext; public DownloadManagerUtil(Context context) { mContext = context; } public long download(String url, String title, String desc) { ToastUtil.toastShort("開始下載"); Uri uri = Uri.parse(url); DownloadManager.Request req = new DownloadManager.Request(uri); //設定WIFI下進行更新,預設是所有網路都支援 //req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); //下載中和下載完後都顯示通知欄 req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //使用系統預設的下載路徑 此處為應用內 /android/data/packages ,所以相容7.0 req.setDestinationInExternalPublicDir("test", "test.apk"); //通知欄標題 req.setTitle(title); //通知欄描述資訊 req.setDescription(desc); //設定型別為.apk req.setMimeType("application/vnd.android.package-archive"); //獲取下載任務ID DownloadManager dm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); long requestId = dm.enqueue(req); //查詢下載資訊 DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(requestId); return requestId; } /** * 下載前先移除前一個任務,防止重複下載 * * @param downloadId */ public void clearCurrentTask(long downloadId) { DownloadManager dm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); try { dm.remove(downloadId); } catch (IllegalArgumentException ex) { ex.printStackTrace(); } } }
主要使用的DownloadManager.Request去設定一些下載的屬性,主要的註釋都標識出來了,比較要注意的問題是這個路徑req.setDestinationInExternalPublicDir("test", "test.apk");待會用FileProvider去獲取路徑的時候,要對應上這個路徑,切記切記!要不然會提示軟體安裝包解析錯誤,因為路徑沒對上,FileProvider沒找到對應的路徑下的apk。所以就會提示解析錯誤,我在這個問題上弄了很久。
三
在使用DownloadManager下載完成之後,會發送一個廣播,所以需要註冊一個廣播接收。
public class DownloadApkReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { //下載完成 long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L); // 根據獲取到的ID,使用上面第3步的方法查詢是否下載成功 DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(downloadId); Cursor cursor = manager.query(query); if (!cursor.moveToFirst()) { cursor.close(); return; } int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); String localFilename; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { localFilename = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); } else { localFilename = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME)); } if (status == DownloadManager.STATUS_SUCCESSFUL) { //setPermission(localFilename); installApk(context, localFilename); } } else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) { //在下載過程中點選 Intent viewDownloadIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); viewDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(viewDownloadIntent); } } private void installApk(Context context, String fileName) { File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "test/test.apk"); Intent install = new Intent(Intent.ACTION_VIEW); install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= 24) { //判讀版本是否在7.0以上,7.0以上獲取下載的apk檔案要用FileProvider Uri apkUri = FileProvider.getUriForFile(context.getApplicationContext(), "你的FileProvider的authority", file); install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //新增這一句表示對目標應用臨時授權該Uri所代表的檔案 install.setDataAndType(apkUri, "application/vnd.android.package-archive"); } else { install.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); } context.startActivity(install); } }
廣播的建立就不介紹了。不過要注意的是清單檔案裡面的intent-filter。要寫成下面這樣子才可以
<receiver android:name=".mine.updateapp.DownloadApkReceiver" android:enabled="false" android:exported="false"> <intent-filter> <action android:name="android.intent.action.DOWNLOAD_COMPLETE" /> <action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED" /> </intent-filter> </receiver>
DownloadManager.ACTION_DOWNLOAD_COMPLETE和DownloadManager.ACTION_NOTIFICATION_CLICKED這兩個action分別對應不同的事件。
在installApk這個方法裡面,涉及到7.0的FileProvider許可權適配,FileProvider詳細用法就不介紹了,百度一下一大堆,值得一提的是File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "test/test.apk");在第二個引數裡面寫的"test/test.apk"對應req.setDestinationInExternalPublicDir("test", "test.apk")這裡的兩個引數,一定要對應上,要不然找不到apk會報解析軟體安裝包錯誤安裝失敗的情況。
四
最後既然有廣播,肯定要有註冊的地方,
private void regist() { receiver = new DownloadApkReceiver(); //register download success broadcast registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); }
最終效果圖

效果圖
效果還是挺好的,比自己寫notification,service這些要簡潔不少。