關(guān)于 Android 7.0 適配中 FileProvider 部分的總結(jié)

由于 Android 7.0 或更高版本的系統(tǒng)在國(guó)內(nèi)手機(jī)市場(chǎng)上的占比不是很高欺缘,很多 Android 開發(fā)人員并沒有做 7.0 適配工作,同時(shí)測(cè)試人員也容易忽視這方面的兼容問題挤安。這導(dǎo)致 7.0 及以上版本的手機(jī)用戶在使用到應(yīng)用部分功能時(shí)可能出現(xiàn) App 崩潰閃退谚殊。其中,大部分原因都是由項(xiàng)目中使用到 file:// 類型的 URI 所引發(fā)的蛤铜。本文我們便來(lái)一探究竟嫩絮。

Android 7.0 權(quán)限變更


為了提高私有目錄的安全性,防止應(yīng)用信息的泄漏围肥,從 Android 7.0 開始剿干,應(yīng)用私有目錄的訪問權(quán)限被做限制。具體表現(xiàn)為穆刻,開發(fā)人員不能夠再簡(jiǎn)單地通過(guò) file:// URI 訪問其他應(yīng)用的私有目錄文件或者讓其他應(yīng)用訪問自己的私有目錄文件置尔。

備注:如果你對(duì)應(yīng)用私有目錄不太清楚的話,可以閱讀我的這篇文章:了解 Android 應(yīng)用的文件存儲(chǔ)目錄氢伟,掌握持久化數(shù)據(jù)的正確姿勢(shì)榜轿。

同時(shí),也是從 7.0 開始朵锣,Android SDK 中的 StrictMode 策略禁止開發(fā)人員在應(yīng)用外部公開 file:// URI谬盐。具體表現(xiàn)為,當(dāng)我們?cè)趹?yīng)用中使用包含 file:// URI 的 Intent 離開自己的應(yīng)用時(shí)诚些,程序會(huì)發(fā)生故障飞傀。

開發(fā)中,如果我們?cè)谑褂?file:// URI 時(shí)忽視了這兩條規(guī)定,將導(dǎo)致用戶在 7.0 及更高版本系統(tǒng)的設(shè)備中使用到相關(guān)功能時(shí)砸烦,出現(xiàn) FileUriExposedException 異常犀被,導(dǎo)致應(yīng)用出現(xiàn)崩潰閃退問題。而這兩個(gè)過(guò)程的替代解決方案便是使用 FileProvider外冀。

FileProvider


作為四大組件之一的 ContentProvider寡键,一直扮演著應(yīng)用間共享資源的角色。這里我們要使用到的 FileProvider雪隧,就是 ContentProvider 的一個(gè)特殊子類西轩,幫助我們將訪問受限的 file:// URI 轉(zhuǎn)化為可以授權(quán)共享的 content:// URI。

第一步脑沿,注冊(cè)一個(gè) FileProvider

作為系統(tǒng)四大組件之一的 ContentProvider藕畔,其子類FileProvider,也同樣需要使用 <provider> 元素在 Manifest 文件中添加注冊(cè)信息庄拇,并按照要求設(shè)置相關(guān)屬性值注服。

<application>
    ...
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.yourname"
        android:exported="false"
        android:grantUriPermissions="true">
        ...
    </provider>
    ...
</application>

其中,android:authorities 屬性值是一個(gè)由 build.gradle 文件中的 applicationId 值和自定義的名稱組成的 Uri 字符串(這樣寫是約定俗成的)措近。其他屬性值使用如上固定值即可溶弟。

第二步,添加共享目錄

在 res/xml 目錄下新建一個(gè) xml 文件瞭郑,用于存放應(yīng)用需要共享的目錄文件辜御。這個(gè) xml 文件的內(nèi)容類似這樣:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="my_images" path="images/"/>
    ...
</paths>

<path> 元素必須包含一到多個(gè)子元素。這些子元素用于指定共享文件的目錄路徑屈张,必須是這些元素之一:

  • <files-path>:內(nèi)部存儲(chǔ)空間應(yīng)用私有目錄下的 files/ 目錄擒权,等同于 Context.getFilesDir() 所獲取的目錄路徑;

  • <cache-path>:內(nèi)部存儲(chǔ)空間應(yīng)用私有目錄下的 cache/ 目錄阁谆,等同于 Context.getCacheDir() 所獲取的目錄路徑碳抄;

  • <external-path>:外部存儲(chǔ)空間根目錄,等同于 Environment.getExternalStorageDirectory() 所獲取的目錄路徑场绿;

  • <external-files-path>:外部存儲(chǔ)空間應(yīng)用私有目錄下的 files/ 目錄剖效,等同于 Context.getExternalFilesDir(null) 所獲取的目錄路徑;

  • <external-cache-path>:外部存儲(chǔ)空間應(yīng)用私有目錄下的 cache/ 目錄裳凸,等同于 Context.getExternalCacheDir()贱鄙;

可以看出,這五種子元素基本涵蓋內(nèi)外存儲(chǔ)空間所有目錄路徑姨谷,包含應(yīng)用私有目錄。同時(shí)映九,每個(gè)子元素都擁有 namepath 兩個(gè)屬性梦湘。

其中,path 屬性用于指定當(dāng)前子元素所代表目錄下需要共享的子目錄名稱。注意:path 屬性值不能使用具體的獨(dú)立文件名捌议,只能是目錄名哼拔。

而 name 屬性用于給 path 屬性所指定的子目錄名稱取一個(gè)別名。后續(xù)生成 content:// URI 時(shí)瓣颅,會(huì)使用這個(gè)別名代替真實(shí)目錄名倦逐。這樣做的目的,很顯然是為了提高安全性宫补。

如果我們需要分享的文件位于同級(jí)別目錄下不同的子目錄中檬姥,就需要添加多個(gè)子元素逐一指定要分享的文件目錄,或者共享他們通用的父目錄也行粉怕。

添加完共享目錄后健民,再在 <provider> 元素中使用 <meta-data> 元素將 res/xml 中的 path 文件與注冊(cè)的 FileProvider 鏈接起來(lái):

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.yourname"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/yourfilename" />
</provider>

第三步,生成 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 + ".myprovider", 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 + ".myprovider", 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 訪問權(quán)限

生成 Content URI 對(duì)象后还绘,需要對(duì)其授權(quán)訪問權(quán)限楚昭。授權(quán)方式有兩種:

第一種方式,使用 Context 提供的 grantUriPermission(package, Uri, mode_flags) 方法向其他應(yīng)用授權(quán)訪問 URI 對(duì)象拍顷。三個(gè)參數(shù)分別表示授權(quán)訪問 URI 對(duì)象的其他應(yīng)用包名抚太,授權(quán)訪問的 Uri 對(duì)象,和授權(quán)類型昔案。其中尿贫,授權(quán)類型為 Intent 類提供的讀寫類型常量:

  • 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è)置讀寫權(quán)限拼缝,可選常量值同上。這種形式的授權(quán)方式彰亥,權(quán)限有效期截止至其它應(yīng)用所處的堆棧銷毀咧七,并且一旦授權(quán)給某一個(gè)組件后,該應(yīng)用的其它組件擁有相同的訪問權(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。

常見使用場(chǎng)景


前面介紹的內(nèi)容都是理論部分丰介,在 開發(fā)者官方 FileProvider 部分 都有所介紹背蟆。接下來(lái)我們看看,實(shí)際開發(fā)一款應(yīng)用的過(guò)程中哮幢,會(huì)經(jīng)常遇見哪些 FileProvider 的使用場(chǎng)景带膀。

自動(dòng)安裝文件

版本更新完成時(shí)打開新版本 apk 文件實(shí)現(xiàn)自動(dòng)安裝的功能,應(yīng)該是最常見的使用場(chǎng)景橙垢,也是每個(gè)應(yīng)用必備功能之一跨算。常見操作為涎显,通知欄顯示下載新版本完畢,用戶點(diǎn)擊或者監(jiān)聽下載過(guò)程自動(dòng)打開新版本 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)訪問:

File apkFile = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app_sample.apk");
Uri apkUri = FileProvider.getUriForFile(this,
        BuildConfig.APPLICATION_ID+".myprovider", 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);

如此這般,便完成了應(yīng)用中調(diào)用系統(tǒng)功能打開 apk 文件的 7.0 適配工作监嗜。

調(diào)用系統(tǒng)拍照

調(diào)用系統(tǒng)拍照功能時(shí)也需要傳遞一個(gè) Uri 對(duì)象谐檀,用于保存圖片至指定目錄,這里也需要適配 7.0 版本裁奇。其他步驟不再贅述桐猬,核心 java 代碼如下(路徑不同,注意添加 res/xml 中的 path 文件子目錄):

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 + ".myprovider", outputFile);

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);
startActivityForResult(intent, REQUEST_TAKE_PICTURE);

調(diào)用系統(tǒng)裁剪

調(diào)用系統(tǒng)裁剪的過(guò)程中涉及到兩個(gè) Uri 對(duì)象:inputUri 和 outputUri框喳,較為復(fù)雜一些课幕。通常厦坛,調(diào)用系統(tǒng)裁剪的來(lái)源為調(diào)用系統(tǒng)拍照或選擇系統(tǒng)相冊(cè)五垮。前者返回的是一個(gè) File URI 對(duì)象乍惊,后者返回的是一個(gè) Content URI 對(duì)象。作為裁剪源放仗,我們要做的就是對(duì)其做進(jìn)一步處理润绎。但是不能像上面那樣使用 getUriForFile() 方法,這個(gè)并不難理解诞挨,因?yàn)槿绻沁x擇系統(tǒng)相冊(cè)所得的圖片莉撇,本身也不一定屬于我們自己的應(yīng)用。正確處理方式是這樣:

private Uri getImageContentUri(String path){
    Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            new String[]{MediaStore.Images.Media._ID},
            MediaStore.Images.Media.DATA + "=? ",
            new String[]{path}, null);
    if (cursor != null && cursor.moveToFirst()) {
        int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));
        Uri baseUri = Uri.parse("content://media/external/images/media");
        return Uri.withAppendedPath(baseUri, ""+id);
    }else {
        ContentValues contentValues = new ContentValues(1);
        contentValues.put(MediaStore.Images.Media.DATA, path);
        return getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
    }
}

拿到正確的 Content URI 后惶傻,作為 inputUri棍郎,傳遞給 Intent 對(duì)象:

Intent intent = new Intent("com.android.camera.action.CROP");
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(inputUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputFile));
startActivityForResult(intent, REQUEST_PICK);

注意:這里的 outputUri 并沒有改變,仍然使用的是 Uri.fromFile() 方法獲取的 File URI 類型银室!這是很奇怪的一點(diǎn)涂佃,但是不得不這么做。事實(shí)上蜈敢,使用這種方式調(diào)用系統(tǒng)裁剪功能本身就是有問題的辜荠!常見問題如:在部分機(jī)型上,調(diào)用系統(tǒng)裁剪并返回前一個(gè)頁(yè)面時(shí)抓狭,在 onActivityResult() 方法中得到的 resultCode 值不等于 RESULT_OK伯病。Crop Intent 在官方文檔中本來(lái)就無(wú)跡可尋,本身就是一種不推薦的用法否过!取而代之的是午笛,我們可以使用 GitHub 上的一些開源庫(kù)實(shí)現(xiàn)應(yīng)用內(nèi)的圖片裁剪功能,比如 uCrop苗桂、cropper 等药磺。

歷史版本問題


說(shuō)了這么多,還有一個(gè)大家比較關(guān)心的問題就是:哪些已經(jīng)上線的舊版本應(yīng)用沒有做 7.0 適配工作怎么辦誉察?關(guān)于這個(gè)問題与涡,Google 已經(jīng)提前幫我們想好解決方案啦。

還記得 6.0 運(yùn)行時(shí)權(quán)限問題嗎持偏?如果你不想處理運(yùn)行時(shí)權(quán)限事宜的話驼卖,只需要在 build.gradle 文件中將 targetSdkVersion 的值設(shè)為 23 以下即可。

同樣的鸿秆,只要 targetSdkVersion 值小于 24酌畜,F(xiàn)ile URI 的使用依舊可以出現(xiàn)在 7.0 及以上版本的設(shè)備中。不過(guò)需要注意的是卿叽,如前面所述桥胞,調(diào)用系統(tǒng)裁剪功能比較特殊恳守,可能會(huì)出現(xiàn)一些問題。

雖然 Google 在每次發(fā)布新版 Android 系統(tǒng)時(shí)贩虾,都提供這種設(shè)置 targetSdkVersion 的方式兼容舊版本催烘,但只是一種臨時(shí)解決方案,并不推薦大家使用這種技巧繞開新版本的適配問題缎罢。要知道伊群,新出現(xiàn)的 API 改變一定是在解決過(guò)去存在的系統(tǒng)問題,是一種進(jìn)步的表現(xiàn)策精。遵循規(guī)范舰始,是我們每個(gè)開發(fā)人員開發(fā)時(shí)都應(yīng)銘記于心的格言。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咽袜,一起剝皮案震驚了整個(gè)濱河市丸卷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌询刹,老刑警劉巖谜嫉,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異范抓,居然都是意外死亡骄恶,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門匕垫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)僧鲁,“玉大人,你說(shuō)我怎么就攤上這事象泵∧海” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵偶惠,是天一觀的道長(zhǎng)春寿。 經(jīng)常有香客問我,道長(zhǎng)忽孽,這世上最難降的妖魔是什么绑改? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮兄一,結(jié)果婚禮上厘线,老公的妹妹穿的比我還像新娘。我一直安慰自己出革,他們只是感情好造壮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著骂束,像睡著了一般耳璧。 火紅的嫁衣襯著肌膚如雪成箫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天旨枯,我揣著相機(jī)與錄音蹬昌,去河邊找鬼。 笑死召廷,一個(gè)胖子當(dāng)著我的面吹牛凳厢,可吹牛的內(nèi)容都是我干的账胧。 我是一名探鬼主播竞慢,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼治泥!你這毒婦竟也來(lái)了筹煮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤居夹,失蹤者是張志新(化名)和其女友劉穎败潦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體准脂,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡劫扒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狸膏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沟饥。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖湾戳,靈堂內(nèi)的尸體忽然破棺而出贤旷,到底是詐尸還是另有隱情,我是刑警寧澤砾脑,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布幼驶,位于F島的核電站,受9級(jí)特大地震影響韧衣,放射性物質(zhì)發(fā)生泄漏盅藻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一畅铭、第九天 我趴在偏房一處隱蔽的房頂上張望氏淑。 院中可真熱鬧,春花似錦顶瞒、人聲如沸夸政。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)守问。三九已至匀归,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間耗帕,已是汗流浹背穆端。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仿便,地道東北人体啰。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嗽仪,于是被迫代替她去往敵國(guó)和親荒勇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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

  • 上周闻坚,寫了個(gè)小demo沽翔,正好同事使用的小米手機(jī)系統(tǒng)內(nèi)核更新到7.0,遂拿來(lái)測(cè)試了一番窿凤。其中遇到的小問題仅偎,現(xiàn)在來(lái)跟大...
    monkey_who閱讀 4,636評(píng)論 0 13
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)雳殊,斷路器橘沥,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • 今天來(lái)聊聊Android 7.0 FileUriExposedException異常,以及它的使用方法和使用場(chǎng)景 ...
    joker_fu閱讀 2,083評(píng)論 0 3
  • Android7.0發(fā)布已經(jīng)有一個(gè)多月了夯秃,Android7.0在給用戶帶來(lái)一些新的特性的同時(shí)座咆,也給開發(fā)者帶來(lái)了新的...
    東經(jīng)315度閱讀 1,364評(píng)論 0 14
  • Android N系列適配---FileProvider Android 7.0的適配,主要包含方面: Andro...
    25a58172fbb5閱讀 7,092評(píng)論 3 32