Android R(Android 11 API 30)于2020年9月9日正式釋出,随國内各終端廠商在售Android裝置的版本更新更新,應用軟體對Android R 版本的相容适配已迫在眉睫。
對于Android R的新特性,這裡按照以下幾個方面進行了歸納:`分區存儲、權限、隐私、性能、安全`。
對于Android R的新特性,這裡按照以下幾個方面進行了歸納:<code>分區存儲、權限、隐私、性能、安全</code>。
官方文檔描述:https://developer.android.google.cn/about/versions/11
從Android 10(API 29)開始,Android<code>預設開啟分區存儲</code>功能,不過Android 10 可通過增加<code>android:requestLegacyExternalStorage="true"</code>配置<code>停用分區存儲</code>;
從Android 11(API 30)開始,<code>強制執行分區存儲</code>,對于Android 11及以上裝置,<code>android:requestLegacyExternalStorage="true"</code>配置将不再有效。
Android 11 分區存儲官方描述:
https://developer.android.google.cn/training/data-storage#scoped-storage
Android 10 預設開啟分區存儲:
https://xiaxl.blog.csdn.net/article/details/103125117
開啟分區存儲後,應用預設情況下隻能通路<code>應用專屬目錄(内部存儲、外部存儲應用專屬目錄)</code>,以及<code>本應用所建立的特定類型的媒體檔案</code>。
應用專屬目錄
包括<code>内部存儲</code>、<code>外部存儲專屬目錄</code>(若應用包名com.xiaxl.demo):
<code>/data/data/com.xiaxl.demo/files,</code>
<code>/sdcard/Android/data/com.xiaxl.demo/files</code>
分别采用以下API進行通路:
<code>File appFile = new File(context.getFilesDir(), filename);</code>
<code>File appExternalFile = new File(context.getExternalFilesDir(), filename);</code>
共享存儲目錄
包括媒體、文檔和其他檔案。例如DCIM、Pictures、Movies、Download等目錄;
注:
<code>Android 10(Android Q)中共享存儲目錄使用MediaStore API通路;</code>
<code>Android 11(Android R)中共享存儲目錄支援MediaStore API與File API通路。</code>
為保證應用在Android 10、Android 11裝置中,使用<code>File API對共享存儲目錄具有相同的檔案通路權限</code>。建議在應用 AndroidManifest配置檔案中,增加<code>requestLegacyExternalStorage="true"</code>辨別,以<code>關閉Android 10裝置上的分區存儲功能</code>,使<code>分區存儲隻對Android 11以上裝置生效</code>:
應用專屬目錄(<code>内部存儲</code>、<code>外部存儲專屬目錄</code>)的讀寫,Android 4.4以上裝置不需要任何權限;
共享存儲路徑的讀寫,需要<code>READ_EXTERNAL_STORAGE</code>與<code>WRITE_EXTERNAL_STORAGE</code> 權限;

Android 11以上裝置中,如果您的應用再次請求<code>READ_EXTERNAL_STORAGE</code>權限時,動态權限申請彈窗将變化為<code>“您的應用正在請求通路照片和媒體”</code>。
檔案媒體通路 官方描述:
如果需要與其他應用共享單個檔案或應用資料,可以使用API:
<code>FileProvider</code>(分享自己的一個或多個檔案)
如果應用需要将自己的一個或多個檔案提供給其他應用,安全的做法是向接收方應用發送檔案的内容 URI,并授予對該 URI 的臨時通路權限。
Android <code>FileProvider</code> 元件提供了 <code>getUriForFile()</code> 方法,用于生成檔案的内容 <code>URI</code>。
<code>ContentProvider</code>(擷取替他應用提供的資料)
如果您需要向其他應用提供資料,可以使用<code>ContentProvider</code>。
<code>ContentProvider</code>是一種标準接口,可将一個程序中的資料與另一個程序中運作的代碼進行連。
Android 11 共享檔案官方描述:
有一些應用需要擷取所有檔案的通路權限,例如:檔案管理器軟體。
擷取所有檔案的通路權限,可申請<code>MANAGE_EXTERNAL_STORAGE</code>權限。
<code>MANAGE_EXTERNAL_STORAGE</code>相關官方描述:
https://developer.android.google.cn/training/data-storage/manage-all-files
Android 11 中對權限進行了如下更改:
新增 <code>READ_PHONE_NUMBERS</code>權限,擷取手機号碼;
<code>背景通路位置</code>權限調整;
使用者<code>多次針對某項特定的權限請求</code>點<code>拒絕</code>,表示使用者希望<code>不再詢問</code>;
應用<code>長時間未使用</code>,系統會<code>自動重置使用者已授予敏感權限</code>;
針對<code>位置、麥克風、攝像頭</code>授權彈窗新增<code>僅限這一次</code>授權按鈕;
<code>SYSTEM_ALERT_WINDOW</code> 權限授權方式改變為系統自動授權;
參考 Android 11 權限更新官方文檔:
https://developer.android.google.cn/about/versions/11/privacy/permissions#one-time
當應用的 <code>targetSdkVersion>=30</code> 時,使用以下API<code>擷取手機号碼</code>時,需要申請<code>READ_PHONE_NUMBERS</code>權限,而不再是<code>READ_PHONE_STATE</code> 權限。
<code>TelephonyManager</code> 類和 <code>TelecomManager</code> 類中的 <code>getLine1Number()</code> 方法。
<code>TelephonyManager</code> 類中不受支援的 <code>getMsisdn()</code> 方法。
在Android 10及之前的裝置,可以繼續使用<code>READ_PHONE_STATE</code>擷取手機号;
對Android11及以上裝置,需擷取<code>READ_PHONE_NUMBERS</code>權限,才能擷取手機号;
對于<code>READ_PHONE_STATE</code>權限
Android 10 開始<code>普通應用</code>已經不能再<code>讀取裝置的硬體ID</code>資訊;
相關資訊參考 https://xiaxl.blog.csdn.net/article/details/103125117;
Android 11 開始<code>擷取手機号</code>相關API更換為<code>READ_PHONE_NUMBERS</code>權限;
<code>READ_PHONE_NUMBERS</code>權限官方API描述:
https://developer.android.google.cn/reference/android/Manifest.permission#READ_PHONE_NUMBERS
在Android10裝置上,同時<code>申請前台、背景位置權限</code>時,并在使用者選擇<code>始終允許</code>後,才能獲得背景位置權限。
在Android11裝置上,對于<code>targetSdkVersion<=29(Android 10)</code>的應用,同時<code>申請前台、背景位置權限</code>時,對話框不再提示始終允許字樣,而是提供了位置權限的設定入口,需要<code>使用者在設定頁面選擇始終允許</code>才能獲得背景位置權限。
在Android11裝置上,對于<code>targetSdkVersion=30(Android 11)</code>的應用,同時<code>申請前台、背景位置權限</code>時,系統會忽略該請求,無任何響應(<code>需首先擷取前台位置權限,再次申請背景位置權限</code>)。
在Android11裝置上,對于<code>targetSdkVersion=30(Android 11)</code>的應用,<code>先申請前台位置權限,後申請背景位置權限</code>。
背景通路位置權限 官方描述:
https://developer.android.google.cn/training/location/background
Android11裝置上,targetSdkVersion=30的應用,申請背景位置權限,直接跳轉到設定頁面。
在 Android 11 中,使用者<code>多次針對某項特定的權限請求</code>點選了<code>拒絕</code>,那麼應用再次請求該項權限時,使用者将不會看到系統權限彈窗,該操作表示使用者希望<code>不再詢問</code>;
在 Android 11 中,當targetSdkVersion>=30時,<code>應用在一段時間内未使用</code>,系統會通過<code>自動重置使用者已授予應用的運作時敏感權限</code>來保護使用者資料;
從 Android 11(API 級别 30)開始,當應用請求與<code>位置、麥克風、攝像頭</code>相關權限時,面向使用者的授權對話框會包含<code>僅限這一次</code>選項;如果使用者在對話框中選擇<code>僅限這一次</code>,系統會向應用授予臨時的單次授權。
權限申請API使用方式不變:
源碼參考:
https://github.com/android/permissions-samples/tree/main/RuntimePermissionsBasic;
在 Android 11 中,<code>SYSTEM_ALERT_WINDOW</code> 權限授權方式更改為:<code>根據請求自動向某些應用授予 SYSTEM_ALERT_WINDOW 權限</code>。
系統會自動向具有 <code>ROLE_CALL_SCREENING</code> 且請求 <code>SYSTEM_ALERT_WINDOW</code> 的所有應用授予該權限。如果應用失去 <code>ROLE_CALL_SCREENING</code>,就會失去該權限。
<code>ROLE_CALL_SCREENING</code>為<code>RoleManager</code>中的常量類,多用于通知使用者将我們的應用替換掉手機自帶的預搭載應用(短信、電話撥号);
系統會自動向通過 <code>MediaProjection</code> 截取螢幕且請求 <code>SYSTEM_ALERT_WINDOW</code> 的所有應用授予該權限,除非使用者已明确拒絕向應用授予該權限。當應用停止截取螢幕時,就會失去該權限。此用例主要用于遊戲直播應用。
SYSTEM_ALERT_WINDOW權限 官方描述:
https://developer.android.google.cn/about/versions/11/privacy/permissions#system-alert
主要更改涉及以下幾個方面:
軟體包可見性:擷取其他應用資訊需在<code>AndroidManifest</code>中增加<code><queries></code>标簽;
前台服務:通路位置資訊、攝像頭、麥克風限制;
永久 SIM 卡辨別符 ICCID 擷取受限;
新增<code>AppOpsManager.OnOpNotedCallback</code>監聽危險權限的調用,進而保護使用者的私密資料;
這樣對于第三方依賴庫的權限使用申請可以做一個監控
在 Android 11 及更高版本裝置中,當應用的 <code>targetSdkVersion>=30</code> 時,如果應用希望擷取其他應用的資訊(比如:包名、軟體名稱),原有方式将無法擷取到。
如需擷取其他應用資訊,需要在<code>AndroidManifest</code>中增加<code><queries></code>元素标簽,告知系統希望擷取哪些應用的資訊或者哪一類應用的資訊。
如果需要擷取所有應用的資訊(比如:Launcher應用、裝置管理器應用):這種情況隻需要在<code>AndroidManifest</code>中添加<code>QUERY_ALL_PACKAGES</code>權限即可。
<code>QUERY_ALL_PACKAGES</code>權限為普通權限,不需要進行動态申請。但送出應用市場後,應用市場可能會進行稽核
軟體包可見性 官方描述:
https://developer.android.google.cn/about/versions/11/privacy/package-visibility
當應用的 <code>targetSdkVersion>=30</code> 時,<code>前台服務</code>通路<code>位置資訊、攝像頭、麥克風</code>時,需添加<code>foregroundServiceType</code>。
前台服務 官方描述:
https://developer.android.google.cn/about/versions/11/privacy/foreground-services
在 Android 11 及更高版本中,使用 <code>SubscriptionInfo.getIccId()</code> 方法通路不可重置的 ICCID 受到限制。
<code>SubscriptionInfo.getIccId()</code> 方法會傳回一個<code>非null的空字元串</code>。
如需唯一辨別裝置上安裝的 SIM 卡,請改用 <code>getSubscriptionId()</code> 方法。<code>SubscriptionId</code>會提供一個索引值,用于唯一識别已安裝的 SIM 卡(包括實體 SIM 卡和電子 SIM 卡),除非裝置恢複出廠設定,否則此辨別符的值對于給定 SIM 卡是保持不變的。
Android 11新增<code>AppOpsManager.OnOpNotedCallback</code>為開發者提供<code>對應用危險權限的使用監聽,進而保護使用者的私密資料</code>。
當應用以及應用的依賴包中,申請某項危險權限時,<code>AppOpsManager.OnOpNotedCallback</code>的對應回調方法将會被調用,進而<code>列印申請的權限</code>與<code>對應的API調用棧</code>。
舉例:
<code>使用位置權限擷取位置資訊</code>時,将會回調<code>AppOpsManager.OnOpNotedCallback</code>中的<code>onNoted</code>方法,并列印<code>使用的權限</code>與<code>對應的API調用棧</code>。
列印日志如下:
從以上日志可以看出,當應用申請<code>ACCESS_COARSE_LOCATION</code>權限并<code>擷取位置資訊時</code>,列印了應用<code>申請的權限</code>與<code>對應的API調用棧</code>。
AppOpsManager 相關官方描述:
https://developer.android.google.cn/guide/topics/data/audit-access#audit-by-attribution-tag
JobScheduler使用頻率進行限制
Android 11 為對<code>JobScheduler</code>使用頻率進行一定限制。
對于 debuggable 清單屬性設定為 true 的應用,過多的調用 <code>JobScheduler</code> API 将傳回 <code>RESULT_FAILURE</code>。
<code>JobScheduler</code>主要用于在未來某個時間下滿足一定條件時觸發執行某項任務,例如:<code>當裝置在空閑狀态, 并且使用wifi時, 自動下載下傳Apk</code>。
<code>JobScheduler</code>典型的使用舉例如下:
官方描述參考:
https://developer.android.google.cn/about/versions/11/behavior-changes-all
官方Demo參考:
https://github.com/googlearchive/android-JobScheduler
非 SDK 接口限制
官方從 Android 9(API 級别 28)開始,對應用使用的非 SDK 接口實施了限制。
如果你的APP通過引用<code>非 SDK 接口</code>或嘗試<code>使用反射或 JNI 來擷取句柄</code>,這些限制就會起作用。官方給出的解釋是為了<code>提升使用者體驗、降低應用崩潰風險</code>。
官方給出了一個檢測工具,下載下傳位址:veridex
veridex使用方法:
以上截圖中,blacklist、greylist、greylist-max-o、greylist-max-p含義如下:
blacklist 黑名單:禁止使用的非SDK接口,運作時直接Crash(是以必須解決)
greylist 灰名單:即目前版本仍能使用的非SDK接口,但在下一版本中可能變成被限制的非SDK接口
greylist-max-o: 在targetSDK<=O中能使用,但是在targetSDK>=P中被禁止使用的非SDK接口
greylist-max-p: 在targetSDK<=P中能使用,但是在targetSDK>=Q中被禁止使用的非SDK接口
非SDK接口限制 官方描述:
https://developer.android.google.cn/about/versions/11/non-sdk-11