Android FileProvider介紹

Android 7.0 文件共享要使用FileProvider

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

對於文件訪問在Android 7.0以前可以使用file://uri的方式訪問,但是這個地方有個問題,就是即使不是你自身應用產生的文件,只要知道對方的uri則就可以調用到,這樣在安全性上就產生了風險。所以Android 7.0後新增了對文件跨進程訪問的限制,這個限制會造成,如果使用file://uri的方式訪問,則會出現android.os.FileUriExposedException的異常。

FileProvider簡介

FileProvider是ContentProvider的一個特殊的子類,它讓應用間共享文件變得更加容易,其通過創建一個Content URI來代替File URI。

一個Content URI 允許開發者可賦予一個臨時的讀或寫權限。當創建一個包含Content URI的Intent的時候,爲了能夠讓另一個應用也可以使用這個URI,你需要調用Intent.setFlags()來添加權限。只要接收Activity的棧是活躍的,則客戶端應用就可以獲取到這些權限。如果該Intent是用來啓動一個Service,則只要該Service是正在運行的,則也可以獲取到這些權限。

相比之下,如果想要通過File URI來控制權限,開發者必須修改底層文件系統的權限。這些權限會對任意的app開放,直到你修改了它。這種級別的訪問基本上是不安全的。

通過Content URI來提高文件訪問的安全性,使得FileProvider成爲Android安全基礎設置的一個關鍵部分。

FileProvider 使用

1、 AndroidManifest ,註冊FileProvider

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
     <application
         ...>
         <provider
             android:name="android.support.v4.content.FileProvider"
             android:authorities="org.unreal.update"
             android:grantUriPermissions="true"
             android:exported="false">
             <meta-data
                 android:name="android.support.FILE_PROVIDER_PATHS"
                 android:resource="@xml/update_files" />
         </provider>
         ...
     </application>
 </manifest>

android:authorities 參數,如同ContentProvider,此處需要你提供一個uri的authorities,以便於content://android:authorities/uri訪問到,android:authorities可以隨意定義。

2、 res下創建xml文件夾,並在xml文件夾下創建update_files.xml

<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <paths>
        <files-path path="files" name="files" />
        <cache-path path="files" name="cache" /> 
        <external-path path="files" name="external" />
        <external-files-path path="files" name="externalfiles"/>
        <!-- 此標籤需要 support 25.0.0以上纔可以使用-->
        <external-cache-path  path="files" name="externalcache"/> 
    </paths>
</resources>

name:名稱標誌字符串,不可以同名!即key

path:文件夾“相對路徑”,完整路徑取決於當前的標籤類型。

標籤 路徑
…….. * 代表 當前文件夾及其子文件夾
file-path 物理路徑爲Context.getFilesDir() + /files/*
cache-path 物理路徑爲Context.getCacheDir() + /files/*
external-path 物理路徑爲Environment.getExternalStorageDirectory() + /files/*
external-files-path 物理路徑爲Context.getExternalFilesDir(String) + /files/*
external-cache-path 物理路徑爲Context.getExternalCacheDir() + /files/*

<paths>裏邊的元素必須是一下的一個或者多個:

<files-path name="name" path="path" /> 對應Context.getFilesDir() + “/path/”,即/data/data/<package-name>/files/path/。
<cache-path name="name" path="path" /> 對應Context.getCacheDir() + “/path/”,即/data/data/<package-name>/cache/path/。
<external-files-path name="name" path="path" /> 對應Context.getExternalFilesDir(null) + “/path/”,即/storage/emulated/0/Android/data/<package_name>/files/path/。
<external-cache-path name="name" path="path" /> 對應Context.getExternalCacheDir() + “/path/”,即/storage/emulated/0/Android/data/<package-name>/cache/path/。
<external-path name="name" path="path" /> 對應Environment.getExternalStorageDirectory() + “/path/”,即/storage/emulated/0/path/。
這些paths裏邊有相同的子元素,即name和path。

3、通過getUriForFile獲取到uri

data = FileProvider.getUriForFile(context,"之前在AndroidManifest中配置的android:authorities", "7.0後文件的路徑");
// 給目標應用一個臨時授權
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
               | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

FLAG_GRANT_READ_URI_PERMISSION:表示讀取權限; 
FLAG_GRANT_WRITE_URI_PERMISSION:表示寫入權限; 
根據你的需求,是讀取呢,還是寫入自行選擇

完!!!

示例: 安裝Apk,兼容Android 7.0以上及以下版本

private void installApk(File apk) {
        if (!apk.exists()) {
            return;
        }
        Uri data;
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // UpdateConfig.FILE_PROVIDER_AUTH 即是在清單文件中配置的authorities
            data = FileProvider.getUriForFile(context, UpdateConfig.FILE_PROVIDER_AUTH, apk);
            // 給目標應用一個臨時授權
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            data = Uri.fromFile(apk);
        }
        intent.setDataAndType(data, "application/vnd.android.package-archive");
        context.startActivity(intent);
        android.os.Process.killProcess(android.os.Process.myPid());
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章