Android 7.0 FileProvider 的使用與疑惑

Android 7.0 設(shè)備已經(jīng)逐漸普及了乔煞。然而和 6.0 系統(tǒng)的 運(yùn)行時(shí)權(quán)限 類似, Google 又針對這個(gè)版本做了一些出于安全性考慮的改動(dòng)劲赠。如果針對 24+ 的 SDK 版本 進(jìn)行開發(fā)逞带,你不得不做一些適配工作陌兑。

FileUriExposedException

我們創(chuàng)建一個(gè)項(xiàng)目铡原,目標(biāo) SDK 設(shè)置為 24 或者更大偷厦。
首先寫一個(gè)布局文件商叹,取名 activity_main
很簡單只泼,一個(gè)幀布局剖笙,里面一個(gè)按鈕。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:padding="16dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.jin.fileprovidertest.MainActivity">

    <Button
        android:id="@+id/ac_main_btn"
        android:text="click me!"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</FrameLayout>

對應(yīng)的 MainActivity 请唱,也很簡單弥咪,聲明賦值按鈕,然后添加點(diǎn)擊事件籍滴。
事件里調(diào)用 系統(tǒng)自帶 的相機(jī)應(yīng)用酪夷,這是 Google 推薦的方式,可以不用額外申請權(quán)限孽惰。
因?yàn)閮H僅為了測試,就不寫回調(diào)了鸥印,直接打開系統(tǒng)相機(jī)的活動(dòng)勋功。

package com.example.jin.fileprovidertest;

import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import java.io.File;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn = (Button) findViewById(R.id.ac_main_btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath()));
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                startActivity(intent);
            }
        });
    }
}

然后在一臺 Android 7.0 設(shè)備上啟動(dòng)之,結(jié)果一點(diǎn)按鈕就崩潰了——

1.gif

查看 logcat 库说,報(bào)了一個(gè)異常叫 FileUriExposedException
原來這是 Android 7.0 的一處安全性改動(dòng):不允許再直接使用真實(shí)路徑的URI狂鞋。 因此不能再直接調(diào) Uri.fromFile() 方法了,需要用到本文的主角 FileProvider 潜的。

FileProvider

這是 Android 7.0 新增的一個(gè)類骚揍,位于 v4 包下,繼承自四大組件之一的 ContentProvider 啰挪,因此需要 在清單配置文件里注冊信不。
首先在資源文件夾下新建一個(gè) xml 目錄,里面包含一個(gè)文件 test ——

1.png

文件內(nèi)容如下——

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <root-path name="my_image" path=""/>
    </paths>
</resources>

外兩層寫法是固定的亡呵,重點(diǎn)在第三層抽活。
首先是節(jié)點(diǎn)名 <root-path> ,它對應(yīng)前面代碼中 Environment.getExternalStorageDirectory() 方法獲取到的存儲(chǔ)路徑锰什。
如果是其它路徑下硕,節(jié)點(diǎn)名要做相應(yīng)改變——
<files-path> ,對應(yīng) Context.getFilesDir() 方法汁胆;
<cache-path> 梭姓,對應(yīng) getCacheDir() 方法;
<external-path>嫩码,Google 官方文檔和網(wǎng)上多數(shù)文章都說誉尖,是對應(yīng) Environment.getExternalStorageDirectory() 方法,但在現(xiàn)有設(shè)備上親測均無效谢谦,會(huì)報(bào) IllegalArgumentException 異常释牺。
<external-files-path> 萝衩,對應(yīng) Context.getExternalFilesDir() 方法;
<external-cache-path> 没咙,對應(yīng) Context.getExternalCacheDir() 方法猩谊。
然后是 path 屬性,Google 官方文檔和網(wǎng)上多數(shù)文章同樣都說祭刚,傳空牌捷,代表你整個(gè)對應(yīng)路徑都可以用于共享;傳入一個(gè)相對路徑涡驮,代表只有這個(gè)路徑的指向才能用于共享暗甥。但在現(xiàn)有設(shè)備上親測同樣均無效,會(huì)報(bào) IllegalArgumentException 異常捉捅。
以上二處原因不明撤防,請指教。

完成 test.xml 后棒口,我們?nèi)?AndroidManifest 文件里注冊——

<provider
    android:authorities="com.example.jin.fileprovidertest.fileprovider"
    android:name="android.support.v4.content.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/test"/>
</provider>

需要注意的就是 authorities 屬性寄月,Google 官方推薦的寫法是「包名 + .fileprovider」。其它屬性寫法固定无牵,resource 的值應(yīng)指向你之前的 xml 文件漾肮。
好了,現(xiàn)在修改我們的 MainAcitivity 茎毁,將 onClick()方法的內(nèi)容修改為——

final String authority = "com.example.jin.fileprovidertest.fileprovider";
boolean b = Build.VERSION.SDK_INT >= 24;
File file = new File(Environment.getExternalStorageDirectory().getPath());
Uri uri = b
        ? FileProvider.getUriForFile(MainActivity.this, authority, file)
        : Uri.fromFile(file);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivity(intent);

然后啟動(dòng)克懊,完美!


2.gif

最近看了一些文章七蜘,補(bǔ)充更新:

  • 建議自己創(chuàng)建一個(gè)類 MyProvider 繼承 FileProvider(類里面可以什么都不寫)谭溉,然后將 name 屬性的值設(shè)為 .MyProvider。這樣做的好處是可以確保當(dāng)進(jìn)行組件化開發(fā)時(shí)崔梗,不同組件之間的 merge 不會(huì)發(fā)生沖突夜只。
  • authority 屬性的值更規(guī)范一點(diǎn)的寫法是 ${applicationId}.fileprovider。相應(yīng)的在代碼中蒜魄,它的賦值為 getPackageName() + ".fileprovider"(需要有上下文環(huán)境)扔亥。
  • xml 文件的 <resource> 節(jié)點(diǎn)可以剝掉,直接用 <paths> 做根節(jié)點(diǎn)谈为。

本文結(jié)束旅挤,歡迎拍磚吐槽 and 指教~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市伞鲫,隨后出現(xiàn)的幾起案子粘茄,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柒瓣,死亡現(xiàn)場離奇詭異儒搭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)芙贫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門搂鲫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人磺平,你說我怎么就攤上這事魂仍。” “怎么了拣挪?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵擦酌,是天一觀的道長。 經(jīng)常有香客問我菠劝,道長赊舶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任赶诊,我火速辦了婚禮锯岖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘甫何。我一直安慰自己,他們只是感情好遇伞,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布辙喂。 她就那樣靜靜地躺著,像睡著了一般鸠珠。 火紅的嫁衣襯著肌膚如雪巍耗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天渐排,我揣著相機(jī)與錄音炬太,去河邊找鬼。 笑死驯耻,一個(gè)胖子當(dāng)著我的面吹牛亲族,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播可缚,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼霎迫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帘靡?” 一聲冷哼從身側(cè)響起知给,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后涩赢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體戈次,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年筒扒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了怯邪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霎肯,死狀恐怖擎颖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情观游,我是刑警寧澤搂捧,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站懂缕,受9級特大地震影響允跑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搪柑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一聋丝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧工碾,春花似錦弱睦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旬迹,卻和暖如春火惊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奔垦。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工屹耐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人椿猎。 一個(gè)月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓惶岭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鸵贬。 傳聞我的和親對象是個(gè)殘疾皇子俗他,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

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