android 7.0 由於file://引發的FileUriExposedException異常

在7.0之前的版本:

//建立臨時圖片
File photoOutputFile = SDPath.getFile("temp.jpg", SDPath.PHOTO_FILE_STR);
Uri photoOutputUri = Uri.fromFile(photoOutputFile);

這個file文件直接很是簡單的轉換成"file://XXX/XXX/XXX"的uri格式java

7.0後的版本:

當把targetSdkVersion指定成24及之上而且在API>=24的設備上運行時。這種方式則會出現FileUriExposedException異常android

android.os.FileUriExposedException:         file:///XXX exposed beyond app through ClipData.Item.getUri()
    at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
    at android.net.Uri.checkFileUriExposed(Uri.java:2346)
    at android.content.ClipData.prepareToLeaveProcess(ClipData.java:832)
    at android.content.Intent.prepareToLeaveProcess(Intent.java:8909)
    ...
緣由

Android再也不容許在app中把file://Uri暴露給其餘app,包括但不侷限於經過Intent或ClipData 等方法。app

緣由在於使用file://Uri會有一些風險,好比:ide

  • 文件是私有的,接收file://Uri的app沒法訪問該文件。
  • 在Android6.0以後引入運行時權限,若是接收file://Uri的app沒有申請READ_EXTERNAL_STORAGE權限,在讀取文件時會引起崩潰。

所以,google提供了FileProvider,使用它能夠生成content://Uri來替代file://Uriui

解決方案

首先在AndroidManifest.xml中添加providergoogle

  • android:authorities
    是用來標識provider的惟一標識,在同一部手機上一個"authority"串只能被一個app使用,衝突的話會致使app沒法安裝。
  • android:exported必須設置成false,後面異常會講爲何
  • android:grantUriPermissions用來控制共享文件的訪問權限,也能夠在java代碼中設置。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.zhongjh.phone.ui"
···

    <provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.zhongjh.phone.ui.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths" />
   </provider>

</manifest >

res/xml/provider_paths.xml
這是指定路徑和轉換規則
<paths>中能夠定義如下子節點.net

子節點 對應路徑 例子
files-path Context.getFilesDir()
cache-path Context.getCacheDir()
external-path Environment.getExternalStorageDirectory() /storage/emulated/0/
external-files-path Context.getExternalFilesDir(null)
external-cache-path Context.getExternalCacheDir()

加入我要替換的目錄是
/storage/emulated/0/diary sdcard/photo/
那麼配置應該寫成code

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="diary sdcard/photo"/>
</paths>

而後修改代碼xml

//建立臨時圖片
File photoOutputFile = SDPath.getFile("temp.jpg", SDPath.PHOTO_FILE_STR);
//Uri photoOutputUri = Uri.fromFile(photoOutputFile);
Uri photoOutputUri = FileProvider.getUriForFile(
                    mContext,
                    mActivity.getPackageName() + ".fileprovider",
                    photoOutputFile);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, photoOutputUri);
我所碰到的異常處理
  • java.lang.SecurityException: Provider must not be exported
    解決方案:android:exported必須設置成false
  • Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
    解決方案:AndroidManifest.xml處的android:authorities必須跟mActivity.getPackageName() + ".fileprovider"同樣
相關文章
相關標籤/搜索