基于Android FileProvider 屬性配置詳解及FileProvider多節(jié)點(diǎn)問(wèn)題
眾所周知在android7.0,修改了對(duì)私有存儲(chǔ)的限制,導(dǎo)致在獲取資源的時(shí)候,不能通過(guò)Uri.fromFile來(lái)獲取uri了我們需要適配7.0+的機(jī)型需要這樣寫(xiě):
1:代碼適配
if (Build.VERSION.SDK_INT > 23) {// intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(context, SysInfo.packageName + ".fileProvider", outputFile); intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); } else { intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-archive"); }
2:創(chuàng)建provider_paths.xml
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- /storage/emulated/0/Download/${applicationId}/.beta/apk--> <external-path name="beta_external_path" path="Download/"/> <!--/storage/emulated/0/Android/data/${applicationId}/files/apk/--> <external-path name="beta_external_files_path" path="Android/data/"/> </paths>
其中 provider_path屬性詳解
name和path
name:uri路徑片段。為了執(zhí)行安全,這個(gè)值隱藏你所共享的子目錄名。此值的子目錄名包含在路徑屬性中。
path:你所共享的子目錄。雖然name屬性是一個(gè)URI路徑片段,但是path是一個(gè)真實(shí)的子目錄名。注意,path是一個(gè)子目錄,而不是單個(gè)文件或者多個(gè)文件。
1.files-path
代表與Context.getFileDir()相同的文件路徑
2.cache-path
<cache-path name="name" path="path" />
代表與getCacheDir()相同的文件路徑
3.external-path
<external-path name="name" path="path" />
代表與Environment.getExternalStorageDirectory()相同的文件路徑
4.external-files-path
<external-files-path name="name" path="path" />
代表與Context#getExternalFilesDir(String) 和Context.getExternalFilesDir(null)相同的文件路徑
5.external-cache-path
<external-cache-path name="name" path="path" />
代表與Context.getExternalCacheDir()相同的文件路徑
6:配置AndroidManifest.xml
android:authorities在FileProvider中使用
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.mydomain.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
7:使用FileProvider
*** 返回URI:content://com.mydomain.fileprovider/my_images/default_image.jpg. File imagePath = new File(Context.getFilesDir(), "images"); File newFile = new File(imagePath, "default_image.jpg"); Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
8.自定義FileProvider
class MyFileProvider extends FileProvider {}
AndroidMenifest.xml中配置 android:authorities即可
3:我們項(xiàng)目中可能會(huì)用到其他一些第三方sdk有用到拍照功能的話,他也為了適配android7.0也添加了這個(gè)節(jié)點(diǎn),此時(shí)有些人可能就不知道如何下手了,其實(shí)很簡(jiǎn)單我們只要重寫(xiě)一個(gè)類 繼承自FileProvider,然后就按上述方法在添加一個(gè)節(jié)點(diǎn)就可以了:
<provider android:name="com.blueZhang.MyFileProvider" android:authorities="${applicationId}.provider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/cust_file_paths" /> </provider>
如果你不想自定義FileProvider,那么還有一種方法,那就是把第三方sdk中的路徑配置copy到provider_paths.xml即可。
如下所示:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- /storage/emulated/0/Download/${applicationId}/.beta/apk--> <external-path name="beta_external_path" path="Download/"/> <!--/storage/emulated/0/Android/data/${applicationId}/files/apk/--> <external-path name="beta_external_files_path" path="Android/data/"/> <external-path name="external_storage_root" path="."/> <files-path name="files" path="."/> </paths>
注意⚠️:在使用provider時(shí) 配置路徑 path="."代表所有路徑
生成 Content URI
在 Android 7.0 出現(xiàn)之前,我們通常使用 Uri.fromFile() 方法生成一個(gè) File URI。這里,我們需要使用 FileProvider 類提供的公有靜態(tài)方法 getUriForFile 生成 Content URI。
比如:
Uri contentUri = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID + ".fileProvider", myFile);
需要傳遞三個(gè)參數(shù)。第二個(gè)參數(shù)便是 Manifest 文件中注冊(cè) FileProvider 時(shí)設(shè)置的 authorities 屬性值,第三個(gè)參數(shù)為要共享的文件,并且這個(gè)文件一定位于第二步我們?cè)?path 文件中添加的子目錄里面。
舉個(gè)例子:
String filePath = Environment.getExternalStorageDirectory() + "/images/"+System.currentTimeMillis()+".jpg"; File outputFile = new File(filePath); if (!outputFile.getParentFile().exists()) { outputFile.getParentFile().mkdir(); } Uri contentUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileProvider", outputFile);
生成的 Content URI 是這樣的:
content://com.yifeng.samples.myprovider/my_images/1493715330339.jpg
其中,構(gòu)成 URI 的 host 部分為 <provider> 元素的 authorities 屬性值(applicationId + customname),path 片段 my_images 為 res/xml 文件中指定的子目錄別名(真實(shí)目錄名為:images)。
第四步,授予 Content URI 訪問(wèn)權(quán)限
生成 Content URI 對(duì)象后,需要對(duì)其授權(quán)訪問(wèn)權(quán)限。授權(quán)方式有兩種:
第一種方式,使用 Context 提供的 grantUriPermission(package, Uri, mode_flags) 方法向其他應(yīng)用授權(quán)訪問(wèn) URI 對(duì)象。三個(gè)參數(shù)分別表示授權(quán)訪問(wèn) URI 對(duì)象的其他應(yīng)用包名,授權(quán)訪問(wèn)的 Uri 對(duì)象,和授權(quán)類型。其中,授權(quán)類型為 Intent 類提供的讀寫(xiě)類型常量:
FLAG_GRANT_READ_URI_PERMISSION
FLAG_GRANT_WRITE_URI_PERMISSION
或者二者同時(shí)授權(quán)。這種形式的授權(quán)方式,權(quán)限有效期截止至發(fā)生設(shè)備重啟或者手動(dòng)調(diào)用 revokeUriPermission() 方法撤銷授權(quán)時(shí)。
第二種方式,配合 Intent 使用。通過(guò) setData() 方法向 intent 對(duì)象添加 Content URI。然后使用 setFlags() 或者 addFlags() 方法設(shè)置讀寫(xiě)權(quán)限,可選常量值同上。這種形式的授權(quán)方式,權(quán)限有效期截止至其它應(yīng)用所處的堆棧銷毀,并且一旦授權(quán)給某一個(gè)組件后,該應(yīng)用的其它組件擁有相同的訪問(wèn)權(quán)限。
第五步,提供 Content URI 給其它應(yīng)用
擁有授予權(quán)限的 Content URI 后,便可以通過(guò) startActivity() 或者 setResult() 方法啟動(dòng)其他應(yīng)用并傳遞授權(quán)過(guò)的 Content URI 數(shù)據(jù)。當(dāng)然,也有其他方式提供服務(wù)。
如果你需要一次性傳遞多個(gè) URI 對(duì)象,可以使用 intent 對(duì)象提供的 setClipData() 方法,并且 setFlags() 方法設(shè)置的權(quán)限適用于所有 Content URIs。
常見(jiàn)使用場(chǎng)景
前面介紹的內(nèi)容都是理論部分,在 開(kāi)發(fā)者官方 FileProvider 部分 都有所介紹。接下來(lái)我們看看,實(shí)際開(kāi)發(fā)一款應(yīng)用的過(guò)程中,會(huì)經(jīng)常遇見(jiàn)哪些 FileProvider 的使用場(chǎng)景。
自動(dòng)安裝文件
版本更新完成時(shí)打開(kāi)新版本 apk 文件實(shí)現(xiàn)自動(dòng)安裝的功能,應(yīng)該是最常見(jiàn)的使用場(chǎng)景,也是每個(gè)應(yīng)用必備功能之一。常見(jiàn)操作為,通知欄顯示下載新版本完畢,用戶點(diǎn)擊或者監(jiān)聽(tīng)下載過(guò)程自動(dòng)打開(kāi)新版本 apk 文件。適配 Android 7.0 版本之前,我們代碼可能是這樣:
File apkFile = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app_sample.apk"); Intent installIntent = new Intent(Intent.ACTION_VIEW); installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); installIntent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); startActivity(installIntent)
現(xiàn)在為了適配 7.0 及以上版本的系統(tǒng),必須使用 Content URI 代替 File URI。
在 res/xml 目錄下新建一個(gè) file_provider_paths.xml 文件(文件名自由定義),并添加子目錄路徑信息:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_download" path="Download"/> </paths>
然后在 Manifest 文件中注冊(cè) FileProvider 對(duì)象,并鏈接上面的 path 路徑文件:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.yifeng.samples.myprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths"/> </provider>
修改 java 代碼,根據(jù) File 對(duì)象生成 Content URI 對(duì)象,并授權(quán)訪問(wèn):
File apkFile = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app_sample.apk"); Uri apkUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID+".fileProvider", apkFile); Intent installIntent = new Intent(Intent.ACTION_VIEW); installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive"); startActivity(installIntent);
好了 有不明白的 及時(shí)聯(lián)系
以上這篇基于Android FileProvider 屬性配置詳解及FileProvider多節(jié)點(diǎn)問(wèn)題就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Android中EditText禁止輸入表情的實(shí)例代碼
本篇文章主要介紹了Android中EditText禁止輸入表情的實(shí)例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08Android 友盟第三方登錄與分享的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 友盟第三方登錄與分享的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09如何利用Flutter實(shí)現(xiàn)酷狗流暢Tabbar效果
這篇文章主要給大家介紹了關(guān)于如何利用Flutter實(shí)現(xiàn)酷狗流暢Tabbar效果的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02一文教你如何使用Databinding寫(xiě)一個(gè)關(guān)注功能
這篇文章主要介紹了一文教你如何使用Databinding寫(xiě)一個(gè)關(guān)注功能,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09Android懸浮窗按鈕實(shí)現(xiàn)點(diǎn)擊并顯示/隱藏多功能列表
這篇文章主要為大家詳細(xì)介紹了Android懸浮窗按鈕實(shí)現(xiàn)點(diǎn)擊并顯示/隱藏多功能列表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07Android端內(nèi)數(shù)據(jù)狀態(tài)同步方案VM-Mapping詳解
這篇文章主要介紹了Android端內(nèi)數(shù)據(jù)狀態(tài)同步方案VM-Mapping詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09android:TextView簡(jiǎn)單設(shè)置文本樣式和超鏈接的方法
這篇文章主要介紹了android:TextView簡(jiǎn)單設(shè)置文本樣式和超鏈接的方法,涉及TextView常見(jiàn)文字屬性的相關(guān)操作技巧,需要的朋友可以參考下2016-08-08Android如何使用ViewPager2實(shí)現(xiàn)頁(yè)面滑動(dòng)切換效果
這篇文章主要給大家介紹了關(guān)于Android如何使用ViewPager2實(shí)現(xiàn)頁(yè)面滑動(dòng)切換效果的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02