FileProvider crash – npe尝试调用空string上的XmlResourceParser
这是我清单的一部分:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.asd" android:versionCode="118" android:versionName="118" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" /> <application android:name="com.example.asd.AsdApplication" android:allowBackup="true" android:allowTaskReparenting="true" android:theme="@style/AsdTheme" > ... <provider android:name="com.example.asd.database.hq.ContentProviderDB" android:authorities="ourContentProviderAuthorities" > </provider> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.asd.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider> ... </application> </manifest>
这是raw / xml / filepaths.xml中的文件path文件
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="media"/> </paths>
我从互联网下载一个video,并将其保存到内部存储:
public static boolean saveInputStreamToInternalStorageFile(Context context, String filename, byte[] dataToWrite, Context ctx) { FileOutputStream fos; try { fos = new FileOutputStream(context.getFilesDir() + File.separator + filename); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(dataToWrite); oos.close(); return true; } catch (FileNotFoundException e) { e.printStackTrace(); return false; } catch (IOException e) { e.printStackTrace(); return false; } }
我尝试像这样使用它:
private void playVideoFromDeviceWithWorkaround(String fileName) { File newFile = new File(getFilesDir(), fileName); Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.example.asd", newFile); try { vvVideoFullscreen.setVideoURI(contentUri); showMediaControls = true; playVideo(); } catch (Exception e) { playVideoFromNetwork(); } }
在这条线上:
Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.example.asd", newFile);
我得到以下错误:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:560) at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:534) at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:376)
问题是在清单中我有这样一行:
android:authorities="com.example.asd.fileprovider"
当调用getUriForFile时,我传递:
Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.example.asd", newFile);
因此,从"com.example.asd"
更改为"com.example.asd.fileprovider"
,它的工作
您可以在不对包名进行硬编码的情况下执行此操作,还可以在同一设备上运行多个变体(请参阅applicationIdSuffix
release
和debug
,请参阅以下问题 ):
基于FileProvider.java:560
final ProviderInfo info = context.getPackageManager() .resolveContentProvider(authority, PackageManager.GET_META_DATA); final XmlResourceParser in = info.loadXmlMetaData( //560 context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);
你使用了错误的authority
,它没有findContentProvider
( info == null
)。
更改您的清单到( ${applicationId}
将被清单合并取代)
android:authorities="${applicationId}.share"
和
Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".share", result);
.share
后缀是可选的,以防你拥有一个真正的ContentProvider
,它最好拥有包名作为权限。
首先,请确保您的提供者android:authorities
不与您的其他提供者冲突。 除此之外,你可以select任何名字的最后部分名称:“供应商”,“文件提供者”等,但应用程序崩溃时,有多个android:authorities
列出,而文档指出,它允许列出多个值。
file://
scheme目前不允许在targetSdkVersion> = 24(Android N 7.0)上的Intent上附加,所有设备(Android 5,6和7)都只传递content://
。 但是我们遇到了小米打破这个Google约定并发送file://
,因此data.getData().getAuthority()
给出了空string。
final String uriScheme = currentUri.getScheme(); if ("content".equals(uriScheme)) { // getting full file path (works with some providers, ie Gallery) path = FileUtils.getPath(getContext(), currentUri); if (path != null) { currentFile = new File(path); } } else if ("file".equals(uriScheme)) { // in a rare case we received file:// in currentUri, we need to: // 1. create new File variable from currentUri that looks like "file:///storage/emulated/0/download/50044382b.jpg" // 2. generate a proper content:// Uri for it currentFile = new File(currentUri.getPath()); String authority = data.getData().getAuthority(); if (authority != null && !authority.isEmpty()) { currentUri = FileProvider.getUriForFile(getActivity(), authority, currentFile); } } else { // throw exception }
另外, FileProvider.getUriForFile()
导致崩溃的bug java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example/files/attachments/image.jpg
在Android支持库v24.2.0。 问题是FileProvider.java没有看到外部path文件夹。
如果您使用BuildConfig
在运行时构buildAUTHORITY,请确保使用完整的类名称(包括您的包名称)。
坏:
final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";
好:
final String AUTHORITY = com.mycompany.myapp.BuildConfig.APPLICATION_ID + ".provider";