Android webview打开相机闪退报android.os.FileUriExposedException 问题解决记录
1.背景
最近优化公司的一款老app。app采用vue+webview+android。为实现产品奇奇怪怪的需求把targetSdkVersion从23升级到了26.然后就出现了UI框架打开相机时闪退的问题。通过错误日志定位到问题android.os.FileUriExposedException.
2.问题产生原因
android7.0后加了私有目录被限制访问所以,Uri.fromFile(file)方法访问file://url会出现android.os.FileUriExposedException异常。
3.解决方案
3.1;在AndroidManifest.xml 清单文件中加入provider。
<application ... <activity .. </activity> <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> <application
3.2.在res->xml目录下新建file_paths.xml
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths>
3.3
在具体保存代码修改
//imageUri = Uri.fromFile(file); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M){ imageUri = Uri.fromFile(file); } else { imageUri= FileProvider.getUriForFile(this, this.getPackageName() + ".provider", file); }
注释掉的是原来代码。在android7.0以上用FileProvider.getUriForFile(context,context.getPackageName() + ".provider", file);替换Uri.fromFile(file);
4.可能有的小伙伴和我一样是webview+H5开发的,然后使用html打开的相机。那么在哪修改imageUri 呢。我是这样做的
4.1打开相机html代码
<van-uploader v-model="fileList" max-count="1" preview-size="150px" upload-text="点击打开相机,拍照" :after-read="afterRead" multiple accept="image/*" />
4.2webview里重写WebChromeClient
mWebView.setWebChromeClient(mWebChromeClient);
private WebChromeClient mWebChromeClient = new WebChromeClient() { @Override public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) { callback.invoke(origin, true, true); } //>5.0.1 @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { mUploadCallbackAboveL = filePathCallback; take(); return true; } //<3.0 public void openFileChooser(ValueCallback<Uri> uploadMsg) { mUploadMessage = uploadMsg; take(); } //>3.0+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { mUploadMessage = uploadMsg; take(); } //>4.1.1 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { mUploadMessage = uploadMsg; take(); } };
take()方法里修改
private void take() { File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp"); if (!imageStorageDir.exists()) { imageStorageDir.mkdirs(); } File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg"); //imageUri = Uri.fromFile(file); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M){ imageUri = Uri.fromFile(file); } else { imageUri= FileProvider.getUriForFile(this, this.getPackageName() + ".provider", file); } final List<Intent> cameraIntents = new ArrayList<Intent>(); final Intent captureIntent = new Intent("android.media.action.IMAGE_CAPTURE"); captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(captureIntent, FILECHOOSER_RESULTCODE);
}