最近在做App的改版(由系統App改爲普通App)過程中,經原來的靜默安裝方式改爲調用系統普通安裝APK的方式時,報錯了,堆棧信息如下:
fatal error
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.xxx.xxx/cache/com.aaa.bbb.apk
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:738)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:417)
at com.xxx.xxx.service.download.ApkDownloadHelper.installApp(ApkDownloadHelper.java:299)
at com.xxx.xxx.service.download.ApkDownloadHelper.checkForInstall(ApkDownloadHelper.java:233)
at com.xxx.xxx.service.download.ApkDownloadHelper.onSuccess(ApkDownloadHelper.java:220)
at com.xxx.xxx.download.FileDownloader.notifySuccess(FileDownloader.java:246)
at com.xxx.xxx.download.FileDownloader.onResponse(FileDownloader.java:197)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:153)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Failed to find configured root that……
這裏的意思是說:沒有找到根目錄的配置。
接下來,我們查閱FileProvider.java源文件,發現有一個常量定義
private static final String TAG_ROOT_PATH = "root-path";
緊接着,我們尋找使用這個變量的地方,在文件第620行找到,如下:
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);
final XmlResourceParser in = info.loadXmlMetaData(
context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);
if (in == null) {
throw new IllegalArgumentException(
"Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data");
}
int type;
while ((type = in.next()) != END_DOCUMENT) {
if (type == START_TAG) {
final String tag = in.getName();
final String name = in.getAttributeValue(null, ATTR_NAME);
String path = in.getAttributeValue(null, ATTR_PATH);
File target = null;
if (TAG_ROOT_PATH.equals(tag)) {
target = DEVICE_ROOT;
} else if (TAG_FILES_PATH.equals(tag)) {
target = context.getFilesDir();
} else if (TAG_CACHE_PATH.equals(tag)) {
target = context.getCacheDir();
} else if (TAG_EXTERNAL.equals(tag)) {
這個方法主要是“分析路徑策略”,主要是解析manifest中配置的provider的meta-data屬性中的resource中file_paths文件。
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
Android N開始,Google逐步提高了系統的安全性,其中包括文件的訪問。所以從Android N開始,提高了文件訪問的門檻,需要再file_paths中增加如下配置:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--Context.getFilesDir()位於/data/data/安裝目錄-->
<files-path
name="internalPath"
path="file" />
<!--Context.getCacheDir()-->
<cache-path
name="cachePath"
path="file" />
<!--Environment.getExternalStorageDirectory()-->
<external-path
name="externalPath"
path="file" />
<!--Context.getExternalFilesDir(null)-->
<external-files-path
name="externalPath"
path="file" />
<external-cache-path
name="externalCachePath"
path="file" />
<!--增加根目錄配置,特別注意:此處的path要爲空字符串-->
<root-path
name="rootPath"
path="" />
</paths>
其中,要特別注意:root-path中的path屬性一定要爲""。
這樣,我們就可以解決一開始拋出的文件。
歡迎大家在評論區留言,一起交流,謝謝。