前言:這是我的第一篇博客,之前一直有寫博客的想法,不知道從何下手而一直往后拖,近來因為一些原因越來越意識到自己的知識的不足,終於下定決心彌補,希望通過堅持寫博客能給自己一些提高。本文是前兩個月遇到的情況,更新APP的時候在一些系統7.0以上的手機會崩潰,所以花了一上午的時間研究,在此再做記錄。
概述
android7.0中有一些新的變化,(官方文檔),例如多窗口的支持,FileProvider等等,今天我們只對FileProvider做講解,這也是我們在開發過程中必須要做的適配。
在7.0以上系統的手機上,Android框架執行的StrictMode API政策禁止在應用外部公開file//URI;傳遞file://URI會報出FileUriExposedException異常。如果要在應用間共享文件,應發送一項content//URI,最簡單的方式就是使用FileProvider類。
使用方法
1,在ManiFest中的的節點中注冊一個FileProvider,因為FileProvider是ContentProvider的子類;
name:配置當前FileProvider的實現類,一般為固定的。也可以寫繼承FileProvider的實現類
authorities:配置一個FileProviderde 名字,必須是當前系統內的唯一值,一般為【包名+字符串】(例:com.example.demo.customName,如上圖格式會自動引用gradle中的applicationId)
exported:表示該FileProvider是否需要公開,一般不需要,本人測試:必須false否則拋異常
grantUriPermissions:是否允許授權的文件臨時訪問權限,本人測試:必須true否則拋異常
2,配置可分享的文件的路徑
name:是固定的,
resource:需要在res目錄下新建xml文件夾,這里指向xml文件夾下的provider_paths文件,此文件的根節點為paths;
paths 標簽內,必須配置最少一個 xxx-path 標簽
//path=""---------表示為當前指定目錄 如下:
//root-path:表示根目錄,『/』。
//files-path:表示 content.getFileDir() 獲取到的目錄。
//files-path:表示 content.getFileDir() 獲取到的目錄。
//files-path:表示 content.getFileDir() 獲取到的目錄。
//files-path:表示 content.getFileDir() 獲取到的目錄。
//external-cache-path:表示 ContextCompat.getExternalCacheDirs() 獲取到的目錄
3,使用content:// 調用FileProvider.getUriForFile()
/**@param context 上下文對象
*@param authority 這里是前面在 AndroidManifest.xml 中 配置的 android:authorities 。
*@param file 要獲取URI的file對象
*/
Fileprovider.getUriForFile(Context context,String authority,File file)
// 調用此方法,會自動得到一個 file:// 轉換成 content:// 的 一個 Uri 對象,可以供我們直接使用。
生成的 Content URI 是這樣的
content://com.example.demo.customName/*****
host 部分為 元素的 authorities 屬性值(applicationId + customname),
path 片段為 res/xml 文件中指定的子目錄別名
4,授予臨時讀寫的權限
在配置 provider 標簽的時候,有一個屬性 android:grantUriPermissions=”true” ,它表示允許它授予 Uri 臨時的權限。
當我們生成出一個 content:// 的 Uri 對象之后,其實也無法對其直接使用,還需要對這個 Uri 接收的 App 賦予對應的權限才可以。
授權類型的常量,被定義在 Intent 類中。
授權的方式分為兩種:
使用Context.grantUriPermission() 為其他 App 授予 Uri 對象的訪問權限。
/****
*
*@param toPackage 表示授予權限的 App 的包名。
*@param uri 授予權限的 content:// 的 Uri。
*@param modeFlags 讀寫權限。
*/
@Override
public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
super.grantUriPermission(toPackage, uri, modeFlags);
}
//這種情況下,授權的有效期限,從授權一刻開始,截止於設備重啟或者手動調用 Context.revokeUriPermission() 方法,才會收回對此 Uri 的授權。
示例代碼:(此處借鑒鴻洋大神的圖片 博客—->)
配合 Intent.addFlags() 授權。 setFlags() == addflags()
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
示例代碼:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
Uri data;
// 判斷版本大於等於7.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
data = FileProvider.getUriForFile(context,"com.example.demo.fileprovider", file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
} else {
data = Uri.fromFile(file);
}
一旦授權,直到該 App 被完全退出,這段時間內,該 App 享有對此 Uri 指向的文件的對應權限,我們無法再主動收回此權限
常見使用場景
1,自動安裝文件
2,調用系統相機拍照
3,調用系統裁剪
總結:終於完成了第一篇技術博客,發現真心不容易,在這里向各位堅持寫博客的大佬致敬,本人也會堅持寫下去,努力提升自己的水平,不拖android開發工程師的后腿。
由於本人水平有限,代碼可能會出現超出本人認知范圍的Bug,歡迎各位大佬批評指正。