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);
}