1. 程式人生 > >解決Android7.0系統 呼叫系統相機、系統播放器播放視訊、切圖相容問題,報異常android.os.FileUriExposedException

解決Android7.0系統 呼叫系統相機、系統播放器播放視訊、切圖相容問題,報異常android.os.FileUriExposedException

Android7.0以前獲取本地檔案uri用的Uri.fromFile(new File(filePath)); 後會得到一個file://,這種方式呢7.0及以後的系統版本就用不了,且會報一個異常:

android.os.FileUriExposedException
file:///storage/emulated/0/Android/data/com.alex.demo/cache/.tmp/show.mp4 exposed beyond app through Intent.getData()

舉個例子:

String filePath = "/storage/emulated/0/Android/data/com.alex.demo/cache/.temp.jpg";

Uri uri = Uri.fromFile(new File(filePath));

這個uri打印出來就是"file:///storage/emulated/0/Android/data/com.alex.demo/cache/.temp.jpg" 

注意:檔案路徑和uri路徑是不一樣的,注意區分,檔案路徑時沒有"file://"字首的哦

Android7.0及以上系統版本由於共享檔案許可權的限制,方法不一樣了,需要用FileProvider.getUriForFile()

舉個例子:

String filePath = "/storage/emulated/0/Android/data/com.alex.demo/cache/.temp.jpg";

Uri uri = FileProvider.getUriForFile(BaseApplication.getApplication(), BaseApplication.getApplication().getPackageName() + ".FileProvider", new File(filePath));

這個uri打印出來是"content://com.alex.demo.FileProvider/external_storage_root/cache/.temp.jpg";

下面介紹下我們常用的幾個功能的具體程式碼,保證目前的主流版本都可以相容使用。

首先宣告:com.alex.demo為專案的包名,以下需要包名的地方替換即可

第一步、

 在AndroidManifest.xml中加上攝像頭、讀寫磁碟的許可權,如下

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
第二步、

 在AndroidManifest.xml中加上自定義許可權的ContentProvider,如下

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.alex.demo.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

android:authorities="com.alex.demo.FileProvider" 自定義的許可權
android:exported="false" 是否設定為獨立程序
android:grantUriPermissions="true" 是否擁有共享檔案的臨時許可權
android:resource="@xml/external_storage_root" 共享檔案的檔案根目錄,名字可以自定義

第三步、

在專案res目錄下建立一個xml資料夾,裡面建立一個file_paths.xml檔案,上一步定義的什麼名稱,這裡就什麼名稱,如圖:


<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="external_storage_root"
        path="." />
</paths>

name="external_storage_root" 這個是根目錄名稱,可以自定義

好了,基本工作準備好,下面開始具體的使用吧

1、呼叫系統相機

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            String filePath = ShowConfig.getCacheFolderPath() + File.separator + ".temp.jpg";
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                mOriginUri = FileProvider.getUriForFile(BaseApplication.getApplication(), BaseApplication.getApplication().getPackageName() + ".FileProvider",
                        new File(filePath));
            } else {
                mOriginUri = Uri.fromFile(new File(filePath));
            }
            intent.putExtra(MediaStore.EXTRA_OUTPUT, mOriginUri);

            if(mAttachedFragment == null)
                mAttachedActivity.startActivityForResult(intent, REQUEST_CROP_CAM_IMG_CODE);
            else
                mAttachedFragment.startActivityForResult(intent, REQUEST_CROP_CAM_IMG_CODE);

2、呼叫系統播放器播放視訊
Intent intent = new Intent();
            intent.setAction(android.content.Intent.ACTION_VIEW);
            File file = FileUtils.createFile(mSelectedVideoPath);
            Uri uri;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri contentUri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".FileProvider", file);
                intent.setDataAndType(contentUri, "video/*");
            } else {
                uri = Uri.fromFile(file);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.setDataAndType(uri, "video/*");
            }

            startActivity(intent);

3、切圖
Intent intent = new Intent("com.android.camera.action.CROP");
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intent.setDataAndType(uri, "image/*");
                Uri customUri = null;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    customUri = FileProvider.getUriForFile(BaseApplication.getApplication(), BaseApplication.getApplication().getPackageName() + ".FileProvider",
                            new File(mLocalAvatarImagePath));
                } else {
                    customUri = Uri.fromFile(new File(mLocalAvatarImagePath));
                }
                intent.putExtra("output", customUri);
                intent.putExtra("crop", "true");
                intent.putExtra("aspectX", 1);  // 裁剪框比例
                intent.putExtra("aspectY", 0.75);
//            intent.putExtra("outputX", mOutputWidth);  // 輸出圖片大小
//            intent.putExtra("outputY", mOutputHeight);
                intent.putExtra("scale", true);  // 去黑邊
                intent.putExtra("scaleUpIfNeeded", true);  // 去黑邊
                if(mAttachedFragment == null)
                    mAttachedActivity.startActivityForResult(intent, REQUEST_UPLOAD_IMG_CODE);
                else
                    mAttachedFragment.startActivityForResult(intent, REQUEST_UPLOAD_IMG_CODE);

4、用系統安裝器安裝APK

Uri fileUri = null;
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                                fileUri = FileProvider.getUriForFile(BaseApplication.getApplication(), BaseApplication.getApplication().getPackageName() + ".FileProvider",
                                        new File(filePath));
                            } else {
                                fileUri = Uri.fromFile(new File(filePath));
                            }
                            Intent installIntent = new Intent(Intent.ACTION_VIEW);
                            installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            installIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                            installIntent.setAction(Intent.ACTION_VIEW);
                            installIntent.setDataAndType(fileUri,
                                    "application/vnd.android.package-archive");
                            context.startActivity(installIntent);


注意:通過content型別的uri和file型別的uri來獲取檔案的時候要注意方法的不同