Android 13 適配指南來啦!
大家好,我是皇叔,最近開了一個(gè)安卓進(jìn)階漲薪訓(xùn)練營,可以幫助大家突破技術(shù)&職場(chǎng)瓶頸,從而度過難關(guān),進(jìn)入心儀的公司。
詳情見這篇文章:繼Android進(jìn)階三部曲之后,我的最強(qiáng)作來啦!
?「是的,你沒看錯(cuò),現(xiàn)在就要帶你適配 Android13」。
?
2022 的Google I/O 發(fā)布了 Android 13 beta 2 和 Android 13 Beta 1 國內(nèi)廠商的設(shè)備支持列表,雖然按照慣例, Android 13 應(yīng)該是年末才發(fā)布正式版,但是相信有的開發(fā)者已經(jīng)收到了平臺(tái)的 Android13 的適配要求,所以本篇也是結(jié)合 Oppo 的 Android 13 應(yīng)用兼容性適配指導(dǎo)?和官方提供的一些文檔內(nèi)容做一個(gè)整理測(cè)試。

目前 Android 13 主要的兼容問題還是在于隱私權(quán)限上,所以本次的適配指南相關(guān)內(nèi)容也是著重在這一部分,?「這里涉及面比較廣的應(yīng)該就是相冊(cè)和通知權(quán)限」。
相冊(cè)權(quán)限
這個(gè)動(dòng)圖大家可能看到過,「這是 Android 13 上提供的系統(tǒng)圖片選擇器,通過?Intent(MediaStore.ACTION_PICK_IMAGES);?就可以打開,支持視頻、音頻、圖片分類,支持多選和單選」?,另外官方也表示過,這個(gè)特性不僅僅會(huì)在 Android 13 中出現(xiàn),谷歌還會(huì)將其放置到 Play 商店中,向 Android 11 和 Android 12 設(shè)備推送。

我們通過調(diào)整 TargetSDK 設(shè)置為?PreView,然后運(yùn)行到 Tiramisu 的模擬器上進(jìn)行測(cè)試,主要測(cè)試 TargetSDK 在低于 "Tiramisu" 和等于 "Tiramisu" 時(shí)的不同情況。

如下圖所示:
圖 1 是?「TargetSDK 低于 13 時(shí)運(yùn)行在 Andorid 13 模擬器上的情況,此時(shí)可以正常訪問到本地圖庫相關(guān)信息」;
圖 2 是使用了 "Tiramisu" 下,「通過官方提供的?
Intent(MediaStore.ACTION_PICK_IMAGES);?打開的系統(tǒng)相冊(cè)選擇組件」,也沒問題圖 3 和 圖 4 是使用了 "Tiramisu",使用以前代碼運(yùn)行后的相冊(cè)讀取情況,沒做處理,可以看到此時(shí)讀取不到相關(guān)信息;
圖 5 是?「通過申請(qǐng)新的
android.permission.READ_MEDIA_IMAGES?權(quán)限,就可以用以前的代碼繼續(xù)訪問到以前的相冊(cè)信息」,因?yàn)閷?duì)于目標(biāo)版本為 Android 13 的情況,現(xiàn)在?READ_EXTERNAL_STORAGE權(quán)限被細(xì)化了,開發(fā)者需要使用READ_MEDIA_IMAGE、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO?來替代適配;圖 6 在申請(qǐng)完權(quán)限之后,就可以正常讀取相冊(cè)等信息;

總結(jié):?「所以如果是 TargetSDK 在 Android 13 以下,不需要處理,如果在 Android 13 以及以上 ,需要增加申請(qǐng)權(quán)限」。
????<uses-permission?android:name="android.permission.READ_MEDIA_IMAGES"?/>
????<uses-permission?android:name="android.permission.READ_MEDIA_AUDIO"?/>
????<uses-permission?android:name="android.permission.READ_MEDIA_VIDEO"?/>
通知
在 Android R 上設(shè)置里開始支持在設(shè)置里對(duì)應(yīng)用的通知權(quán)限進(jìn)行管理,但是應(yīng)用自身是無法修改應(yīng)用級(jí)別的通知權(quán)限,所以 App 無法知道自身有沒有發(fā)送通知的權(quán)限
「所以在 Android 13 里增加了通知的運(yùn)行時(shí)權(quán)限」,其中 Android 13 (33) 的通知會(huì)根據(jù)正在運(yùn)行的應(yīng)用程序的目標(biāo) API 級(jí)別進(jìn)行不同的處理,「不過不管應(yīng)用程序的目標(biāo)API級(jí)別如何,Android 13 都會(huì)提示用戶授予應(yīng)用程序發(fā)送通知的權(quán)限」。
「例如下圖,是 targetSdk 30 運(yùn)行在 Android 13 模擬器上,依然會(huì)彈出讓用戶是否允許推送」。

選擇“允許”,應(yīng)用就可以通過任何渠道發(fā)送通知,并發(fā)布與前臺(tái)服務(wù)相關(guān)的通知; 選擇不允許選項(xiàng),應(yīng)用將無法發(fā)送通知,除了幾個(gè)特定規(guī)則之外,所有通知渠道都會(huì)被屏蔽,這類似于用戶在系統(tǒng)設(shè)置中手動(dòng)關(guān)閉應(yīng)用的所有通知后發(fā)生的行為。 如果用戶不選擇,比如劃開對(duì)話框,或者直接返回,則應(yīng)用只能在系統(tǒng)有臨時(shí)授權(quán)的情況下發(fā)送通知(應(yīng)用必須已經(jīng)具有通知渠道,并且用戶未在搭載 12L 或更低版本的設(shè)備上明確停用應(yīng)用的通知)
當(dāng)然,系統(tǒng)也會(huì)根據(jù)應(yīng)用程序的目標(biāo) API 級(jí)別處理通知訪問:
對(duì)新安裝的應(yīng)用,程序的目標(biāo)API不同表現(xiàn)為: 以 Android 13 為 TargetSDK(33) ,應(yīng)用程序需要在 Manifest 中聲明?android.permission.POST_NOTIFICATION?權(quán)限,「此權(quán)限的級(jí)別為“dangerous”」,因此 App 需要向用戶顯示運(yùn)行時(shí)提示才能被授予權(quán)限(也就是代碼里調(diào)用權(quán)限申請(qǐng),在設(shè)置里打開不行),未被授予權(quán)限的App 的通知將被系統(tǒng)自動(dòng)刪除; 如果應(yīng)用以 12L(API 級(jí)別 32)或更低版本為目標(biāo)平臺(tái);當(dāng)應(yīng)用程序創(chuàng)建其第一個(gè)通知渠道時(shí),系統(tǒng)將顯示權(quán)限對(duì)話框;
如果是現(xiàn)有應(yīng)用更新,程序的目標(biāo) API 級(jí)別為:
以 Android 13 (33)為目標(biāo)平臺(tái),系統(tǒng)臨時(shí)授予應(yīng)用發(fā)送通知的權(quán)限,直到應(yīng)用中的 activity 首次啟動(dòng)(也就是更新之后第一次啟動(dòng)應(yīng)用),這要求應(yīng)用必須有一個(gè)現(xiàn)有的通知通道,且其通知不得被用戶明確禁用; 如果應(yīng)用以 12L (32)或更低版本為目標(biāo)平臺(tái),系統(tǒng)臨時(shí)授予應(yīng)用發(fā)送通知的權(quán)限,直到用戶在權(quán)限對(duì)話框中明確選擇一個(gè)選項(xiàng);
最后測(cè)試和總結(jié)一下:
「如果是 TargetSDK 在 Android 12L (32) 以下,只要用戶同意才能發(fā)送通知,一般是在應(yīng)用啟動(dòng)的時(shí)候,比如用戶點(diǎn)擊了不允許,就無法發(fā)出通知,需要等到下次 App 再啟動(dòng),才會(huì)再次詢問,或者去設(shè)置通知中心打開」;
「如果是 TargetSDK 在 Android 13 (33) 以上,就一定需要手動(dòng)添加?
android.permission.POST_NOTIFICATIONS?和代碼調(diào)用申請(qǐng),不然可能設(shè)置中心都無法打開;」
image-20220520174116399
附近的WIFI設(shè)備權(quán)限
由于 Android 之前可以通過跟蹤附近的 Wi-Fi AP 和藍(lán)牙設(shè)備來推斷設(shè)備的位置,所以這次谷歌決定禁止應(yīng)用程序訪問藍(lán)牙或Wi-Fi掃描結(jié)果,除非這類應(yīng)用需要聲明??ACCESS_FINE_LOCATION?權(quán)限。
在 Android 13 中,Google 將 Wi-Fi 掃描與位置相關(guān)內(nèi)容分離, Android 13 為管理設(shè)備與周圍 Wi-Fi 熱點(diǎn)連接的應(yīng)用添加? NEARBY_WIFI_DEVICES 運(yùn)行時(shí)權(quán)限 (屬于 NEARBY_DEVICES權(quán)限組),從而在不需要 ACCESS_FINE_LOCATION 權(quán)限的情況下,也可以讓應(yīng)用訪問附近的 Wi-Fi 設(shè)備。
此前,對(duì)于僅需要連接 Wi-Fi 設(shè)備,但實(shí)際上并不需要了解設(shè)備位置的應(yīng)用來說,以 Android 13 (33)為目標(biāo)平臺(tái)的應(yīng)用現(xiàn)在可以通過 “neverForLocation” 屬性來完善申請(qǐng) NEARBY_WIFI_DEVICES 權(quán)限。
?只要你的應(yīng)用不會(huì)通過 Wi-Fi API 來推導(dǎo)物理位置,那么當(dāng)你以 Android 13 或更高版本為目標(biāo)平臺(tái)并使用 Wi-Fi API 時(shí),就可以請(qǐng)求?
?NEARBY_WIFI_DEVICES?而不是?ACCESS_FINE_LOCATION。
這項(xiàng)新權(quán)限會(huì)影響幾個(gè)不同的 Wi-Fi 用例,包括以下用例:
查找或連接到附近的設(shè)備,如打印機(jī)或媒體投射設(shè)備,類似場(chǎng)景可以使用以下方式: 通過帶外方式(例如通過 BLE)接收 AP 信息; 使用僅限本地使用的熱點(diǎn),通過 Wi-Fi 感知和連接功能發(fā)現(xiàn)并連接到設(shè)備; 通過 Wi-Fi 直連發(fā)現(xiàn)和連接到設(shè)備; 發(fā)起與已知 SSID(例如汽車或智能家居設(shè)備)的連接。 開啟僅限本地使用的熱點(diǎn)。 連接到附近的 Wi-Fi 感知設(shè)備。
所以開發(fā)需要區(qū)分不同api對(duì)應(yīng)的權(quán)限;
需要新權(quán)限(NEARBY_WIFI_DEVICES)的 API:
WifiManager:startLocalOnlyHotspot() WifiAwareManager:attach() WifiAwareSession:publish()、subscribe() WifiP2pManager:addLocalService()、connect()、createGroup()、discoverPeers()、discoverServices()、requestDeviceInfo()、requestGroupInfo()、requestPeers() WifiRttManager:startRanging() 仍需要位置信息權(quán)限(ACCESS_FINE_LOCATION )的API:
WifiManager:getScanResults()、startScan()
由于 NEARBY_WIFI_DEVICES 權(quán)限僅適用于 Android 13 或更高版本, 如果是 Android12L(32) 以及以下的 App 應(yīng)保留對(duì) ACCESS_FINE_LOCATION 的所有聲明:
<manifest?...>
????<uses-permission?android:name="android.permission.ACCESS_FINE_LOCATION"
?????????????????????android:maxSdkVersion="32"?/>
????<application?...>
????????...
????application>
manifest>
以 Android 13(33) 為目標(biāo)平臺(tái)時(shí),如果應(yīng)用不會(huì)通過 Wi-Fi API 推導(dǎo)物理位置,請(qǐng)?jiān)谇鍐挝募袑?usesPermissionFlags 屬性設(shè)為 neverForLocation。
<manifest?...>
????<uses-permission?android:name="android.permission.NEARBY_WIFI_DEVICES"
?????????????????????android:usesPermissionFlags="neverForLocation"?/>
????<application?...>
????????...
????application>
manifest>
所以總結(jié):「以 Android 13(33) 為目標(biāo)平臺(tái)的應(yīng)用程序,訪問附近的 WI-FI 設(shè)備。除特例API需要申請(qǐng)ACCESS_FINE_LOCATION外,其他需要申請(qǐng) android.permission.NEARBY_WIFI_DEVICES 運(yùn)行時(shí)權(quán)限」;
在后臺(tái)使用身體傳感器需要新的權(quán)限
Android 13 中引入了 “在使用時(shí)” 訪問身體傳感器(例如心率、體溫和血氧飽和度)的概念,此訪問模式與 Android 10(API 級(jí)別 29)系統(tǒng)為位置信息引入的模式非常相似。
「如果你的 App 以 Android 13(33) 為目標(biāo)平臺(tái),并且在后臺(tái)運(yùn)行時(shí)需要訪問身體傳感器信息,那么除了現(xiàn)有的?BODY_SENSORS?權(quán)限外,還必須聲明新的?BODY_SENSORS_BACKGROUND?權(quán)限」。
?「注意」:這是受到“硬性限制”的權(quán)限,除非設(shè)備的安裝程序針對(duì)你的應(yīng)用將該權(quán)限列入了許可名單,否則你的應(yīng)用將無法獲得此權(quán)限,如需了解詳情,請(qǐng)參閱有關(guān)受限權(quán)限的指南。
?
Intent 過濾器會(huì)屏蔽不匹配的 intent
當(dāng) App 以 Android 13(33) 或更高版本為 Target 的其他應(yīng)用的導(dǎo)出組件發(fā)送 intent 時(shí),僅當(dāng)該 intent 與接收應(yīng)用中的??元素匹配時(shí),系統(tǒng)才會(huì)傳送該 intent,換言之系統(tǒng)會(huì)屏蔽所有不匹配的 intent,但以下情況除外:
發(fā)送給其他應(yīng)用的未聲明任何 intent 過濾器的組件的 intent; 發(fā)送給你應(yīng)用中的其他組件的 intent; 由系統(tǒng)發(fā)送的 intent; 由具有根級(jí)特權(quán)的用戶發(fā)送的 intent;
更安全地導(dǎo)出上下文注冊(cè)的接收器
為了幫助提高運(yùn)行時(shí)接收器的安全性,Android 13 允許你指定 App 中的特定廣播接收器是否應(yīng)被導(dǎo)出以及是否對(duì)設(shè)備上的其他應(yīng)用可見,此變更是 Android 12 更安全的組件?的延續(xù);
以 Android 13(33) 或更高版本為目標(biāo)平臺(tái)的應(yīng)用,必須為每個(gè)廣播接收器指定?RECEIVER_EXPORTED或?RECEIVER_NOT_EXPORTED,否則當(dāng) App 嘗試注冊(cè)廣播接收器時(shí),系統(tǒng)會(huì)拋出?SecurityException
//?This?broadcast?receiver?should?be?able?to?receive?broadcasts?from?other?apps.
//?This?option?causes?the?same?behavior?as?setting?the?broadcast?receiver's
//?"exported"?attribute?to?true?in?your?app's?manifest.
context.registerReceiver(sharedBroadcastReceiver,?intentFilter,
????RECEIVER_EXPORTED);
//?For?app?safety?reasons,?this?private?broadcast?receiver?should?**NOT**
//?be?able?to?receive?broadcasts?from?other?apps.
context.registerReceiver(privateBroadcastReceiver,?intentFilter,
????RECEIVER_NOT_EXPORTED);
應(yīng)用自撤銷權(quán)限
在 Android 13中,谷歌添加了一個(gè)新的API,允許開發(fā)者降級(jí)權(quán)限。
應(yīng)用程序可以觸發(fā)撤銷授予調(diào)用 API 的包的一個(gè)或多個(gè)運(yùn)行時(shí)權(quán)限,不需要訪問特定運(yùn)行時(shí)權(quán)限控制 API 的應(yīng)用程序可以自行撤銷這些權(quán)限,這樣用戶就可以確保這些應(yīng)用程序不會(huì)在不知情的情況下使用這些API。
如需撤消特定運(yùn)行時(shí)權(quán)限,請(qǐng)將該權(quán)限的名稱傳入 revokeOwnPermissionOnKill()?方法,如需同時(shí)撤消一組運(yùn)行時(shí)權(quán)限,請(qǐng)將這組權(quán)限的名稱傳入 revokeOwnPermissionsOnKill()。
?撤消是異步發(fā)生的,會(huì)終止與應(yīng)用的 UID 相關(guān)聯(lián)的所有進(jìn)程。
?
系統(tǒng)只有在安全的情況下才會(huì)觸發(fā)撤消操作,也就是當(dāng)有應(yīng)用組件仍在前臺(tái)運(yùn)行,或者有另一個(gè)應(yīng)用正在訪問你應(yīng)用的組件(如 content provider)時(shí)不會(huì)發(fā)生撤消。
?如果你想立即撤消權(quán)限,可以調(diào)用 exit()。但是進(jìn)行 exit() 調(diào)用可能會(huì)導(dǎo)致當(dāng)前正在訪問 App 的其他應(yīng)用出現(xiàn)崩潰。
?
剪貼板擦除
Android 之前一直提供了一個(gè)剪貼板服務(wù),所有 App 都可以使用它來放置和檢索文本。
盡管從技術(shù)上講,任何應(yīng)用都可以清除全局剪貼板中的主內(nèi)容(只要它們是前臺(tái)應(yīng)用或 Android 10+ 上的默認(rèn)輸入法),但 Android 本身不會(huì)自動(dòng)清除剪貼板。
這意味著任何留在全局剪貼板中的剪貼板內(nèi)容,都可以在以后被應(yīng)用程序讀取,盡管 Android 的剪貼板訪問有 toast 消息可能會(huì)提醒用戶。
Android 13 增加了剪貼板自動(dòng)清除功能,此功能在默認(rèn)情況下處于禁用狀態(tài),在經(jīng)過設(shè)定的時(shí)間后,將自動(dòng)從全局剪貼板中清除主剪輯, 默認(rèn)情況下經(jīng)過3600000毫秒(60分鐘)后,剪貼板將被清除。
每次執(zhí)行復(fù)制/讀取(寫入剪貼板?setPrimaryClip,讀?getPrimaryClip?)時(shí),會(huì)重置一個(gè)消息 timeout(60min),之后會(huì)自動(dòng)清除剪貼板內(nèi)存中的內(nèi)容,即60min內(nèi),如果一直沒有寫入剪貼板的操作,剪貼板的內(nèi)容會(huì)被自動(dòng)清除。
前臺(tái)服務(wù) (FGS) 任務(wù)管理器
Android 13 的新前臺(tái)服務(wù)( Foreground Services:FGS)任務(wù)管理器顯示當(dāng)前運(yùn)行前臺(tái)服務(wù)的應(yīng)用程序列表,此列表稱為活動(dòng)應(yīng)用程序,可以通過下拉通知抽屜并點(diǎn)擊啟示來訪問,這時(shí)候每個(gè)應(yīng)用程序旁邊都會(huì)有一個(gè)“停止”按鈕。
?注意:當(dāng)用戶點(diǎn)擊應(yīng)用旁邊的停止按鈕時(shí),系統(tǒng)會(huì)停止整個(gè)應(yīng)用,而不僅僅是正在運(yùn)行的前臺(tái)服務(wù)。
?

?注意:如果系統(tǒng)檢測(cè)到你的應(yīng)用長(zhǎng)時(shí)間運(yùn)行某項(xiàng)前臺(tái)服務(wù)(在 24 小時(shí)的時(shí)間段內(nèi)至少運(yùn)行 20 小時(shí)),便會(huì)發(fā)送通知邀請(qǐng)用戶與 FGS 任務(wù)管理器進(jìn)行互動(dòng),詳見:https://developer.android.google.cn/about/versions/13/changes/fgs-manager#system-prompt-long-running-fgs
?
使用 JobScheduler 改進(jìn)預(yù)提取處理
利用 JobScheduler,應(yīng)用可使用 JobInfo.Builder.setPrefetch() 將特定作業(yè)標(biāo)記為“預(yù)提取”,這意味著理想情況下這些作業(yè)應(yīng)該在應(yīng)用下一次啟動(dòng)前提前一點(diǎn)運(yùn)行,以提升用戶體驗(yàn)。
過去,JobScheduler 僅使用該信號(hào)讓預(yù)提取作業(yè)有機(jī)會(huì)使用免費(fèi)或多余的數(shù)據(jù),在 Android 13 中系統(tǒng)現(xiàn)在會(huì)嘗試確定應(yīng)用下次啟動(dòng)的時(shí)間,并根據(jù)該估算值運(yùn)行預(yù)提取作業(yè),應(yīng)用應(yīng)嘗試使用“預(yù)提取”來完成他們想要在下次應(yīng)用啟動(dòng)前完成的任何工作。
電池資源利用率
Android 13 中引入了?電池資源利用率?功能,以便為系統(tǒng)提供多種方法來更好地管理設(shè)備電池續(xù)航時(shí)間:
更新了有關(guān)系統(tǒng)何時(shí)將你的應(yīng)用放入“受限”應(yīng)用待機(jī)模式存儲(chǔ)分區(qū)的規(guī)則。 對(duì)于應(yīng)用在以下情況下可以執(zhí)行的操作制定了新限制:用戶因你應(yīng)用的后臺(tái)電池用量過高而將其置于“受限”狀態(tài)。 新增了系統(tǒng)通知,用于就電池用量過高和長(zhǎng)時(shí)間運(yùn)行的前臺(tái)服務(wù)向用戶發(fā)出警告。
總結(jié)
TargetSDK 33 才會(huì)需要處理的變動(dòng):
相冊(cè)權(quán)限 附近的WIFI設(shè)備權(quán)限 在后臺(tái)使用身體傳感器需要新的權(quán)限 intent 過濾器會(huì)屏蔽不匹配的 intent 更安全地導(dǎo)出上下文注冊(cè)的接收器 應(yīng)用自撤銷權(quán)限 剪貼板擦除
影響所有應(yīng)用的變動(dòng):
通知權(quán)限 前臺(tái)服務(wù) (FGS) 任務(wù)管理器 使用 JobScheduler 改進(jìn)預(yù)提取處理 電池資源利用率

? 耗時(shí)2年,Android進(jìn)階三部曲第三部《Android進(jìn)階指北》出版!
? 『BATcoder』做了多年安卓還沒編譯過源碼?一個(gè)視頻帶你玩轉(zhuǎn)!
? 重生!進(jìn)階三部曲第一部《Android進(jìn)階之光》第2版 出版!
