應(yīng)用安裝(FileProvider)
應(yīng)用內(nèi)安裝apk時(shí)涉及到通過(guò)Intent在兩個(gè)應(yīng)用間共享資源,Android 從 N 開(kāi)始不允許以 file:// 的方式通過(guò) Intent 在兩個(gè) App 之間分享文件,取而代之的是通過(guò) FileProvider 生成 content://Uri,否則直接使用會(huì)拋出異常導(dǎo)致奔潰所以首先我們來(lái)了解一下FileProvider相關(guān)的知識(shí)。
首先給出FileProvider的官方介紹https://developer.android.google.cn/reference/androidx/core/content/FileProvider
FileProvider 是一個(gè)特殊的 ContentProvider 子類(lèi),通過(guò) content://Uri 代替 file://Uri 實(shí)現(xiàn)不同 App 間的文件安全共享。當(dāng)通過(guò)包含 Content URI 的 Intent 共享文件時(shí),需要申請(qǐng)臨時(shí)的讀寫(xiě)權(quán)限,可以通過(guò) Intent.setFlags() 方法實(shí)現(xiàn)。而 file://Uri 方式需要申請(qǐng)長(zhǎng)期有效的文件讀寫(xiě)權(quán)限,直到這個(gè)權(quán)限被手動(dòng)改變?yōu)橹梗@是極其不安全的做法。因此 Android 從 N 版本開(kāi)始禁止通過(guò) file://Uri 在不同 App 之間共享文件。
FileProvider的使用步驟:
1,定義一個(gè)FileProvider
2,指定可用的文件
3,生成文件的Content URI
4,給此URI授予臨時(shí)權(quán)限
5,將此Content URI通過(guò)Intent傳遞給另一個(gè)App
接下來(lái)詳細(xì)介紹
1,定義FileProvider
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
android:authorities:applicationId+fileProvider
applicationId:這里寫(xiě)成動(dòng)態(tài)獲取應(yīng)用包名,如果寫(xiě)死在項(xiàng)目包含多個(gè)moudle時(shí)出現(xiàn)不一致的情況
android:exported="false":表示此provider是私有的不對(duì)其他應(yīng)用公開(kāi)
android:grantUriPermissions="true":表示授予臨時(shí)權(quán)限
provider_paths:指定用來(lái)配置文件目錄的xml文件,此處指定為provider_paths,名稱可以自定義
2,指定可用的文件provider_paths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--相當(dāng)于Context.getFilesDir()-->
<files-path name="name" path="path" />
<!--相當(dāng)于Context. getCacheDir()-->
<cache-path name="name" path="path" />
<!--相當(dāng)于Environment.getExternalStorageDirectory()-->
<external-path name="name" path="path" />
<!--相當(dāng)于Context.getExternalFilesDir(String) Context.getExternalFilesDir(null).-->
<external-files-path name="name" path="path" />
<!--相當(dāng)于Context.getExternalCacheDir()-->
<external-cache-path name="name" path="path" />
<!--相當(dāng)于Context.getExternalMediaDirs()-->
<external-media-path name="name" path="path" />
...
</paths>
注意:這里只需配置自己文件所保存的path即可,name為別名影藏實(shí)際名稱,path為實(shí)際的文件路徑如果對(duì)Android文件存儲(chǔ)這塊知識(shí)不太了解可以看一下另一篇介紹:http://www.itdecent.cn/p/88dbdd8613db
3,生成文件的Content URI
完成了前兩步的配置,接下來(lái)通過(guò)官方提供的getUriForFile(File file) 方法生成能夠被其他應(yīng)用訪問(wèn)的Content URI加入有一個(gè)apk文件路徑為磁盤(pán)根目錄則相應(yīng)文件對(duì)象為
File file = new File("/sdcard/app.apk");
此文件對(duì)應(yīng)的可通過(guò)如下方式獲得uri
Uri uri=FileProvider.getUriForFile(FaceTempratureActivity.this, BuildConfig.APPLICATION_ID + ".provider", file);
獲取到Uri后我們將通過(guò)Intent將此Uri傳遞給另一個(gè)應(yīng)用,此處定義一個(gè)Intent
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(uri,"application/vnd.android.package-archive");
4,給此URI授予臨時(shí)權(quán)限
有了可訪問(wèn)的Uri路徑我們還得申請(qǐng)臨時(shí)讀寫(xiě)文件權(quán)限,這里可以直接通過(guò)Intent.setFlags()方法設(shè)置
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
5,將此Content URI通過(guò)Intent傳遞給另一個(gè)App
statActivity(intent);
最后給出一個(gè)應(yīng)用安裝的示例:
private void installApp(Content content) {
File fileS = new File("/sdcard/temp.apk");
Uri data;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
// 給目標(biāo)應(yīng)用一個(gè)臨時(shí)授權(quán)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
data = FileProvider.getUriForFile(content, BuildConfig.APPLICATION_ID + ".provider", fileS);
} else {
data = Uri.fromFile(fileS);
}
intent.setDataAndType(data, "application/vnd.android.package-archive");
startActivity(intent);
}