Android適配總結(jié)之FileProvider

日常夜拍

1 概述

由于前段時間做的應(yīng)用都是車機(jī)上的(Android6.0),并不需要太多適配。想想到現(xiàn)在麸祷,Android已經(jīng)到9了澎怒,貌似我好久都沒做過適配了,這些適配的工作還是有必要做一些記錄阶牍。

參考資料

通常開發(fā)中喷面,適配Android7.0主要就是適配其中文件訪問權(quán)限。比如調(diào)用系統(tǒng)拍照走孽,應(yīng)用更新后安裝功能都是涉及到惧辈。

在官方7.0的以上的系統(tǒng)中,嘗試傳遞 file://URI可能會觸發(fā)FileUriExposedException

本文內(nèi)容主要參考鴻洋_的文章磕瓷,僅作記錄盒齿。

2 使用FileProvider

2.1 聲明provider念逞,設(shè)置文件

在清單文件中聲明provider標(biāo)簽,如:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="me.newtrekwang.android7.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

android:authorities表示授權(quán)者边翁,這里的格式一般是[appId].fileprovider
android:exported只能為false
android:grantUriPermissions="true"表示授權(quán)Uri權(quán)限 翎承,且必須為true

meta-data里設(shè)置指定的文件目錄,為引用某個xml文件,格式如下

例如:file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <root-path name="root" path="" />
    <files-path name="files" path="" />
    <cache-path name="cache" path="" />
    <external-path name="external" path="" />
    <external-files-path name="name" path="path" />
    <external-cache-path name="name" path="path" />
</paths>
  • <root-path/> 代表設(shè)備的根目錄new File("/");
  • <files-path/> 代表context.getFilesDir()
  • <cache-path/> 代表context.getCacheDir()
  • <external-path/> 代表Environment.getExternalStorageDirectory()
  • <external-files-path>代表context.getExternalFilesDirs()
  • <external-cache-path>代表getExternalCacheDirs()

比如我常用的:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <!--存儲卡的Pictures目錄放圖片文件-->
    <external-path
        name="my_images"
        path="Pictures"/>
    <!--存儲卡的flyaudiosmart2019/apk目錄放安裝包文件-->  
    <external-path
        name="my_download"
        path="flyaudiosmart2019/apk"/>
</paths>

2.2 使用FileProvider API

調(diào)用系統(tǒng)拍照倒彰,構(gòu)造Intent就需要傳入一個Uri,那么Uri就必須使用FileProvider來獲取审洞,例如:

    // 文件名
    String filename = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.CHINA)
                    .format(new Date()) + ".png";
    // file對象,注意路徑要和resource xml里配置的一樣
    File file = new File(Environment.getExternalStorageDirectory()+File.separator+"Pictures", filename);
    // 生成Uri
    Uri fileUri = FileProvider.getUriForFile(this, "me.newtrekwang.android7.fileprovider", file);
    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

其實Uri的實際內(nèi)容為:content://me.newtrekwang.android7.fileprovider/my_images/Pictures/20190210-041411.png,即格式為content://authorities/定義的name屬性/文件的相對路徑

但是這樣統(tǒng)一用FileProvider獲取Uri是不夠的待讳,在Android7可以正常運行芒澜,但是Android4.4就會報錯

Caused by: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{52b029b8 1670:com.android.camera/u0a36} (pid=1670, uid=10036) that is not exported from uid 10052
at android.os.Parcel.readException(Parcel.java:1465)
at android.os.Parcel.readException(Parcel.java:1419)
at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:2848)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:4399)

因為低版本的系統(tǒng),僅僅是把這個當(dāng)成一個普通的Provider在使用创淡,而我們沒有授權(quán)痴晦,contentprovider的export設(shè)置的也是false;導(dǎo)致Permission Denial琳彩。

所以唯一的辦法就是授權(quán)了誊酌,可以使用Context的grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION| Intent.FLAG_GRANT_WRITE_URI_PERMISSION)!,不過加了步授權(quán)露乏,很麻煩碧浊。

最簡單的方法是判斷下版本,低版本直接獲取Uri就行了瘟仿。

即:

Uri fileUri = null;
if (Build.VERSION.SDK_INT >= 24) {
    fileUri = FileProvider.getUriForFile(this, "me.newtrekwang.android7.fileprovider", file);
} else {
    fileUri = Uri.fromFile(file);
}

在拍照場景這樣的Uri是不會出問題的箱锐,注意:在安裝apk的業(yè)務(wù)場景下,intent用這樣的Uri還是會報錯,請求安裝的intent劳较,還要加上intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION),否則還是要步授權(quán)設(shè)置

3 總結(jié):統(tǒng)一的方案:單獨一個Module做FileProvder配置

1 創(chuàng)建一個library的Module驹止,然后在該清單文件定義你的FileProvider,然后在路徑配置文件中定義項目中所有要用到的路徑。

2 定義一個工具观蜗,功能就是獲取合適的Uri

FileProviderUtils.java

/**
 * @author newtrekWang
 * @fileName FileProviderUtils
 * @createDate 2019/2/14 11:47
 * @email 408030208@qq.com
 * @desc FileProvider工具類,用于適配Android7.0的文件訪問變更
 */
public final class FileProviderUtils {
    /**
     * 獲取合適的File Uri
     * @param context
     * @param file
     * @return
     */
    public static Uri getUriForFile(Context context, File file) {
        Uri fileUri = null;
        if (Build.VERSION.SDK_INT >= 24) {
            fileUri = getUriForFile24(context, file);
        } else {
            fileUri = Uri.fromFile(file);
        }
        return fileUri;
    }

    /**
     * 獲取File Uri from 安卓7及以上版本
     * @param context
     * @param file
     * @return
     */
    public static Uri getUriForFile24(Context context, File file) {
        Uri fileUri = android.support.v4.content.FileProvider.getUriForFile(context,
                "你自己的authorities",
                file);
        return fileUri;
    }

    /**
     * 用戶安裝apk場景
     * @param context
     * @param intent
     * @param type
     * @param file
     * @param writeAble
     */
    public static void setIntentDataAndType(Context context,
                                            Intent intent,
                                            String type,
                                            File file,
                                            boolean writeAble) {
        if (Build.VERSION.SDK_INT >= 24) {
            intent.setDataAndType(getUriForFile(context, file), type);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            if (writeAble) {
                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            }
        } else {
            intent.setDataAndType(Uri.fromFile(file), type);
        }
    }

3 其他有需要的module依賴此module即可臊恋,獲取Uri從FileProviderUtils獲取。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末墓捻,一起剝皮案震驚了整個濱河市抖仅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌砖第,老刑警劉巖撤卢,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異厂画,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拷邢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門袱院,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事忽洛∧寤荩” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵欲虚,是天一觀的道長集灌。 經(jīng)常有香客問我,道長复哆,這世上最難降的妖魔是什么欣喧? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮梯找,結(jié)果婚禮上唆阿,老公的妹妹穿的比我還像新娘。我一直安慰自己锈锤,他們只是感情好驯鳖,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著久免,像睡著了一般浅辙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阎姥,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天记舆,我揣著相機(jī)與錄音,去河邊找鬼丁寄。 笑死氨淌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伊磺。 我是一名探鬼主播盛正,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼屑埋!你這毒婦竟也來了豪筝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤摘能,失蹤者是張志新(化名)和其女友劉穎续崖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體团搞,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡严望,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逻恐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片像吻。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡峻黍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拨匆,到底是詐尸還是另有隱情姆涩,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布惭每,位于F島的核電站骨饿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏台腥。R本人自食惡果不足惜宏赘,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望览爵。 院中可真熱鬧置鼻,春花似錦、人聲如沸蜓竹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俱济。三九已至嘶是,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛛碌,已是汗流浹背聂喇。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留蔚携,地道東北人希太。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像酝蜒,于是被迫代替她去往敵國和親誊辉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容