日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Android 下 FileProvider 的 authorities 重名會(huì)怎么樣?

先說結(jié)論:如果有兩個(gè)或多個(gè) FileProvider 的 authorities 重名,那么只有合并后的 AndroidManifest.xml 文件里,排在最前面的那個(gè)配置會(huì)生效。

創(chuàng)新互聯(lián)主要從事做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)鐵鋒,10多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220

一、場景

應(yīng)用里有個(gè)自升級的功能,下載完 apk 后,通過 FileProvider 提供 Uri 進(jìn)行安裝。我修改了文件下載路徑后,功能失效了,報(bào)錯(cuò)如下:

java.lang.IllegalArgumentException: Failed to find configured root that contains /data/user/0/org.mazhuang.test/cache/download/xxx.apk
    at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:738)
    at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:417)

對應(yīng)的 provider 的聲明是:


    

provider_paths 內(nèi)容:



    

二、分析

對照 FileProvider 官方文檔:https://developer.android.com/reference/android/support/v4/content/FileProvider.html ,我再三確認(rèn)了配置本身沒有問題。

然后在報(bào)錯(cuò)堆棧的 android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile 方法處下斷點(diǎn)調(diào)試:

@Override
public Uri getUriForFile(File file) {
    // some code here
    // Find the most-specific root path
    Map.Entry mostSpecific = null;
    for (Map.Entry root : mRoots.entrySet()) {
        final String rootPath = root.getValue().getPath();
        if (path.startsWith(rootPath) && (mostSpecific == null
                || rootPath.length() > mostSpecific.getValue().getPath().length())) {
            mostSpecific = root;
        }
    }

    if (mostSpecific == null) {
        throw new IllegalArgumentException(
                "Failed to find configured root that contains " + path);
    }
    // some code here
}

發(fā)現(xiàn) SimplePathStrategy 的 mRoots 里確實(shí)沒有我配置的路徑。而 SimplePathStrategy 唯一的構(gòu)造方法的參數(shù)是 authority,該實(shí)例的 authority 確實(shí)是 ${applicationId}.provider 無誤……那么,合理猜測,是有同名的 FileProvider,這里用到的是另一個(gè) FileProvider 的 mRoots。

為了驗(yàn)證該猜測,我從兩方面做確認(rèn):

  • 查看合并后的 AndroidManifest.xml 文件,是否有其它 FileProvider 的 authorities 也是 ${applicationId}.provider?
  • 閱讀 Android Frameworks 里的相關(guān)源碼,確認(rèn)解析 provider 配置、取 FileProvider 實(shí)例的邏輯。

1.查看合并后的 AndroidManifest.xml

現(xiàn)在 Android Studio 已經(jīng)提供了非常方便的查看合并后的 AndroidManifest.xml 的功能,打開 app 項(xiàng)目的 AndroidMenifest.xml 文件,在編輯器底部有個(gè) Merged Manifest 選項(xiàng)卡,點(diǎn)擊即可查看。

可以看到,確實(shí)有兩個(gè) FileProvider 的 authorities 都是 ${applicationId}.provider,另一個(gè)是從一個(gè)第三方庫里來的,并且,它排在前面。

2.源碼確認(rèn)

首先是在 Android Studio 里進(jìn)行,找到調(diào)用 SimplePathStrategy 構(gòu)造方法的地方,是在 android.support.v4.content.FileProvider#parsePathStrategy:

/**
 * Parse and return {@link PathStrategy} for given authority as defined in
 * {@link #META_DATA_FILE_PROVIDER_PATHS} {@code }.
 *
 * @see #getPathStrategy(Context, String)
 */
private static PathStrategy parsePathStrategy(Context context, String authority)
        throws IOException, XmlPullParserException {
    final SimplePathStrategy strat = new SimplePathStrategy(authority);

    final ProviderInfo info = context.getPackageManager()
            .resolveContentProvider(authority, PackageManager.GET_META_DATA);
    // some code here
}

這里的 context.getPackageManager().resolveContentProvider 的實(shí)現(xiàn),一路通過以下路徑找到:

// android.app.ContextImpl#getPackageManager
// -->
// android.app.ActivityThread#getPackageManager
public static IPackageManager getPackageManager() {
    if (sPackageManager != null) {
        return sPackageManager;
    }
    IBinder b = ServiceManager.getService("package");
    sPackageManager = IPackageManager.Stub.asInterface(b);
    return sPackageManager;
}

到這里動(dòng)用一點(diǎn)歷史經(jīng)驗(yàn),可知實(shí)際實(shí)現(xiàn)類是 PackageManagerService,來看看 PackageManagerService#resolveContentProvider 的實(shí)現(xiàn):

@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
    if (!sUserManager.exists(userId)) return null;
    flags = updateFlagsForComponent(flags, userId, name);
    final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid());
    // reader
    synchronized (mPackages) {
        final PackageParser.Provider provider = mProvidersByAuthority.get(name);
        // some code here
    }
    // some code here
}

在 PackageManagerService 里繼續(xù)查找寫入 mProvidersByAuthority 的地方,在 PackageManagerService#commitPackageSettings:

/**
 * Adds a scanned package to the system. When this method is finished, the package will
 * be available for query, resolution, etc...
 */
private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting,
        UserHandle user, int scanFlags, boolean chatty) throws PackageManagerException {
    // some code here
    synchronized (mPackages) {
        // some code here
        for (i=0; i

從上面這段中我們可以得到兩個(gè)知識(shí)點(diǎn):

  • 如果已經(jīng)有同名的 authority,那么后面的 Provider 配置會(huì)被忽略掉;
  • authority 可以配置多個(gè),用分號分隔。(這一點(diǎn)在官方文檔之類的都沒有找到說明,也許官方覺得配置項(xiàng)的名稱 autorities 就說明了一切?實(shí)測可正常使用。)

接下來還有一點(diǎn)需要確認(rèn)的,就是 pkg.providers 是否是按 AndroidManifexs.xml 里的順序排列的。

根據(jù)上面代碼里的線索,可以留意到 PackageParser 類,按如下順序遞進(jìn):

// android.content.pm.PackageParser#parseBaseApk(java.io.File, android.content.res.AssetManager, int)
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
        throws PackageParserException {
        // some code here
        // 下面這行里的 ANDROID_MANIFEST_FILENAME = AndroidManifest.xml
        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

        final String[] outError = new String[1];
        final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
        // some code here
}

// --> 
// android.content.pm.PackageParser#parseBaseApk(java.lang.String, android.content.res.Resources, android.content.res.XmlResourceParser, int, java.lang.String[])
// -->
// android.content.pm.PackageParser#parseBaseApkCommon
// -->
// android.content.pm.PackageParser#parseBaseApplication
// -->
private boolean parseBaseApplication(Package owner, Resources res,
        XmlResourceParser parser, int flags, String[] outError)
    // some code here
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
            continue;
        }

        String tagName = parser.getName();
        if (tagName.equals("activity")) {
            // some code here
        } else if (tagName.equals("provider")) {
            Provider p = parseProvider(owner, res, parser, flags, outError);
            if (p == null) {
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }

            owner.providers.add(p);
        // some code here

至此,我們已經(jīng)可以確定 pkg.providers 是按 AndroidManifest.xml 里的順序解析出來的了。

三、解決方案

既然已經(jīng)知道了問題的原因,那么解決方案也就呼之欲出了:

修改自己的 FileProvider 的 authorities,不會(huì)和其它庫的 authorities 重名即可。

四、小結(jié)

源碼面前,了無秘密?!罱?/p>

如果遇到疑難問題,而恰好又有源碼可查,那么就不要猶豫,直接去看源碼吧!花一些時(shí)間和耐心,最終會(huì)找到你想要的。


新聞名稱:Android 下 FileProvider 的 authorities 重名會(huì)怎么樣?
本文地址:http://www.5511xx.com/article/cdgsoij.html