DownloaderManager
作爲一個很好用的系統級別API用作下載文件很方便,我們這篇文章不介紹如何使用它,我們來說說在使用過程中出現的幾個crash,如果出現了這幾個crash,尤其是第三個,那麼恭喜你,可以去買彩票了,嘿嘿
- 首先來說第一個問題
Fatal Exception: java.lang.UnsupportedOperationException Cannot update URI: content://downloads/my_downloads/-1
, 這個問題是因爲我們在使用DownloaderManager
進行remove
操作的時候,參數id爲-1
導致的 - 解決辦法:
remove之前記得判斷一個
id是否大於0
即可
- 第二個crash,
Fatal Exception: java.lang.IllegalArgumentException Unknown URL content://downloads/my_downloads, android.content.ContentResolver.insert (ContentResolver.java:1541), android.app.DownloadManager.enqueue (DownloadManager.java:1504)
,如果出現這個crash,說明用戶手機中DownloaderManager被disable了。 - 解決辦法:在測試過程中我們可以通過
adb shell pm enable com.android.providers.downloads
來enable
;或者使用adb shell pm disable com.android.providers.downloads
來disable
;google play的做法是在進入應用的時候檢查用戶是否disable了DownloaderManager,如果disable了,會彈出一個dialog提示用戶(如下圖所示):
- 如果用戶點擊同意就enable ·DownloaderManager·進入應用,點擊取消就退出應用,不讓用戶使用;我們可以參考這個做法,如果應用是系統級應用,可以申請權限
CHANGE_COMPONENT_ENABLED_STATE
,調用PackageManager.setApplicationEnabledSetting
來enable, 或者引導用戶開啓。
public static void startActivityToOpenDownloadManager(Context context) { Intent downloadIntent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); downloadIntent.setData(Uri.parse("package:com.android.providers.downloads")); downloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(downloadIntent); }
- 如果用戶點擊同意就enable ·DownloaderManager·進入應用,點擊取消就退出應用,不讓用戶使用;我們可以參考這個做法,如果應用是系統級應用,可以申請權限
第三個crash是Fatal Exception: java.lang.NullPointerException Attempt to invoke virtual method java.lang.String android.net.Uri.getLastPathSegment()?on a null object reference android.app.DownloadManager.enqueue (DownloadManager.java:1026)
,如果出現這個crash,你真的可以去買彩票了。
- 分析:通過查看源碼得知是在
DownloaderManager.enqueue
出現的crash,我們查看源碼:public long enqueue(Request request) { ContentValues values = request.toContentValues(mPackageName); Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values); long id = Long.parseLong(downloadUri.getLastPathSegment()); return id; } public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url, @Nullable ContentValues values) { Preconditions.checkNotNull(url, "url"); try { if (mWrapped != null) return mWrapped.insert(url, values); } catch (RemoteException e) { return null; } IContentProvider provider = acquireProvider(url); if (provider == null) { throw new IllegalArgumentException("Unknown URL " + url); } try { long startTime = SystemClock.uptimeMillis(); Uri createdRow = provider.insert(mPackageName, url, values); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */); return createdRow; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; } finally { releaseProvider(provider); } }
- 從上面的代碼中可以看出是因爲
insert
返回了null,所以導致後面(downloadUri.getLastPathSegment()
出現了空指針異常,而能出現null的情況都是拋出了RemoteException
,官網對這個異常的描述是Parent exception for all Binder remote-invocation errors
,說明這個異常是和binder機制相關的,那麼基本可以確定是DownloadManager被disable掉了,那麼解決辦法自然和第二個crash一樣。