天天看點

安卓适配--FileProvider使用FileProvider

官方文檔說:

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

要在應用間共享檔案,您應發送一項 content:// URI,并授予 URI 臨時通路權限。進行此授權的最簡單方式是使用 FileProvider 類。

在所有情況下,要将應用中的檔案提供給其他應用,唯一安全的做法就是向接收方應用發送檔案的内容 URI,并授予對該 URI 的臨時通路權限。具有臨時 URI 通路權限的内容 URI 之是以安全,是因為它們僅供接收該 URI 的應用使用,并且會自動過期。Android FileProvider 元件提供了 getUriForFile() 方法,用于生成檔案的内容 URI。

使用FileProvider

1、在清單中聲明provider标簽,如下:

由于FileProvider的預設功能包括檔案的内容URI生成,是以不需要在代碼中定義子類。相反,您可以在應用程式中包含一個檔案提供程式,方法是完全用XML指定它。要指定FileProvider元件本身,請在應用程式清單中添加< provider>元素。

将android:name屬性設定為androidx.core.content.FileProvider。

将android:authorities屬性設定為基于您控制的域的URI權限;例如,如果您控制域mydomain.com,則應使用authority com.mydomain.fileprovider。

将android:exported屬性設定為false;檔案提供程式不需要是公共的。

将android:grantUriPermissions屬性設定為true,以允許您授予對檔案的臨時通路權限

<provider
            android:authorities="com.example.broadcast_force_offline.fileprovider"
            android:name="androidx.core.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>
//android:authorities表示授權者,這裡的格式一般是[appId].fileprovider
//android:exported隻能為false,檔案提供程式不需要是公共的
//android:grantUriPermissions="true"表示授權Uri權限 ,且必須為true
           

meta-data裡設定指定的檔案目錄,為引用某個xml檔案,格式如下:

< meta-data >

直譯為“中繼資料”,該标簽可為< activity>、< activity-alias>、< application>、< provider>、< receiver>、< service>等元件提供附加資料項。

元件元素可以包含任意數量的< meta-data >子元素。系統将meta-data配置的資料存儲于一個Bundle對象中,可以通過PackageItemInfo.metaData字段擷取。

文法配置:

1、android:name 配置設定給該标簽的鍵,即唯一名稱。

2、android:resource 對資源的引用,如“@string/app_name”。該資源ID可以通過該metaData.getInt()方法擷取 。

3、android:value 配置設定給該标簽的值,如String、Boolean等。

例如:file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
    name="my_images"
    path=""/>
    <!--path屬性的值表示共享的的具體路徑,為空代表将整個SD卡共享-->
</paths>
           

< root-path/> 代表裝置的根目錄new File("/");

< files-path/> 代表context.getFilesDir()

< cache-path/> 代表context.getCacheDir()

< external-path/> 代表Environment.getExternalStorageDirectory()

< external-files-path>代表context.getExternalFilesDirs()

< external-cache-path>代表getExternalCacheDirs()

參考代碼案例:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <!--存儲卡的Pictures目錄放圖檔檔案-->
    <external-path
        name="my_images"
        path="Pictures"/>
    <!--存儲卡的flyaudiosmart2019/apk目錄放安裝封包件-->  
    <external-path
        name="my_download"
        path="flyaudiosmart2019/apk"/>
</paths>
           

2、使用FileProvider API

調用系統拍照,構造Intent就需要傳入一個Uri,那麼Uri就必須使用FileProvider來擷取:

Uri imageUri;
       	
        protected void onCreate(Bundle savedInstanceState){
       
       								......
       
        takePhoto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //建立File對象,用于存儲拍照後的照片,将圖檔命名為output_image.jpg,并将它存放在目前應用緩存資料的位置,4.4系統以前通路SD卡關聯目錄需要聲明通路SD卡權限
                File outputImage=new File(getExternalCacheDir(),"output_image.jpg");
                try {
                    if (outputImage.exists()){
                        outputImage.delete();
                    }
                    outputImage.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (Build.VERSION.SDK_INT>=24)
                {
                //生成Uri
                    imageUri= FileProvider.getUriForFile(CameraAlbumActivity.this,"com.example.broadcast_force_offline.fileprovider",outputImage);
                }
                else
                {
                    imageUri=Uri.fromFile(outputImage);
                }
                //啟動相機程式
                Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
                //調用putExtra()方法指定圖檔的輸出位址
                startActivityForResult(intent,1);
            }
        });
        
        							......
        
        }
     @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode)
        {
            case 1:
                if(resultCode==RESULT_OK)
                {
                    try {
                        //将照片顯示出來
                        Bitmap bitmap= BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                        picture.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }

                }
                break;
                default:
                    break;
        }
    }
           

這裡在ImageView裡直接顯示Bitmap。如果需要上傳圖檔到伺服器,需要把圖檔儲存為臨時檔案。建立檔案需要聲明對外部存儲的寫權限

參考資料:

Android适配總結之FileProvider

第一行代碼(第二版)

淺談Android中的meta-data及其應用

官方文檔:FileProvider