android 選擇圖片的權(quán)限問題

我們知道從android 6.0開始 系統(tǒng)開始對權(quán)限把控,7.0權(quán)限更是對系統(tǒng)權(quán)限進(jìn)一步更改?主要就是三個方面

APP應(yīng)用程序的私有文件不再向使用者放寬

Intent組件傳遞file://URI的方式可能給接收器留下無法訪問的路徑,觸發(fā)FileUriExposedException異常,推薦使用FileProvider

DownloadManager不再按文件名分享私人存儲的文件。舊版應(yīng)用在訪問COLUMN_LOCAL_FILENAME時可能出現(xiàn)無法訪問的路徑。面向 Android 7.0 或更高版本的應(yīng)用在嘗試訪問 COLUMN_LOCAL_FILENAME 時會觸發(fā) SecurityException

一、深入理解FileProvider

FileProvider屬于Android 7.0新增的一個類,該類位于v4包下,詳情可見android.support.v4.content.FileProvider,使用方法類似與ContentProvider,簡單概括為三個步驟,這里以調(diào)用sdcard公共目錄安裝app為例,演示使用過程:

在資源文件夾res/xml下新建file_provider.xml文件,文件聲明權(quán)限請求的路徑,代碼如下:

<?xml version="1.0" encoding="utf-8"?>

<resources>

? ? <paths>

? ? ? ? <external-path path="" name="download"/>

? ? </paths>

</resources>

在AndroidManifest.xml添加組件provider相關(guān)信息,類似組件activity,指定resource屬性引用上一步創(chuàng)建的xml文件(后面會詳細(xì)介紹各個屬性的用法),代碼如下:

<provider

? ? android:name="android.support.v4.content.FileProvider"

? ? android:authorities="com.wisdomclass.clus.fileprovider"

? ? android:grantUriPermissions="true"

? ? android:exported="false">

? ? <meta-data

? ? ? ? android:name="android.support.FILE_PROVIDER_PATHS"

? ? ? ? android:resource="@xml/provider_paths" />

</provider>

最后一步,Java代碼申請權(quán)限,使用新增的方法getUriForFile()和grantUriPermission(),代碼如下(后面會詳細(xì)介紹方法對應(yīng)參數(shù)的使用):

public void InstallApk(){

? ? String filePath = getExternalFilesDir("Download").getAbsolutePath() + File.separator+"軟件名稱"+mVersion+".apk";

? ? Intent install = new Intent(Intent.ACTION_VIEW);

? ? if(Build.VERSION.SDK_INT>=24) {//判讀版本是否在7.0以上

? ? ? ? Uri apkUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID +".fileprovider", new File(filePath));

? ? ? ? install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

? ? ? ? install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//添加這一句表示對目標(biāo)應(yīng)用臨時授權(quán)該Uri所代表的文件

? ? ? ? install.setDataAndType(apkUri, "application/vnd.android.package-archive");

? ? }else {

? ? ? ? install.setDataAndType(Uri.parse("file://"+filePath), "application/vnd.android.package-archive");

? ? ? ? install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

? ? }

? ? startActivity(install);

}

1.1 定義一個FileProvider

直接使用FileProvider本身或者它的子類,需要在AndroidManifest.xml文件中聲明組件的相關(guān)屬性,包括:

android:name,對應(yīng)屬性值:android.support.v4.content.FileProvider或者子類完整路徑

android:authorities,對應(yīng)屬性值是一個常量,通常定義的方式packagename.fileprovider,例如:cn.teachcourse.fileprovider

android:exported,對應(yīng)屬性值是一個boolean變量,設(shè)置為false

android:grantUriPermissions,對應(yīng)屬性值也是一個boolean變量,設(shè)置為true,允許獲得文件臨時的訪問權(quán)限

<manifest>

? ? ...

? ? <application>

? ? ? ? ...

? ? ? ? <provider

? ? ? ? ? ? android:name="android.support.v4.content.FileProvider"

? ? ? ? ? ? android:authorities="com.mydomain.fileprovider"

? ? ? ? ? ? android:exported="false"

? ? ? ? ? ? android:grantUriPermissions="true">

? ? ? ? ? ? ...

? ? ? ? </provider>

? ? ? ? ...

? ? </application>

</manifest>


想要關(guān)聯(lián)res/xml文件夾下創(chuàng)建的file_provider.xml文件,需要在<provider>標(biāo)簽內(nèi),添加<meta-data>子標(biāo)簽,設(shè)置<meta-data>標(biāo)簽的屬性值,包括:

android:name,對應(yīng)屬性值是一個固定的系統(tǒng)常量android.support.FILE_PROVIDER_PATHS

android:resource,對應(yīng)屬性值指向我們的xml文件@xml/file_provider

<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_provider" />

</provider>

1.2 指定授予臨時訪問權(quán)限的文件目錄

上一步說明了怎么定義一個FileProvider,這一步主要說明怎么定義一個@xml/file_provider文件。Android Studio或Eclipse開發(fā)工具創(chuàng)建Android項目的時候默認(rèn)不會創(chuàng)建res/xml文件夾,需要開發(fā)者手動創(chuàng)建,點擊res文件夾新建目錄,命名xml,如下圖:

然后,在xml文件夾下新建一個xml文件,文件命名file_provider.xml,指定根標(biāo)簽為paths,如下圖:

在xml文件中指定文件存儲的區(qū)塊和區(qū)塊的相對路徑,在<paths>根標(biāo)簽中添加<files-path>子標(biāo)簽(稍后詳細(xì)列出所有子標(biāo)簽),設(shè)置子標(biāo)簽的屬性值,包括:

name,是一個虛設(shè)的文件名(可以自由命名),對外可見路徑的一部分,隱藏真實文件目錄

path,是一個相對目錄,相對于當(dāng)前的子標(biāo)簽<files-path>根目錄

<files-path>,表示內(nèi)部內(nèi)存卡根目錄,對應(yīng)根目錄等價于Context.getFilesDir(),查看完整路徑:

/data/user/0/cn.teachcourse.demos/files

代碼如下:

<paths xmlns:android="http://schemas.android.com/apk/res/android">

? ? <files-path name="my_images" path="images/"/>

? ? ...

</paths>

<paths>根標(biāo)簽下可以添加的子標(biāo)簽也是有限的,參考官網(wǎng)的開發(fā)文檔,除了上述的提到的<files-path>這個子標(biāo)簽外,還包括下面幾個:

<cache-path>,表示應(yīng)用默認(rèn)緩存根目錄,對應(yīng)根目錄等價于getCacheDir(),查看完整路徑:/data/user/0/cn.teachcourse.demos/cache

<external-path>,表示外部內(nèi)存卡根目錄,對應(yīng)根目錄等價于

Environment.getExternalStorageDirectory(),

查看完整路徑:/storage/emulated/0

<external-files-path>,表示外部內(nèi)存卡根目錄下的APP公共目錄,對應(yīng)根目錄等價于

Context#getExternalFilesDir(String) Context.getExternalFilesDir(null),

查看完整路徑:

/storage/emulated/0/Android/data/cn.teachcourse.demos/files/Download

<external-cache-path>,表示外部內(nèi)存卡根目錄下的APP緩存目錄,對應(yīng)根目錄等價于Context.getExternalCacheDir(),查看完整路徑:

/storage/emulated/0/Android/data/cn.teachcourse.demos/cache

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容