作者:積木zz


前言


最近看到一條新聞,Android 11(version 30,Andorid R)最終Beta版 如期發(fā)布,看到這個(gè)新聞我知道我不能再拖了,再不好好準(zhǔn)備好迎接Android11的到來,到時(shí)候迎接我的就是客戶的指責(zé),甚至老板的一封休書了 。

今天就和大家一起看看Android11到底改了些什么,以及最重要的,我們需要怎么適配?targetversion不改到30,是不是就不用適配了呢?

以下我分為兩部分講述,分別是

  • 以Adnroid11 為目標(biāo)版本的應(yīng)用(targetSdkVersion>=30才有影響)?
  • 所有應(yīng)用在Android11設(shè)備上適配改動(dòng)(無論targetSdkVersion是多少,只要在Android11設(shè)備上運(yùn)行的應(yīng)用都有影響)

為什么先說targetSdkVersion>=30的模塊呢?因?yàn)橐话銇碚f為了Google為了讓我們更長時(shí)間適應(yīng)新的內(nèi)容以及保障線上應(yīng)用的穩(wěn)定,都會(huì)把改動(dòng)大的,需要花時(shí)間適配的內(nèi)容放到新的targetSdkVersion對(duì)應(yīng)的應(yīng)用上,如果你暫時(shí)沒有適配targetSdkVersion30的需求,也可以看看第二模塊,看看是否有涉及你的應(yīng)用相關(guān)內(nèi)容。GOGOGO!

Tips:此適配文章會(huì)不間斷更新,根據(jù)Android11發(fā)布進(jìn)度調(diào)整,歡迎點(diǎn)贊關(guān)注。(打?的格外注意哦)


適配targetSdkVersion30

此模塊的修改內(nèi)容只針對(duì)targetSdkVersion 30或者以上才生效。


分區(qū)存儲(chǔ)強(qiáng)制執(zhí)行?

對(duì)外部存儲(chǔ)目錄的訪問僅限于應(yīng)用專屬目錄,以及應(yīng)用已創(chuàng)建的特定類型的媒體。

關(guān)于分區(qū)存儲(chǔ),在Android10就已經(jīng)推行了,簡單的說,就是應(yīng)用對(duì)于文件的讀寫只能在沙盒環(huán)境,也就是屬于自己應(yīng)用的目錄里面讀寫。其他媒體文件可以通過MediaStore進(jìn)行訪問。

但是在android10的時(shí)候,Google還是為開發(fā)者考慮,留了一手。在targetSdkVersion = 29應(yīng)用中,設(shè)置android:requestLegacyExternalStorage="true",就可以不啟動(dòng)分區(qū)存儲(chǔ),讓以前的文件讀取正常使用。但是targetSdkVersion = 30中不行了,強(qiáng)制開啟分區(qū)存儲(chǔ)。 當(dāng)然,作為人性化的android,還是為開發(fā)者留了一小手,如果是覆蓋安裝呢,可以增加android:preserveLegacyExternalStorage="true",暫時(shí)關(guān)閉分區(qū)存儲(chǔ),好讓開發(fā)者完成數(shù)據(jù)遷移的工作。為什么是暫時(shí)呢?因?yàn)橹灰遁d重裝,就會(huì)失效了。以下是關(guān)于分區(qū)存儲(chǔ)會(huì)遇到的所有情況,給大家羅列出來了,先上代碼:

fun saveFile() { if (checkPermission()) { //getExternalStoragePublicDirectory被棄用,分區(qū)存儲(chǔ)開啟后就不允許訪問了 val filePath = Environment.getExternalStoragePublicDirectory("").toString() + "; val fw = FileWriter(filePath) ("hello world") () showToast("文件寫入成功") } }

分情況運(yùn)行:

1) targetSdkVersion = 28,運(yùn)行后正常讀寫。

2) targetSdkVersion = 29,不刪除應(yīng)用,targetSdkVersion 由28修改到29,覆蓋安裝,運(yùn)行后正常讀寫。

3) targetSdkVersion = 29,刪除應(yīng)用,重新運(yùn)行,讀寫報(bào)錯(cuò),程序崩潰(open failed: EACCES (Permission denied))

4) targetSdkVersion = 29,添加android:requestLegacyExternalStorage="true"(不啟用分區(qū)存儲(chǔ)),讀寫正常不報(bào)錯(cuò)

5) targetSdkVersion = 30,不刪除應(yīng)用,targetSdkVersion 由29修改到30,讀寫報(bào)錯(cuò),程序崩潰(open failed: EACCES (Permission denied))

6) targetSdkVersion = 30,不刪除應(yīng)用,targetSdkVersion 由29修改到30,增加android:preserveLegacyExternalStorage="true",讀寫正常不報(bào)錯(cuò)

7) targetSdkVersion = 30,刪除應(yīng)用,重新運(yùn)行,讀寫報(bào)錯(cuò),程序崩潰(open failed: EACCES (Permission denied))


ok,那到底應(yīng)該怎么改呢?三種方法訪問文件:

1)應(yīng)用專屬目錄

//分區(qū)存儲(chǔ)空間 val file = File, filename) //應(yīng)用專屬外部存儲(chǔ)空間 val appSpecificExternalDir = File(), filename)

2)訪問公共媒體目錄文件

val cursor = con, null, null, null, "${MediaS} desc") if (cursor != null) { while ()) { val id = cur)) val uri = Con, id) println("image uri is $uri") } cur() }

3) SAF(存儲(chǔ)訪問框架--Storage Access Framework)

val intent = Inten) in) in = "image/*" startActivityForResult(intent, 100) @RequiresApi) override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { (requestCode, resultCode, data) if (data == null || resultCode != Ac) return if (requestCode == 100) { val uri = da println("image uri is $uri") } }

具體還有很多操作可以看看網(wǎng)上關(guān)于分區(qū)存儲(chǔ)的資料,因?yàn)锳ndroid10已經(jīng)出來很久了,所以資料還是很多的,這里推薦幾篇

訪問應(yīng)用專屬文件

Android 10適配要點(diǎn),作用域存儲(chǔ)

AndroidQ(10)分區(qū)存儲(chǔ)完美適配

說到這里可能又有人問了,那我的應(yīng)用就是個(gè)手機(jī)管理器,總不能不讓我清其他應(yīng)用的緩存了吧,有辦法!Android提供了兩個(gè)intent入口:

  • 調(diào)用ACTION_MANAGE_STORAGE intent 操作檢查可用空間。
  • 調(diào)用ACTION_CLEAR_APP_CACHE intent 操作清除所有緩存。

說來說去,反正應(yīng)用數(shù)據(jù)私有化是大勢所趨,還是早點(diǎn)適配分區(qū)存儲(chǔ),別等以后手機(jī)只有沙盒機(jī)制的時(shí)候,就來不及了。


媒體文件訪問權(quán)限 ?

為了在保證用戶隱私的同時(shí)可以更輕松地訪問媒體,Android 11 增加了以下功能。執(zhí)行批量操作和使用直接文件路徑和原生庫訪問文件。

1)執(zhí)行批量操作

這里的批量操作指的是Android 11 向 MediaStore API 中添加了多種方法,用于簡化特定媒體文件更改流程(例如在原位置編輯照片),分別是:

  • createWriteRequest() 用戶向應(yīng)用授予對(duì)指定媒體文件組的寫入訪問權(quán)限的請(qǐng)求。
  • createFavoriteRequest()用戶將設(shè)備上指定的媒體文件標(biāo)記為“收藏”的請(qǐng)求。對(duì)該文件具有讀取訪問權(quán)限的任何應(yīng)用都可以看到用戶已將該文件標(biāo)記為“收藏”。
  • createTrashRequest() 用戶將指定的媒體文件放入設(shè)備垃圾箱的請(qǐng)求。垃圾箱中的內(nèi)容會(huì)在系統(tǒng)定義的時(shí)間段后被永久刪除。
  • createDeleteRequest() 用戶立即永久刪除指定的媒體文件(而不是先將其放入垃圾箱)的請(qǐng)求。

直接看個(gè)例子:

val urisToModify = listOf(uri,uri,...) val editPendingIntent = MediaS(contentResolver, urisToModify) // Launch a system prompt requesting user permission for the operation. startIntentSenderForResul, EDIT_REQUEST_CODE, null, 0, 0, 0) override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { EDIT_REQUEST_CODE -> if (resultCode == Ac) { /* Edit request granted; proceed. */ } else { /* Edit request not granted; explain to the user. */ } } }

傳入uri的集合,獲取用戶的同意后,就可以進(jìn)行操作了。

2)直接文件路徑和原生庫訪問文件

沒錯(cuò)!Android11又恢復(fù)了使用直接文件路徑訪問訪問媒體文件!哈哈,這樣就方便多了。也就是除了 MediaStore API之外還有兩種方式可以訪問媒體文件:

  • File API。
  • 原生庫,例如 fopen()。

那Android10咋辦呢??要不就用MediaStore,要不就直接把分區(qū)存儲(chǔ)關(guān)了吧(requestLegacyExternalStorage=true)


所有文件訪問權(quán)限 ?

雖然說了這么多,但是還有些應(yīng)用就要訪問所有文件,比如殺毒軟件,文件管理器。放心,有辦法!MANAGE_EXTERNAL_STORAGE 這不來了嗎。 這個(gè)權(quán)限就是用來獲取所有文件的管理權(quán)限。:

<uses-permission android:name="android.; /> val intent = Intent() in Se startActivity(intent) //判斷是否獲取MANAGE_EXTERNAL_STORAGE權(quán)限: val isHasStoragePermission= Environment.isExternalStorageManager()

來張截圖過過癮:


電話號(hào)碼相關(guān)權(quán)限 ?

Android 11 更改了您的應(yīng)用在讀取電話號(hào)碼時(shí)使用的與電話相關(guān)的權(quán)限。

具體改了什么呢?其實(shí)就是兩個(gè)API:

  • TelecomManager 類中的 getLine1Number() 方法
  • TelecomManager 類中的 getMsisdn() 方法

也就是當(dāng)用到這兩個(gè)API的時(shí)候,原來的READ_PHONE_STATE權(quán)限不管用了,需要READ_PHONE_NUMBERS權(quán)限才行。

下面具體說說,targetSdkVersion修改到30,然后運(yùn)行一個(gè)獲取電話號(hào)碼的程序:

Ac(this, arrayO), 100) b { val tm = ) as TelephonyManager val phoneNumber = showToast(phoneNumber) }

崩潰了:

java.lang.SecurityException: getLine1NumberForDisplay: Neither user 10151 nor current process has android., android., or android.

預(yù)想之中哈,Andmani中注冊(cè)好權(quán)限,并且添加動(dòng)態(tài)權(quán)限申請(qǐng):

<uses-permission android:name="android." /> <uses-permission android:name="android." /> Ac(this, arrayO,Manifest.), 100)

搞定,如果你只需要獲取手機(jī)號(hào)碼這一個(gè)功能,也可以只申請(qǐng)READ_PHONE_NUMBERS這一個(gè)權(quán)限:

<uses-permission android:name="android." android:maxSdkVersion="29" /> <uses-permission android:name="android." />


自定義消息框視圖被屏蔽 ?

從 Android 11 開始,已棄用自定義消息框視圖。如果您的應(yīng)用以 Android 11 為目標(biāo)平臺(tái),包含自定義視圖的消息框在從后臺(tái)發(fā)布時(shí)會(huì)被屏蔽

可能有人會(huì)奇怪了,什么是自定義消息框視圖?。课艺f英文你就知道了,英文是custom toast views,也就是自定義toast。簡單寫個(gè)代碼:

Toast toast = new Toast(context); (show_length); (view); ();

糟了糟了,自定義toast被棄用了?我們項(xiàng)目就是用的這個(gè)??!不用擔(dān)心,只是不允許自定義toast從后臺(tái)顯示了。 比如我寫一個(gè)3秒后再顯示toast,然后應(yīng)用一打開就進(jìn)入后臺(tái),看看會(huì)發(fā)生什么:

Handler().postDelayed({ IToa("你好,我是自定義toast") }, 3000) W/NotificationService: Blocking custom toast from package com.exam due to package not in the foreground

啥也沒顯示,只是發(fā)出來一個(gè)警告。 所以不用太過擔(dān)心,如果實(shí)在需要后臺(tái)顯示,就用普通的toast吧!


現(xiàn)在需要 APK 簽名方案 v2 ?

對(duì)于以 Android 11(API 級(jí)別 30)為目標(biāo)平臺(tái),且目前僅使用 APK 簽名方案 v1 簽名的應(yīng)用,現(xiàn)在還必須使用 APK 簽名方案 v2 或更高版本進(jìn)行簽名。用戶無法在搭載 Android 11 的設(shè)備上安裝或更新僅通過 APK 簽名方案 v1 簽名的應(yīng)用。

這個(gè)介紹已經(jīng)很明顯了吧,如果你的targetSdkVersion修改到30,那么你就必須要加上v2簽名才行。否則無法安裝和更新。


媒體intent操作需要系統(tǒng)默認(rèn)相機(jī) ?

從 Android 11 開始,只有預(yù)裝的系統(tǒng)相機(jī)應(yīng)用可以響應(yīng)以下 intent 操作:

android.media.ac android.media.ac android.media.ac_SECURE

也就是說,如果我調(diào)用intent喚起照相機(jī),使用VIDEO_CAPTURE的action,只有系統(tǒng)的相機(jī)能夠響應(yīng),而第三方的相機(jī)應(yīng)用不會(huì)響應(yīng)了。

val intent=Intent() inandroid.provider.MediaStore.ACTION_IMAGE_CAPTURE startActivity(intent) //無法喚起第三方相機(jī)了,只能喚起系統(tǒng)相機(jī)

這點(diǎn)對(duì)普通的相機(jī)應(yīng)用還是有點(diǎn)打擊的,官方給的建議是如果要使用特定的第三方相機(jī)應(yīng)用來代表其捕獲圖片或視頻,可以通過為intent設(shè)置軟件包名稱或組件來使這些intent變得明確。


5G ?

Android 11 添加了在您的應(yīng)用中支持 5G 的功能

新的Android11也是支持了5G相關(guān)的一些功能,包括:

  • 檢測是否連接到了5G網(wǎng)絡(luò)
  • 檢查按流量計(jì)費(fèi)性

首先是檢測5G網(wǎng)絡(luò),通過TelephonyManager的監(jiān)聽方法:

private fun getNetworkType(){ val tManager = getSystemService) as TelephonyManager (object : PhoneStateListener() { @RequiresApi) override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) { if (this@Android11Test2Activity, android.Manifest.) != android.con) { return } (telephonyDisplayInfo) when) { Tele -> showToast("高級(jí)專業(yè)版 LTE (5Ge)") Tele -> showToast("NR (5G) - 5G Sub-6 網(wǎng)絡(luò)") Tele_MMWAVE -> showToast("5G+/5G UW - 5G mmWave 網(wǎng)絡(luò)") else -> showToast("other") } } }, P) }

如果是5g網(wǎng)絡(luò),就免不了要去判斷是不是按流量計(jì)費(fèi)的,否則5G的流量可不是開玩笑的。

檢測流量計(jì)費(fèi)方法也很簡單,監(jiān)聽網(wǎng)絡(luò),在回調(diào)中判斷:

val manager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager manager.registerDefaultNetworkCallback(object : Connec() { override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { (network, networkCapabilities) //true 代表連接不按流量計(jì)費(fèi) val isNotFlowPay=ne) || ne) } })

判斷該值,如果為 true,則將連接視為不按流量計(jì)費(fèi)。


后臺(tái)位置信息訪問權(quán)限 ?

在搭載 Android 11 的設(shè)備上,當(dāng)應(yīng)用中的某項(xiàng)功能請(qǐng)求在后臺(tái)訪問位置信息時(shí),用戶看到的系統(tǒng)對(duì)話框不再包含用于啟用后臺(tái)位置信息訪問權(quán)限的按鈕。如需啟用后臺(tái)位置信息訪問權(quán)限,用戶必須在設(shè)置頁面上針對(duì)應(yīng)用的位置權(quán)限設(shè)置一律允許選項(xiàng)。

什么意思呢?在較低版本的Android系統(tǒng)中,當(dāng)應(yīng)用獲得前臺(tái)位置信息訪問權(quán)限時(shí),也會(huì)自動(dòng)獲得后臺(tái)位置信息訪問權(quán)限。比如我請(qǐng)求一個(gè)前臺(tái)位置訪問權(quán)限:

requestPermissions(arrayO), 100)

授權(quán)后,就能同時(shí)獲取前臺(tái)位置權(quán)限和后臺(tái)位置權(quán)限(ACCESS_BACKGROUND_LOCATION)。

但是現(xiàn)在不行了,你必須單獨(dú)申請(qǐng)后臺(tái)位置權(quán)限,而且,要在獲取前臺(tái)權(quán)限之后,順序還不能亂。

requestPermissions(arrayO), 100)

如果在沒獲取前臺(tái)權(quán)限的時(shí)候執(zhí)行這個(gè)獲取后臺(tái)權(quán)限的代碼會(huì)沒反應(yīng),等獲取前臺(tái)權(quán)限(ACCESS_COARSE_LOCATION)之后,申請(qǐng)后臺(tái)權(quán)限就會(huì)跳轉(zhuǎn)到一個(gè)新的權(quán)限頁面了,而且必須選擇Allow all the time (始終允許)才能獲得后臺(tái)位置權(quán)限,看圖:



軟件包可見性 ?

Android 11 更改了應(yīng)用查詢用戶已在設(shè)備上安裝的其他應(yīng)用以及與之交互的方式。使用新的 元素,應(yīng)用可以定義一組自身可訪問的其他應(yīng)用。通過告知系統(tǒng)應(yīng)向您的應(yīng)用顯示哪些其他應(yīng)用,此元素有助于鼓勵(lì)最小權(quán)限原則。此外,此元素還可幫助 Google Play 等應(yīng)用商店評(píng)估應(yīng)用為用戶提供的隱私權(quán)和安全性。

也就是說,Android11中,如果你想去獲取其他應(yīng)用的信息,比如包名,名稱等等,不能直接獲取了,必須在清單文件中添加<queries>元素,告知系統(tǒng)你要獲取哪些應(yīng)用信息或者哪一類應(yīng)用。

比如我這段查詢應(yīng)用信息的代碼:

val pm = val listAppcations: List<ApplicationInfo> = pm .getInstalledApplication) for (app in listAppcations) { Log.e("lz",a) }

在Android11版本,只能查詢到自己應(yīng)用和系統(tǒng)應(yīng)用的信息,查不到其他應(yīng)用的信息了。怎么呢?添加<queries>元素,兩種方式:

1)元素中加入具體包名

<manifest package="com.exam;> <queries> <package android:name="com.exam; /> <package android:name="com.exam; /> </queries> ... </manifest>

1)元素中加入固定過濾的intent

<manifest package="com.exam;> <queries> <intent> <action android:name="android.in; /> <data android:mimeType="image/jpeg" /> </intent> </queries> </manifest>

可能還是有人會(huì)疑惑,那我的應(yīng)用是瀏覽器或者設(shè)備管理器咋辦呢?我就要獲取所有包名??? 放心,Android11還引入了 QUERY_ALL_PACKAGES 權(quán)限,清單文件中加入即可。但是Google Play可不一定能濫用哦,它為需要QUERY_ALL_PACKAGES 權(quán)限的應(yīng)用會(huì)提供相關(guān)指南,但是還沒出來,具體要看后面的消息了。

至于國內(nèi)市場。。。(希望能有個(gè)應(yīng)用市場一統(tǒng)天下好好管理這混亂的市場吧?。?/p>


文檔訪問限制

為讓開發(fā)者有時(shí)間進(jìn)行測試,以下與存儲(chǔ)訪問框架 (SAF) 相關(guān)的變更只有在應(yīng)用以 Android 11 為目標(biāo)平臺(tái)時(shí)才會(huì)生效。

上文存儲(chǔ)的時(shí)候說過可以通過SAF(存儲(chǔ)訪問框架--Storage Access Framework)來訪問公共目錄,但是Android11再次升級(jí),部分目錄和文件不能訪問了,具體如下:

無法再使用 ACTION_OPEN_DOCUMENT_TREE intent 操作請(qǐng)求訪問以下目錄:

  • 內(nèi)部存儲(chǔ)卷的根目錄。
  • 設(shè)備制造商認(rèn)為可靠的各個(gè) SD 卡卷的根目錄,無論該卡是模擬卡還是可移除的卡??煽康木硎侵笐?yīng)用在大多數(shù)情況下可以成功訪問的卷。
  • Download 目錄。

無法再使用 ACTION_OPEN_DOCUMENT_TREE 或 ACTION_OPEN_DOCUMENT intent 操作請(qǐng)求用戶從以下目錄中選擇單獨(dú)的文件:

  • Android/data/ 目錄及其所有子目錄。
  • Android/obb/ 目錄及其所有子目錄。


限制對(duì) APN 數(shù)據(jù)庫的讀取訪問

以 Android 11 為目標(biāo)平臺(tái)的應(yīng)用現(xiàn)在必須具備 Mani 特權(quán),才能讀取或訪問電話提供程序 APN 數(shù)據(jù)庫。如果在不具備此權(quán)限的情況下嘗試訪問 APN 數(shù)據(jù)庫,會(huì)生成安全異常。

問題來了,APN是啥?

  • 指一種網(wǎng)絡(luò)接入技術(shù),是通過手機(jī)上網(wǎng)時(shí)必須配置的一個(gè)參數(shù),APN配置參數(shù)包括名字,運(yùn)營商編號(hào),APN接入點(diǎn)等等。

就是說如果沒有Mani權(quán)限就不能讀取APN數(shù)據(jù)庫了,但是!這個(gè)權(quán)限很早之前就被限定只有系統(tǒng)程序才能申請(qǐng)這個(gè)權(quán)限了,現(xiàn)在這個(gè)特權(quán)沒理解到是什么意思,難道系統(tǒng)程序都不能隨便申請(qǐng)了?有大神可以評(píng)論區(qū)留言告知。


在元數(shù)據(jù)文件中聲明“無障礙”按鈕使用情況

從 Android 11 開始,您的無障礙服務(wù)無法在運(yùn)行時(shí)聲明與系統(tǒng)的“無障礙”按鈕的關(guān)聯(lián)。如果您將 Acce 附加到 AccessibilityServiceInfo 對(duì)象的 flags 屬性,框架就不會(huì)將“無障礙”按鈕回調(diào)事件傳遞給您的服務(wù)。

做過無障礙輔助功能的應(yīng)該都知道AccessibilityServiceInfo要設(shè)置flag為FLAG_REQUEST_ACCESSIBILITY_BUTTON,getAccessibilityButtonController方法獲取輔助功能按鈕控制器,并且可用于查詢輔助功能按鈕的狀態(tài)并注冊(cè)監(jiān)聽器以進(jìn)行交互和輔助功能按鈕的狀態(tài)更改。

但是,Android 11開始,這樣寫不能獲取輔助按鈕回調(diào)事件了,得換成另外一種寫法。在元數(shù)據(jù)文件(通常為 res/raw)中使用 flagRequestAccessibilityButton 標(biāo)記聲明您的無障礙服務(wù)與“無障礙”按鈕的關(guān)聯(lián)。


Firebase JobDispatcher 和 GCMNetworkManager

如果您的應(yīng)用以 API 級(jí)別 30 或更高級(jí)別為目標(biāo)平臺(tái),在搭載 Android 6.0(API 級(jí)別 23)或更高版本的設(shè)備上會(huì)停用 Firebase JobDispatcher 和 GcmNetworkManager API 調(diào)用。

這兩個(gè)api國內(nèi)都用不了,主要用于后臺(tái)任務(wù)。官方給出的替代意見是WorkManager,這個(gè)國內(nèi)是可以用的,屬于jetpack組件,主要用于調(diào)度和執(zhí)行可延期的后臺(tái)工作。


設(shè)備到設(shè)備文件傳輸

如果您的應(yīng)用以 Android 11 為目標(biāo)平臺(tái),您將無法再使用 allowBackup 屬性停用應(yīng)用文件的設(shè)備到設(shè)備遷移。系統(tǒng)會(huì)自動(dòng)啟用此功能。不過,即使您的應(yīng)用以 Android 11 為目標(biāo)平臺(tái),您也可以通過將 allowBackup 屬性設(shè)置為 false 來停用應(yīng)用文件的云端備份和恢復(fù)。

android:allowBackup屬性

  • 代表是否允許應(yīng)用參與備份和恢復(fù)基礎(chǔ)架構(gòu)。如果將此屬性設(shè)為 false,則永遠(yuǎn)不會(huì)為該應(yīng)用執(zhí)行備份或恢復(fù),即使是采用全系統(tǒng)備份方法也不例外(這種備份方法通常會(huì)通過 adb 保存所有應(yīng)用數(shù)據(jù))。此屬性的默認(rèn)值為 true。

所以這里是不能停用文件的設(shè)備到設(shè)備遷移,但是可以停用云端備份和恢復(fù)


自動(dòng)重置權(quán)限

如果應(yīng)用以 Android 11 為目標(biāo)平臺(tái)并且數(shù)月未使用,系統(tǒng)會(huì)通過自動(dòng)重置用戶已授予應(yīng)用的運(yùn)行時(shí)敏感權(quán)限來保護(hù)用戶數(shù)據(jù)。此操作與用戶在系統(tǒng)設(shè)置中查看權(quán)限并將應(yīng)用的訪問權(quán)限級(jí)別更改為拒絕的做法效果一樣。如果應(yīng)用已遵循有關(guān)在運(yùn)行時(shí)請(qǐng)求權(quán)限的最佳做法,那么您不必對(duì)應(yīng)用進(jìn)行任何更改。這是因?yàn)椋?dāng)用戶與應(yīng)用中的功能互動(dòng)時(shí),您應(yīng)該會(huì)驗(yàn)證相關(guān)功能是否具有所需權(quán)限。

官方說明說的很清楚了,而且只要應(yīng)用遵循有關(guān)在運(yùn)行時(shí)請(qǐng)求權(quán)限的最佳做法,也就是每次需要調(diào)用權(quán)限的時(shí)候都會(huì)去判斷,那么就不會(huì)有什么問題。

如果需要關(guān)閉這個(gè)功能怎么辦呢?只有引導(dǎo)用戶去設(shè)置頁面關(guān)閉了,可以調(diào)用包含Se action的 Intent將用戶定向到系統(tǒng)設(shè)置中應(yīng)用的頁面。

怎么檢查應(yīng)用是否停用自動(dòng)重置功能呢?調(diào)用 PackageManager的isAutoRevokeWhitelisted()方法。如果此方法返回 true,代表系統(tǒng)不會(huì)自動(dòng)重置應(yīng)用的權(quán)限。


前臺(tái)服務(wù)類型

從 Android 9 開始,應(yīng)用僅限于在前臺(tái)訪問攝像頭和麥克風(fēng)。為了進(jìn)一步保護(hù)用戶,Android 11 更改了前臺(tái)服務(wù)訪問攝像頭和麥克風(fēng)相關(guān)數(shù)據(jù)的方式。如果您的應(yīng)用以 Android 11 為目標(biāo)平臺(tái)并且在某項(xiàng)前臺(tái)服務(wù)中訪問這些類型的數(shù)據(jù),您需要在該前臺(tái)服務(wù)的聲明的 foregroundServiceType 屬性中添加新的 camera 和 microphone 類型。

舉例,如果應(yīng)用某項(xiàng)前臺(tái)服務(wù)需要訪問位置信息、攝像頭和麥克風(fēng),那么就這樣添加:

<manifest> <service ... android:foregroundServiceType="location|camera|microphone" /> </manifest>


適配Android11手機(jī)

此模塊的修改內(nèi)容針對(duì)所有項(xiàng)目在Android11手機(jī)上存在的改動(dòng),與targetSdkVersion無關(guān)。


數(shù)據(jù)訪問審核 ?

為了讓應(yīng)用及其依賴項(xiàng)訪問用戶私密數(shù)據(jù)的過程更加透明,Android 11 引入了數(shù)據(jù)訪問審核功能。借助此流程得出的見解,您可以更好地識(shí)別和糾正可能出現(xiàn)的意外數(shù)據(jù)訪問。

哪些范疇屬于用戶私密數(shù)據(jù)呢?其實(shí)就是危險(xiǎn)權(quán)限的調(diào)用,所以這個(gè)功能就是提供了可以監(jiān)聽危險(xiǎn)權(quán)限調(diào)用的監(jiān)聽。主要涉及到的方法是A。無論是應(yīng)用本身,還是依賴庫或者SDK中的代碼,只要訪問到私密數(shù)據(jù)(危險(xiǎn)權(quán)限),都會(huì)回調(diào)給我們。

對(duì)于工程龐大或者使用較多SDK的工程比較適合用上這個(gè)功能,讓自己應(yīng)用的私有數(shù)據(jù)管理更加透明規(guī)范,否則對(duì)于私有數(shù)據(jù)的使用和管理并不全面和方便。而且還可以對(duì)權(quán)限使用添加歸因,也就是一個(gè)tag,標(biāo)志權(quán)限用到了什么地方。方便回調(diào)的時(shí)候知曉哪里使用了私有數(shù)據(jù)。

來:

override fun onCreate(savedInstanceState: Bundle?) { (savedInstanceState) setContentView) //創(chuàng)建歸因(attribute) attributionContext = createAttributionContext("shareLocation") //監(jiān)聽事件 val appOpsCallback = object : A() { private fun logPrivateDataAccess( opCode: String, attributionTag: String, trace: String) { Log.i(TAG, "Private data accessed. " + "Operation: $opCode\n " + "Attribution Tag:$attributionTag\nStack Trace:\n$trace") } override fun onNoted(syncNotedAppOp: SyncNotedAppOp) { { logPrivateDataAcce, it, Throwable().()) } } override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) { { logPrivateDataAcce, it, Throwable().()) } } override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) { a { logPrivateDataAcce, it, a) } } } //開啟私密數(shù)據(jù)監(jiān)聽 val appOpsManager = getSystemService(AppOpsManager::class.java) as AppOpsManager a(mainExecutor, appOpsCallback) b { getLocation() } } fun getLocation() { val locationManager = a( LocationManager::class.java) as LocationManager if (!checkPermission()) { return } val location: Location? = loca) if (location != null) { showToast("${loca}") } }

該例子主要展示了一個(gè)獲取位置信息的功能,如果調(diào)用到getLocation方法,就會(huì)觸發(fā)onNoted回調(diào),回調(diào)信息包括危險(xiǎn)權(quán)限code以及歸因。

其中OnOpNotedCallback 一共三個(gè)回調(diào)方法:

  • onNoted 正常情況下都會(huì)回調(diào)到該方法
  • onAsyncNoted 如果數(shù)據(jù)訪問并非發(fā)生在應(yīng)用調(diào)用API期間,就會(huì)調(diào)用onAsyncNoted(),比如一些監(jiān)聽器的回調(diào)。
  • onSelfNoted 在極少數(shù)情況下,如果應(yīng)用將自身的UID傳遞到 noteOp(),需要調(diào)用 onSelfNoted()。

最后點(diǎn)擊按鈕,看下回調(diào)的結(jié)果日志:

Private data accessed. Operation: android:coarse_location Attribution Tag:shareLocation Stack Trace: [Ljava.lang.StackTraceElement;@14f5a16

可以看到權(quán)限代碼:android:coarse_location 以及歸因 shareLocation

單次授權(quán)

在 Android 11 中,每當(dāng)應(yīng)用請(qǐng)求與位置信息、麥克風(fēng)或攝像頭相關(guān)的權(quán)限時(shí),面向用戶的權(quán)限對(duì)話框會(huì)包含僅限這一次選項(xiàng)。如果用戶在對(duì)話框中選擇此選項(xiàng),系統(tǒng)會(huì)向應(yīng)用授予臨時(shí)的單次授權(quán)。

簡單的說,就是在申請(qǐng)與位置信息、麥克風(fēng)或攝像頭相關(guān)的權(quán)限時(shí),系統(tǒng)會(huì)自動(dòng)提供一個(gè)單次授權(quán)的選項(xiàng),只供這一次權(quán)限獲取。然后用戶下次打開app的時(shí)候,系統(tǒng)會(huì)再次提示用戶授予權(quán)限。這個(gè)影響應(yīng)該不大,只要我們每次使用的時(shí)候都去判斷權(quán)限,沒有就去申請(qǐng)即可。放一張新版本權(quán)限獲取樣式:


權(quán)限對(duì)話框的可見性

Android 11 建議不要請(qǐng)求用戶已選擇拒絕的權(quán)限。在應(yīng)用安裝到設(shè)備上后,如果用戶在使用過程中屢次針對(duì)某項(xiàng)特定的權(quán)限點(diǎn)按拒絕,此操作表示其希望“不再詢問”。

這個(gè)都算不上改動(dòng),只是官方的一個(gè)良好建議。建議在用戶多次拒絕之后,不要再展示權(quán)限申請(qǐng)。


Scudo Hardened Allocator

Android 11 在內(nèi)部使用 Scudo Hardened Allocator 為堆分配提供服務(wù)。Scudo 能夠檢測并減輕某些類型的內(nèi)存安全違規(guī)行為。如果您在原生代碼崩潰報(bào)告中發(fā)現(xiàn)與 Scudo 相關(guān)的崩潰(例如 Scudo ERROR:),請(qǐng)參閱 Scudo 問題排查文檔。

Scudo是一種動(dòng)態(tài)的用戶模式內(nèi)存分配器,旨在抵御與堆相關(guān)的漏洞,同時(shí)保持良好的性能。它是一個(gè)開源的項(xiàng)目。 Android 11中,將采用這個(gè)新的heap分配器,性能更好,更安全。


文件描述符排錯(cuò)程序

Android 10 引入了 fdsan(文件描述符排錯(cuò)程序)。fdsan 檢測錯(cuò)誤處理文件描述符所有權(quán)的錯(cuò)誤,例如 use-after-close 和 double-close。在 Android 11 中,fdsan 的默認(rèn)模式發(fā)生了變化。現(xiàn)在,fdsan 會(huì)在檢測到錯(cuò)誤時(shí)中止,而以前的行為則是記錄警告并繼續(xù)。

問題來了,fdsan是啥?先要了解fd是啥

文件描述符(FileDescriptor) 是Unix/Linux系統(tǒng)文件操作的相關(guān)概念,它在形式上是一個(gè)非負(fù)整數(shù)。當(dāng)程序打開一個(gè)現(xiàn)有文件或者創(chuàng)建一個(gè)新文件時(shí),內(nèi)核向進(jìn)程返回一個(gè)文件描述符。 系統(tǒng)的進(jìn)程也就是使用了這個(gè)fd來標(biāo)示打開的文件,有了它就能對(duì)文件做各種操作,獲得文件的各種相關(guān)信息了。

所以fdsan也就是檢測文件處理中發(fā)生的一些錯(cuò)誤。


應(yīng)用使用情況統(tǒng)計(jì)信息

為了更好地保護(hù)用戶,Android 11 將每個(gè)用戶的應(yīng)用使用情況統(tǒng)計(jì)信息存儲(chǔ)在憑據(jù)加密存儲(chǔ)空間中。

這就涉及到了UsageStatsManager,UsageStatsManager是Android提供統(tǒng)計(jì)應(yīng)用使用情況的服務(wù)。通過這個(gè)服務(wù)可以獲取指定時(shí)間區(qū)間內(nèi)應(yīng)用使用統(tǒng)計(jì)數(shù)據(jù)、組件狀態(tài)變化事件統(tǒng)計(jì)數(shù)據(jù)以及硬件配置信息統(tǒng)計(jì)數(shù)據(jù)。

比如queryAndAggregateUsageStats方法,可以獲取指定時(shí)間區(qū)間內(nèi)使用統(tǒng)計(jì)數(shù)據(jù),以應(yīng)用包名為鍵值進(jìn)行數(shù)據(jù)合并。

但是在Android 11 設(shè)備中,不好意思,不能隨意使用這些信息了。只有當(dāng)isUserUnlocked()方法返回true的時(shí)候,才能正常訪問這些數(shù)據(jù)。也就是以下兩種情況:

  • 用戶在系統(tǒng)啟動(dòng)后首次解鎖其設(shè)備
  • 用戶在設(shè)備上切換到自己的帳號(hào)


JobScheduler API 調(diào)用限制調(diào)試

JobScheduler任務(wù)調(diào)度器,可以在設(shè)備空閑時(shí)做一些任務(wù)處理。Android11中如果你設(shè)置為debug模式(debuggable 清單屬性設(shè)置為 true),超出速率限制的JobScheduler API調(diào)用將返回 RESULT_FAILURE。這個(gè)有什么用呢?應(yīng)該可以幫助我們發(fā)現(xiàn)一些性能問題,感興趣的可以自己試試。

順便提下,Jetpack組件WorkManager也是用到了JobScheduler,不熟悉的同學(xué)可以去了解下,JobScheduler是由SystemServer進(jìn)程啟動(dòng)的一個(gè)系統(tǒng)服務(wù),所以才可以有這么大的權(quán)限。


無障礙操作

在以前的 Android 版本中,框架會(huì)向未正確處理基于點(diǎn)擊的無障礙操作的微件分派觸摸事件。通常,這些視圖會(huì)直接處理觸摸事件,而不是注冊(cè)點(diǎn)擊監(jiān)聽器。 為了在正確定義無障礙操作的應(yīng)用中創(chuàng)建更一致的行為,Android 11 絕不會(huì)分派觸摸事件。相反,系統(tǒng)會(huì)完全依賴于基于點(diǎn)擊的無障礙操作:ACTION_CLICK 和 ACTION_LONG_CLICK。此更改會(huì)影響屏幕閱讀器的行為。

在Android手機(jī)上有個(gè)預(yù)安裝的屏幕閱讀服務(wù),叫做TalkBack,為視力障礙人士或者視力狀態(tài)不佳的老年人提供。那我們應(yīng)用為了讓這個(gè)閱讀器能夠讀懂你的自定義view操作,必須給與自定義控件定義處理程序,包括點(diǎn)擊,長按等操作。原來版本可能對(duì)于OnTouchListener也支持無障礙觸摸事件,而在Android11中,必須專門制定點(diǎn)擊或者長按事件才行了。給個(gè):

class TriSwitch(context: Context) : Switch(context) { // 0, 1, or 2. var currentState: Int = 0 private set init { updateAccessibilityActions() } private fun updateAccessibilityActions() { ViewCom(this, ACTION_CLICK, action-label) { view, args -> moveToNextState() }) } private fun moveToNextState() { currentState = (currentState + 1) % 3 } }

一個(gè)自定義控件TriSwitch,繼承自Switch,由于和Switch的點(diǎn)擊效果不一樣,所以必須通過替換 ViewCom() 來重新定義相應(yīng)的無障礙操作。


非SDK接口限制

Android 11 包含更新后的受限制非 SDK 接口列表(基于與 Android 開發(fā)者之間的協(xié)作以及最新的內(nèi)部測試)。在限制使用非 SDK 接口之前,我們會(huì)盡可能確保提供公開替代方案。

老樣子,Android11也會(huì)限制一些接口,包括灰名單和白名單,具體看非SDK接口列表

總結(jié)

一路分析下來也可以看到,如果是重要的改動(dòng),特別是涉及到崩潰的改動(dòng)還是放到了targetSdkVersion=30的內(nèi)容中,這也是每次Android發(fā)版的一個(gè)潛規(guī)則吧,為了最大程度不影響已上線的app所作出的舉動(dòng)。 但是,這并不意味我們就可以不改。因?yàn)閼?yīng)用可拖不起,用戶可拖不起,畢竟升級(jí)才能給到用戶最好的體驗(yàn)。而且各大應(yīng)用市場也都會(huì)建議或者強(qiáng)制應(yīng)用升級(jí)targetSdkVersion,以便適配最新的手機(jī)。

所以,行動(dòng)吧。


附件

官網(wǎng)改動(dòng)介紹


這里我分享一份大佬收錄整理的Android學(xué)習(xí)PDF+架構(gòu)視頻+面試文檔+源碼筆記,高級(jí)架構(gòu)技術(shù)進(jìn)階腦圖、Android開發(fā)面試專題資料,高級(jí)進(jìn)階架構(gòu)資料

這些都是我現(xiàn)在閑暇還會(huì)反復(fù)翻閱的精品資料。里面對(duì)近幾年的大廠面試高頻知識(shí)點(diǎn)都有詳細(xì)的講解。相信可以有效的幫助大家掌握知識(shí)、理解原理。

當(dāng)然你也可以拿去查漏補(bǔ)缺,提升自身的競爭力。

如果你有需要的話,可以【評(píng)論或私信】獲取

如果你覺得自己學(xué)習(xí)效率低,缺乏正確的指導(dǎo),可以加入資源豐富,學(xué)習(xí)氛圍濃厚的技術(shù)圈一起學(xué)習(xí)交流吧!


喜歡本文的話,不妨順手給我點(diǎn)個(gè)贊、評(píng)論區(qū)留言或者轉(zhuǎn)發(fā)支持一下唄~



1.《[11平臺(tái)如何切換賬號(hào)]成長守護(hù)平臺(tái)切換賬號(hào)!》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識(shí),僅代表作者本人觀點(diǎn),與本網(wǎng)站無關(guān),侵刪請(qǐng)聯(lián)系頁腳下方聯(lián)系方式。

2.《[11平臺(tái)如何切換賬號(hào)]成長守護(hù)平臺(tái)切換賬號(hào)!》僅供讀者參考,本網(wǎng)站未對(duì)該內(nèi)容進(jìn)行證實(shí),對(duì)其原創(chuàng)性、真實(shí)性、完整性、及時(shí)性不作任何保證。

3.文章轉(zhuǎn)載時(shí)請(qǐng)保留本站內(nèi)容來源地址,http://f99ss.com/keji/3224648.html