【個人Android進階之旅】 解決bug: Expected file scheme in URI: content://downloads/my_downloads/12

1、錯誤描述

今天測試MM用HTC手機測試某個模塊的時候crash了,抓log後發現是使用DownloadManager下載apk安裝包而後自動安裝的時候,拋了異常:java.lang.IllegalArgumentException: Expected file scheme in URI: content://downloads/my_downloads/12css

具體crash錯誤信息以下所示:html

11-30 09:24:21.933 28279 28279 E AndroidRuntime: FATAL EXCEPTION: main
11-30 09:24:21.933 28279 28279 E AndroidRuntime: Process: com.xtc.watch, PID: 28279
11-30 09:24:21.933 28279 28279 E AndroidRuntime: java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.DOWNLOAD_COMPLETE flg=0x10 pkg=com.xtc.watch (has extras) } in
 com.xtc.watch.service.compatible.VersionCompatibleUpdateService$DownloadCompleteReceiver@33fa7ec
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:934)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at android.os.Handler.handleCallback(Handler.java:739)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:95)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:168)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:5885)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
11-30 09:24:21.933 28279 28279 E AndroidRuntime: Caused by: java.lang.IllegalArgumentException: Expected file scheme in URI: content://downloads/my_downloads/12
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at java.io.File.checkURI(File.java:223)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at java.io.File.<init>(File.java:175)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at com.xtc.watch.service.compatible.VersionCompatibleUpdateService.a(Unknown Source)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at com.xtc.watch.service.compatible.VersionCompatibleUpdateService.a(Unknown Source)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at com.xtc.watch.service.compatible.VersionCompatibleUpdateService$DownloadCompleteReceiver.onReceive(Unknown Source)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:915)
11-30 09:24:21.933 28279 28279 E AndroidRuntime:        ... 7 more
11-30 09:24:21.939  1785  3503 E ActivityManager: App crashed! Process: com.xtc.watch

2、錯誤分析

進入File.java查看代碼,java

/**
     * Constructs a new File using the path of the specified URI. {@code uri}
     * needs to be an absolute and hierarchical Unified Resource Identifier with
     * file scheme and non-empty path component, but with undefined authority,
     * query or fragment components.
     *
     * @param uri
     *            the Unified Resource Identifier that is used to construct this
     *            file.
     * @throws IllegalArgumentException
     *             if {@code uri} does not comply with the conditions above.
     * @see #toURI
     * @see java.net.URI
     */
    public File(URI uri) {
        // check pre-conditions
        checkURI(uri);
        this.path = fixSlashes(uri.getPath());
    }

checkURI(URI uri)的源代碼以下:android

private static void checkURI(URI uri) {
        if (!uri.isAbsolute()) {
            throw new IllegalArgumentException("URI is not absolute: " + uri);
        } else if (!uri.getRawSchemeSpecificPart().startsWith("/")) {
            throw new IllegalArgumentException("URI is not hierarchical: " + uri);
        }
        if (!"file".equals(uri.getScheme())) {
            throw new IllegalArgumentException("Expected file scheme in URI: " + uri);
        }
        String rawPath = uri.getRawPath();
        if (rawPath == null || rawPath.isEmpty()) {
            throw new IllegalArgumentException("Expected non-empty path in URI: " + uri);
        }
        if (uri.getRawAuthority() != null) {
            throw new IllegalArgumentException("Found authority in URI: " + uri);
        }
        if (uri.getRawQuery() != null) {
            throw new IllegalArgumentException("Found query in URI: " + uri);
        }
        if (uri.getRawFragment() != null) {
            throw new IllegalArgumentException("Found fragment in URI: " + uri);
        }
    }

原來就是下面這一段代碼拋出的異常,若是uri不是以file開頭的就拋異常IllegalArgumentException(「Expected file scheme in URI: 」 + uri),數據庫

if (!"file".equals(uri.getScheme())) {
            throw new IllegalArgumentException("Expected file scheme in URI: " + uri);
        }

而咱們的uri 是經過下面代碼獲取而來的markdown

Uri downloadFileUri = manager.getUriForDownloadedFile(downId);
File file = new File(new URI(downloadFileUri.toString()));

獲得的uri信息爲 app

content://downloads/my_downloads/12

所以就拋了該異常出來。oop

getUriForDownloadedFile(long id)源代碼以下:測試

/**
     * Returns the {@link Uri} of the given downloaded file id, if the file is
     * downloaded successfully. Otherwise, null is returned.
     *<p>
     * If the specified downloaded file is in external storage (for example, /sdcard dir),
     * then it is assumed to be safe for anyone to read and the returned {@link Uri} corresponds
     * to the filepath on sdcard.
     *
     * @param id the id of the downloaded file.
     * @return the {@link Uri} of the given downloaded file id, if download was successful. null
     * otherwise.
     */
    public Uri getUriForDownloadedFile(long id) {
        // to check if the file is in cache, get its destination from the database
        Query query = new Query().setFilterById(id);
        Cursor cursor = null;
        try {
            cursor = query(query);
            if (cursor == null) {
                return null;
            }
            if (cursor.moveToFirst()) {
                int status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS));
                if (DownloadManager.STATUS_SUCCESSFUL == status) {
                    int indx = cursor.getColumnIndexOrThrow(
                            Downloads.Impl.COLUMN_DESTINATION);
                    int destination = cursor.getInt(indx);
                    // TODO: if we ever add API to DownloadManager to let the caller specify
                    // non-external storage for a downloaded file, then the following code
                    // should also check for that destination.
                    if (destination == Downloads.Impl.DESTINATION_CACHE_PARTITION ||
                            destination == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION ||
                            destination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING ||
                            destination == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE) {
                        // return private uri
                        return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, id);
                    } else {
                        // return public uri
                        String path = cursor.getString(
                                cursor.getColumnIndexOrThrow(COLUMN_LOCAL_FILENAME));
                        return Uri.fromFile(new File(path));
                    }
                }
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        // downloaded file not found or its status is not 'successfully completed'
        return null;
    }

3、錯誤解決

將以前查找下載好的apk文件的代碼this

Uri downloadFileUri = manager.getUriForDownloadedFile(downId);

File file = new File(new URI(downloadFileUri.toString()));

改成經過downId來查詢DownloadManager下載的數據庫的記錄,而後查找本地文件的路徑,以下所示:

Cursor c = manager.query(new DownloadManager.Query().setFilterById(downId));
if(c != null){
    c.moveToFirst();
    int fileNameIdx = c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
    String fileName = c.getString(fileNameIdx);
    File file  = new File(fileName);
    LogUtil.d(TAG, "安裝文件:" + file.getAbsolutePath());
    c.close();
}

安裝apk的代碼由

//自動安裝apk
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);

改成

//自動安裝apk
Intent install = new Intent(Intent.ACTION_VIEW);
//install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive");
install.setDataAndType(Uri.parse("file://" + fileName.toString()), "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(install);

這樣經過查詢本地真實的下載好apk的真實本地路徑,而後再進行安裝的話就不會報異常了。

參考文章:
http://blog.csdn.net/q445697127/article/details/40537945

http://www.xuebuyuan.com/2163407.html

http://www.2cto.com/kf/201502/376975.html


做者:歐陽鵬 歡迎轉載,與人分享是進步的源泉!
轉載請保留原文地址:http://blog.csdn.net/ouyang_peng/article/details/53404379

這裏寫圖片描述

相關文章
相關標籤/搜索