1. 程式人生 > >快速使用FileProvider解決Android7.0檔案許可權問題

快速使用FileProvider解決Android7.0檔案許可權問題

升級到Android7.0之後,啟動系統相機或者截圖,傳入URI的時候可能會導致程式閃退崩潰。這是因為7.0的新的檔案許可權導致的。下面是解決這個問題的快速解決方案。

問題程式碼

在7.0可能會出問題的程式碼:

final String CACHE_IMG = Environment.getExternalStorageDirectory()+"/demo/"
final int TAG_PHOTO_CAMERA=200;


Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

String fileName = "defaultImage.jpg"
; File file = new File(CACHE_IMG, fileName); Uri uri = Uri.fromFile(file); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, TAG_PHOTO_CAMERA);

其中Uri uri = Uri.fromFile(file);這裡會導致閃退。

解決方法

step1. 將Uri的生成方式改為由FileProvider提供的臨時授權路徑,並且在intent中新增flag
修改後程式碼如下

final
String CACHE_IMG = Environment.getExternalStorageDirectory()+"/demo/" final int TAG_PHOTO_CAMERA=200; Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); String fileName = "defaultImage.jpg"; File file = new File(CACHE_IMG, fileName); Uri imageUri=FileProvider.getUriForFile(activity,"me.xifengwanzhao.fileprovider"
, file);//這裡進行替換uri的獲得方式 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//這裡加入flag startActivityForResult(intent, TAG_PHOTO_CAMERA);

step2.在AndroidManifest.xml中的application標籤中新增provider的配置

   <application
       ...>
         <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="me.xifengwanzhao.fileprovider"//這裡需要和上面部分字串相同
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
    </application>

step3.在res/xml中新建一個檔案file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<resource xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="images"
        path="demo/" />
</resource>

OK,大功告成,這樣就不會崩潰了

程式碼解釋

對於面向 Android 7.0 的應用,Android 框架執行的 StrictMode API 政策禁止在您的應用外部公開 file:// URI。如果一項包含檔案 URI 的 intent 離開您的應用,則應用出現故障,並出現 FileUriExposedException 異常。

要在應用間共享檔案,您應傳送一項 content:// URI,並授予 URI 臨時訪問許可權。進行此授權的最簡單方式是使用 FileProvider 類。如需瞭解有關許可權和共享檔案的詳細資訊,請參閱共享檔案。

根據文件提示我們使用FileProvider進行處理,同時利用xml對FileProvider進行配置
參考如下

java根路徑產生方式 對應xml根節點名稱
Context.getFilesDir() files-path
getCacheDir() cache-path
Environment.getExternalStorageDirectory() external-path
Context#getExternalFilesDir(String) Context.getExternalFilesDir(null) external-files-path
Context.getExternalCacheDir() external-cache-path

節點中的name 不可重名,path為自定義

關於相簿選圖和相機裁剪

有同學反映相簿選圖和相機裁剪時候的報錯問題,這裡也說一下
系統相簿選圖返回的Uri是可以直接使用的,不需要也不能使用FileProvider進行轉換
如果需要根據uri獲得轉換後的uri 可以參考如下方式

Uri fromUri;
if (uri.getScheme() != null && uri.getScheme().startsWith("file")) {
    fromUri =
         FileProvider.getUriForFile(mContext,"me.xifengwanzhao.fileprovider", new File(FileUtils.getPath(mContext, uri)));//這裡進行替換uri的獲得方式
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//這裡加入flag
} else {
     //相簿選圖適配
     fromUri = uri;
}

關於相機裁剪
相機裁剪 intent.setDataAndType(fromUri, “image/*”);這裡是需要對uri進行轉換的,
而 intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));這裡使用原來的方式獲取uri就可以了
那麼啟動系統裁剪的方法可以寫成這樣

    /**
     * 開啟截圖,啟動系統的截圖方法 返回requestCode為 {Constant.IMG_ZOOM}
     *
     * @param mContext 必須為activity
     * @param uri      需要進行裁剪的圖片的uri
     * @param size     截圖的大小寬和高的數值,這裡僅限截圖為1:1的正方形
     * @return path 截圖返回的路徑
     * @see Constant#IMG_ZOOM
     */
    public static String startPhotoZoom(Activity mContext, Uri uri, int size) {
        //這裡生成一個儲存截圖用的臨時路徑並且返回出去
        String imgPath;
        File file = new File(Constant.ZOOM_IMAGE, Constant.getNewestImageName(mContext));
        imgPath = file.getPath();

        Intent intent = new Intent("com.android.camera.action.CROP");
        Uri fromUri;
        if (uri.getScheme() != null && uri.getScheme().startsWith("file")) {
            fromUri = FileProvider.getUriForFile(mContext, "me.xifengwanzhao.fileprovider", new File(FileUtils.getPath(mContext, uri)));//這裡進行替換uri的獲得方式
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//這裡加入flag
        } else {
            //相簿選圖適配
            fromUri = uri;
        }
        intent.setDataAndType(fromUri, "image/*");
        intent.putExtra("crop", "true");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
        if (android.os.Build.MANUFACTURER.contains("HUAWEI")) {// 華為特殊處理 不然會顯示圓
            intent.putExtra("aspectX", 9998);
            intent.putExtra("aspectY", 9999);
        } else {
            intent.putExtra("aspectX", 1);
            intent.putExtra("aspectY", 1);
        }
        intent.putExtra("outputX", size);
        intent.putExtra("outputY", size);
        mContext.startActivityForResult(intent, Constant.IMG_ZOOM);
        return imgPath;
    }

參考文件