Android 14 應用適配指南:https://dev.mi.com/distribute/doc/details?pId=1718
1.獲取Android 14
1.1 谷歌發(fā)布時間表
https://developer.android.com/about/versions/14/overview#timeline
1.2 小米手機升級Android 14
現(xiàn)在Xiaomi 13、Xiaomi 13 Pro、Xiaomi Pad 6 可通過鏈接,線刷基于Android? 14 Beta 1的MIUI 14開發(fā)者預覽版。
https://web.vip.miui.com/page/info/mio/mio/detail?postId=40403123&fromPathname=mioSingleBoard&app_version=dev.230112
如您暫未擁有上述設備也沒關系,我們?yōu)槟峁┝俗懔康脑茰y設備,來支持廣大開發(fā)者的適配工作,云測平臺詳見:https://testit.miui.com/android_14 (云測平臺使用權限申請:https://m.beehive.miui.com/Ooq42P35TPAIP-sZJT1EqA)
1.3 Google原生機升級Android 14
開發(fā)者持有Pixel系列的機器可以直接ota升級,或者下載鏡像升級,具體見鏈接:
https://developer.android.google.cn/about/versions/14/download
1.4 Android模擬器
在Android Studio中,可按照如下方式安裝Android 14 SDK:
- 依次點擊Tools>SDK Manager。
- 在“SDK Plateforms”標簽頁中,選擇Android 14。
- 在“SDK Tools”標簽頁中,選擇Android SDK Build-Tools 34。
點擊OK,安裝SDK。
如需訪問 Android 14 API 并測試您的應用與 Android 14 的兼容性,請打開模塊級build.gradle或build.gradle.kts文件,并使用 Android 14所對應的值對它們進行更新:如何設置這些值的格式取決于您所使用的 Android Gradle 插件 (AGP) 版本。
注意:如果您尚未準備好完全支持 Android 14,您仍然可以使用可調試的應用、Android 14 設備和兼容性框架來執(zhí)行應用兼容性測試,而無需更改應用以使其與預覽版 SDK 兼容或以此為目標平臺。
2.新功能和API
2.1 應用級語言偏好
Android 14 擴展了 Android 13(API level 33)中引入的App級語言功能:
- 自動生成App的 localeConfig:從 Android Studio Giraffe Canary 7 和 AGP 8.1.0-alpha07 開始,開發(fā)者可以配置應用以自動支持每個應用的語言偏好。根據項目資源,Android Gradle 插件生成 LocaleConfig 文件并在最終AndroidManifest.xml中添加對它的引用,因此不再需要手動創(chuàng)建或更新該文件。 AGP 使用App模塊的 res 文件夾中的資源和庫模塊依賴項來確定要包含在 LocaleConfig 文件中的區(qū)域設置。
- 應用的 localeConfig 的動態(tài)更新:使用 LocaleManager 中的 setOverrideLocaleConfig() 和 getOverrideLocaleConfig() 在設備的系統(tǒng)設置中動態(tài)更新App的支持語言列表。使用這個特性來自定義每個區(qū)域支持的語言列表、做 A/B 測試,或者如果App利用服務器端推送進行本地化,則提供更新的語言環(huán)境列表。
我們在此展示一個動態(tài)修改系統(tǒng)設置中可選語言列表的例子:
我們名為multilingual settings的demo App中res/xml/locales_config.xml內容如下:
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en"/>
<locale android:name="en-GB"/>
<locale android:name="fr"/>
<locale android:name="ja"/>
<locale android:name="zh-Hans-MO"/>
<locale android:name="zh-Hant-MO"/>
</locale-config>復制
然后在manifest.xml的application下加入:
android:localeConfig="@xml/locales_config"
復制
打開設置 > 系統(tǒng) > 語言和輸入法 > 應用語言 > multilingual settings,可以看到我們在locals_config.xml中編寫的可選語言列表:
若我們在App內通過按鈕調用U上新增的setOverrideLocaleConfig方法:
MainActivity#overrideLocalConfig:
public void overrideLocalConfig(View view) {
this.getSystemService(LocaleManager.class).setOverrideLocaleConfig(new LocaleConfig(new LocaleList(Locale.ENGLISH, Locale.GERMAN, Locale.KOREAN, Locale.ITALIAN)));
}復制
按下按鈕后,我們回到設置 > 系統(tǒng) > 語言和輸入法 > 應用語言 > multilingual settings,發(fā)現(xiàn):
系統(tǒng)設置中的可選語言列表變成了我們在setOverrideLocaleConfig中的參數所代表的語言。
而在T上,我們沒有overrideLocalConfig API,系統(tǒng)設置中的App的可選語言列表只能由res/xml/locales_config.xml指定,一旦App編譯完成便無法更改。overrideLocalConfig給了我們動態(tài)更改的能力。
3.輸入法 (IME) 的App語言可見性:IME 可以利用 getApplicationLocales() 方法檢查當前App的語言并將 IME 語言與該語言相匹配。
2.2 語法性別變化API
30 億人使用帶有性別的語言:語法類別(例如名詞、動詞、形容詞和介詞)根據交談或談論的人和對象的性別而變化的語言。傳統(tǒng)上,許多性別語言使用陽性語法性別作為默認或通用性別。
以錯誤的語法性別稱呼用戶,例如以男性語法性別稱呼女性,會對他們的表現(xiàn)和態(tài)度產生負面影響。相比之下,具有正確反映用戶語法性別的語言的 UI 可以提高用戶參與度并提供更加個性化和自然的用戶體驗。
為了幫助開發(fā)者為性別語言構建以用戶為中心的 UI,Android 14 引入了語法性別變形 API,從而無需重構應用即可添加對語法性別的支持。
本特性的調用較為簡單,首先在Android Studio中,在res文件夾下新建命名如values-fr-feminine的文件夾,在里面新建strings.xml,并僅將涉及語法性別的文本包含進去。本例中,不涉及語法性別的法語文本應被放進res/values-fr/strings.xml中。官方文檔可見為帶有語法性別的語言添加翻譯。
然后,我們即可為應用設定語法性別:
this.getSystemService(GrammaticalInflectionManager.class).setRequestedApplicationGrammaticalGender(Configuration.GRAMMATICAL_GENDER_FEMININE);復制
上面的代碼將應用的語法性別設為陰性,即假定用戶為女性。
要獲取App的語法性別int值,可以:
int gender = this.getSystemService(GrammaticalInflectionManager.class).getApplicationGrammaticalGender();復制
Android 14上共有4種與語法性別相關的int值,分別為:
Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED, Configuration.GRAMMATICAL_GENDER_NEUTRAL, Configuration.GRAMMATICAL_GENDER_FEMININE, Configuration.GRAMMATICAL_GENDER_MASCULINE復制
2.3 地區(qū)偏好
區(qū)域偏好使用戶能夠個性化溫度單位、一周的第一天和編號系統(tǒng)。居住在美國的歐洲人可能更喜歡以攝氏度而不是華氏度為單位的溫度單位,并且希望應用將星期一視為一周的開始,而不是美國默認的星期日。
Android 14為用戶提供了一個集中的位置來更改這些應用偏好。這些偏好也會在備份和恢復過程中持續(xù)存在。多個 API 和intent(例如 getTemperatureUnit 和 getFirstDayOfWeek)授予應用讀取用戶偏好的權限,因此應用可以調整其顯示信息的方式。開發(fā)者還可以在 ACTION_LOCALE_CHANGED 上注冊 BroadcastReceiver 以在區(qū)域偏好更改時收到廣播。
要找到這些設置,請打開“Settings”并導航至System > Languages & input > Regional preferences。
2.4 分享表自定義行為和改善直接共享目標的排名
Android 14更新了系統(tǒng)的分項表,可支持自定義app行為,并且為用戶提供更多有用的預覽信息。
2.4.1 添加自定義的行為
在Android 14,app可以在系統(tǒng)分享表中自定義行為。在分享表中,可借助ChooserAction.Builder來構建自定義ChooserAction,指定ChooserActions的列表作為使用Intent.createChooser創(chuàng)建的Intent的Intent.EXTRA_CHOOSER_CCUSTOM_ACTIONS。
以下是創(chuàng)建自定義行為的一般過程
以發(fā)送多張圖片為例
//創(chuàng)建Intent
Intent shareIntent1 =new Intent();
shareIntent1.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent1.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
shareIntent1.setType("image/*");//各種類型的圖像
Intent shareIntent = Intent.createChooser(shareIntent1,null);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//添加自定義行為的intent
PendingIntent customAction = PendingIntent.getBroadcast(
mContext,
1,
new Intent(CHOOSER_CUSTOM_ACTION_BROADCAST_ACTION),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent fakeCustomAction = PendingIntent.getBroadcast(
mContext,
1,
new Intent("some_action"),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent fakeCustomAction2 = PendingIntent.getActivity(
mContext,
1,
new Intent(CHOOSER_CUSTOM_ACTION_BROADCAST_ACTION),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
//創(chuàng)建action
ChooserAction[] actions = new ChooserAction[] {
createChooserAction("act1", fakeCustomAction2),
createChooserAction("act2", fakeCustomAction),
createChooserAction("act3", fakeCustomAction),
createChooserAction("act4", fakeCustomAction),
createChooserAction("act5", customAction),
};
PendingIntent modifyShare = PendingIntent.getBroadcast(
mContext,
1,
new Intent(CHOOSER_CUSTOM_ACTION_BROADCAST_ACTION),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
//創(chuàng)建修改分享媒體資源的action
ChooserAction modifyShareAction = new ChooserAction.Builder(
Icon.createWithResource(
mContext.getPackageName(),
R.drawable.img),
modifyShareLabel,
modifyShare
).build();
//將自定義行為存到intent里
shareIntent.putExtra(Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION, modifyShareAction);
mContext.startActivity(shareIntent);復制
2.4.2 改善直接共享目標的排名
Android 14中為了給用戶提供更有用的結果,其使用更多來自app的標簽來決定直接分享對象的排名。為了提供更有用的標簽,當用戶向聯(lián)系人發(fā)送消息時,通過使用相應的快捷方式調用pushDynamicShortcut來報告快捷方式的使用情況。并通過調用ShortcutInfoCompat.Builder#addCapabilityBinding(“actions.intent.SEND_MESSAGE”)將相應的功能“actions.intent.SEND.MESSAGE”附加到該快捷方式。
以下方法可有助于提高直接共享目標的排名
- 確保所有 shortcutId 獨一無二,且不在不同的目標上重復使用。
- 通過調用 setLongLived(true) 確??旖莘绞介L期存在。
- 直接提供排名,借助setRank()來設置
- 對于社交應用
- 在快捷方式中為相關的Person對象提供set鍵
- 快捷方式連接到來自同一個人或多個人的相關通知
- 在提供的Person對象中,提供設備上的關聯(lián)聯(lián)系人的有效URI
2.5 支持應用內部動畫和自定義動畫
Android 13在開發(fā)者選項中引入了預測性返回動畫。當在開發(fā)者選項中開啟受支持的應用使用時,向后滑動會顯示一個動畫,指示返回手勢退出應用程序并返回主屏幕。
Android 14中對預測性返回進行了多項改進并添加多項新的功能:
可以通過設置android:enableOnBackInvokedCallback=true為每個Activity而不是整個應用設置預測性返回系統(tǒng)動畫。
添加了新的系統(tǒng)動畫,以配合Android 13的返回主屏幕動畫。新的系統(tǒng)動畫是跨活動和跨任務的,在遷移到預測性返回后退出系統(tǒng)動畫后自動獲得。
為Bottom sheets、Side sheets和Search添加了新的動畫。
為創(chuàng)建自定義動畫和過渡提供了設計指南。
添加了很多新的API支持自定義應用內部過渡動畫:
在OnBackPressedCallback添加了handleOnBackStarted、handleOnBackProgressed和handleOnBackCancelled。
在OnBackAnimationCallback添加了onBackStarted、onBackProgressed和onBackCancelled。
在用戶滑動返回時的響應,請使用overrideActivityTransition而不是overridePendingTransition。
在新發(fā)布的Android 14預覽版中,預測性返回所有的功能仍然保留在開發(fā)者選項中。請參閱開發(fā)者者指南進行支持應用的預測性返回手勢,同樣借助開發(fā)者指南進行創(chuàng)建自定義應用內部過渡動畫。
2.6 對應用商店的改善
Android 14引入了幾個新的PackageInstaller API,以改善應用商店的用戶體驗。
2.6.1 在下載前請求用戶同意
一般情況下,安裝或更新應用需要得到用戶的同意。當安裝器利用REQUEST_INSTALL_PACKAGES權限嘗試去安裝一個新的應用,在Android 14以前的版本中,應用可以在APKs寫入到session,且已提交session的后面向用戶申請權限。
從Android 14開始,借助requestUserPreapproval()方法可以讓安裝器在提交session前申請用戶的同意。這一改善讓應用商店征得用戶安裝同意后開始下載APKs。并且一旦用戶同意安裝后,應用商店就可在后臺進行下載和安裝,這期間不會干擾用戶的正常使用,有利于改善用戶體驗。
2.6.2 負責未來更新
安裝者借助新的方法setRequestUpdateOwnership()向系統(tǒng)表明它要對未來它安裝的應用的更新負責。此功能強制執(zhí)行了更新所有權,意味著只有更新所有者才能安裝應用程序的自動更新。強制認定更新所有者有助于確保用戶僅從預期的應用商店接收更新。
任何其他的安裝者,包括使用INSTALL_PACKAGES權限的,都必須明確收到用戶的同意才能安裝更新。如果用戶決定從其他地方更新應用,擁有更新所有權的安裝者將失去更新所有權。
2.6.3 盡量以較少的中斷時間來更新應用
應用商店應該避免去更新用戶正在使用的應用,這是因為更新操作可能會殺死正在使用的應用,會導致中斷用戶正在做的事情。
從Android 14開始,IntallConstraints API提供給用戶一個方式確保應用在一個合適的時間進行更新。例如,應用商店可通過調用commitSessionAfterInstallConstraintsAreMet()方法來確保提交更新時用戶不再與應用交互。
2.6.4 無縫安裝拆分的APK
通過拆分,可將一整個APK按功能拆分成幾個分散的APK文件。拆分APKs可讓應用商店優(yōu)化應用不同組件的發(fā)布。例如,應用商店可能基于目標硬件的特性來進行優(yōu)化。自API 22,PackageInstaller API開始支持拆分APK操作。
在Android 14中,安裝者借助setDontKillApp()方法聲明當安裝新的被拆分APK的部分組成時,不應該殺死正在運行應用的進程。當用戶正在使用應用時,應用商店可以借助這個功能來無縫安裝應用新的部分內容。
2.7 監(jiān)測用戶截屏行為
為了創(chuàng)建標準化的屏幕截圖檢測體驗,Android 14引入了一個隱私保護的屏幕截屏檢測API。這個API允許應用程序在每一個活動的基礎上進行注冊回調。當Activity在可見的情況下進行截屏時,Activity會調用這些回調,并通知用戶一些信息。
API使用流程
- 聲明"android.permission.DETECT_SCREEN_CAPTURE"權限
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />復制
- 實現(xiàn)一個回調接口,重寫onScreenCapture()函數。
final Activity.ScreenCaptureCallback screenCaptureCallback =
new Activity.ScreenCaptureCallback() {
@Override
public void onScreenCaptured() {
// Add logic to take action in your app.
}
};復制
- 注冊:在Activity的onStart()方法中注冊screenCaptureCallback()
@Override
protected void onStart() {
super.onStart();
// Pass in the callback created in the previous step
// and the intended callback executor (e.g. Activity's mainExecutor).
registerScreenCaptureCallback(executor, screenCaptureCallback);
}復制
- 取消注冊:在activity的onStop()方法取消注冊screenCaptureCallback
@Override
protected void onStop() {
super.onStop();
unregisterScreenCaptureCallback(screenCaptureCallback);
}復制
注意:截屏方式觸發(fā)回調函數執(zhí)行的條件:
- 生效:只有當用戶執(zhí)行特定的按鍵組合才會觸發(fā),比如電源鍵+音量鍵下鍵
- 不生效:通過adb或者捕獲設備當前屏幕內容的儀器測試中拍攝的屏幕快照,不會調用回調函數。
2.8 路徑是可查詢和可插值的
Android的Path API是一種強大而靈活的機制,用于創(chuàng)建和渲染矢量圖形,能夠繪制或填充路徑,并且通過構建線段或者二次曲線或者三次曲線路徑,然后執(zhí)行相應的運算來獲得更復雜的形狀。其中路徑對象的內部信息對調用方是不透明的。
創(chuàng)建一個路徑,可通過方法moveTo()、lineTo()和cubicTo()等方法添加路徑片段。但是對整個路內部的每一段內容無法訪問,因此你必須要在創(chuàng)建路徑開始的時候保存相關信息。
從Android 14開始,你可以查詢每段路徑的內容。路徑內容都保存在PathIterator對象里面,該對象可通過Path.getPathIterator方法獲得。
2.8.1 查詢路徑片段內容
Path path = new Path();
path.moveTo(1.0F, 1.0F);
path.lineTo(2.0F, 2.0F);
path.close();
PathIterator pathIterator = path.getPathIterator();復制
通過以上代碼可獲取PathIterator的對象,該對象包含了路徑片段的信息。通過PathIterator的迭代器可訪問PathIterator的對象的路徑信息。
while (pathIterator.hasNext()) {
PathIterator.Segment segment = pathIterator.next();
Log.i(LOG_TAG, "segment: " + segment.getVerb() + ", " + segment.getPoints());
}復制
2.8.2 路徑插值
通過查詢路徑可用來對路徑進行插值。即通過兩條內部結構相同的路徑,通過interpolate()插值的方法來生成一條新的路徑。通過兩條構建兩條路徑path和path2,借助插值方法interpolate()得到resultPath,resultPath是由path和path2線性插值得到,插值因子是0.8f。三條路徑如下圖所示。
Path path = new Path();
path.lineTo(300, 1200);
Path path2 = new Path();
path2.moveTo(300f,600f);
path2.lineTo(600, 1200);
Path resultPath = new Path();
if(path.isInterpolatable(path2)){
path.interpolate( path2, 0.8f, resultPath);
}復制
注意:兩條路徑能夠進行插值,需要兩條路徑的內部結構相同。即路徑中點的數量相同;對應路徑片段構建的方法相同,比如都是用lineTo()方法構建;以及曲線權重相同(目前使用權重的方法是conicTo(float x1, float y1, float x2, float y2, float weight))。
2.9 OpenJDK 17 更新
- 更新了大約 300 個 java.base 類以支持 Java 17。
- Text Blocks,將多行字符串文字引入Java。
- instanceof的模式匹配,它允許將對象視為具有instanceof中的特定類型,而無需任何其他變量。
- 密封類,允許限制哪些類和接口可以擴展或實現(xiàn)它們。
3.影響應用的行為變更
3.1行為變更:所有應用
3.1.1 設置精確鬧鐘權限默認關閉
精確鬧鐘適用于需要在精確時間發(fā)生的用戶意圖的通知或操作。
SCHEDULE_EXACT_ALARM 是 Android 12 中引入的允許應用安排準確鬧鐘的權限,該權限不再預先授予大多數targetSDK>= Android 14的新安裝App(默認設置為拒絕)。如果用戶通過備份還原操作將應用數據傳輸到運行Android 14的設備上,權限仍然會被拒絕。如果現(xiàn)有的應用已經擁有此權限,它將在設備升級到 Android 14 時預先授予。
需要 SCHEDULE_EXACT_ALARM 權限才能通過以下 API 啟動準確的警報,否則將拋出 SecurityException:
- setExact()
- setExactAndAllowWhileIdle()
- setAlarmClock()
SCHEDULE_EXACT_ALARM 權限的現(xiàn)有最佳實踐仍然適用,包括以下內容:
- 在安排確切的警報之前,使用 canScheduleExactAlarms() 檢查權限。
- 將應用設置為偵聽前臺廣播 AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED 并對其做出正確反應,系統(tǒng)會在用戶授予權限時發(fā)送該廣播。
在U上,App即使在AndroidManifest.xml里申明了
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />復制
在安裝后系統(tǒng)也不會自動授予android.permission.SCHEDULE_EXACT_ALARM權限。若App沒有該權限,在調用AlarmManager#setExact,AlarmManager#setAlarmClock和AlarmManager#setExactAndAllowWhileIdle三個設置精確鬧鐘的方法時,系統(tǒng)會拋出 SecurityException 。
若App想要獲取該權限,需要用戶到應用信息中或Settings->Apps->Special app access->Alarms & reminders中手動打開:
3.1.1.1 受影響的應用
如果設備運行的是 Android 14 或更高版本,此更改將影響新安裝的具有以下特征的應用:
- targetSDK >= Android 13 。
- 在AndroidManifest.xml中聲明 SCHEDULE_EXACT_ALARM 權限。
- 不屬于豁免或授權前情形。
- 不是日歷或鬧鐘應用。
3.1.1.2 日歷和鬧鐘應用應該申明USE_EXACT_ALARM權限
日歷或鬧鐘應用需要在應用不再運行時發(fā)送日歷提醒、喚醒警報或警報。這些應用可以請求 USE_EXACT_ALARM 普通權限。 USE_EXACT_ALARM 權限將在安裝時授予,擁有此權限的應用程序將能夠像具有 SCHEDULE_EXACT_ALARM 權限的應用程序一樣安排確切的警報。小米應用商店會加強對此權限的管控。
3.1.1.3 可能不需要精確鬧鐘的使用場景
由于 SCHEDULE_EXACT_ALARM 權限現(xiàn)在默認被拒絕,并且權限授予過程需要用戶執(zhí)行額外的步驟,因此強烈建議開發(fā)人員評估他們的用例并確定確切的警報是否仍然對他們的用例有意義。
以下列表顯示了可能不需要確切警報的常見工作流程:
- 在應用的生命周期內安排重復工作
如果任務需要牢記實時約束,例如明天下午 2:00 或 30 分鐘后出發(fā),則 set() 方法很有用。否則,建議改用 postAtTime() 或 postDelayed() 方法。
- 預定的后臺工作,例如更新您的應用程序和上傳日志
WorkManager 提供了一種方法來安排對時間敏感的周期性工作。您可以提供重復間隔和 flexInterval(至少 15 分鐘)來定義工作的細粒度運行時間。
- 當系統(tǒng)處于空閑狀態(tài)時,需要在大約某個時間發(fā)出警報
使用不準確的警報。具體來說,調用 setAndAllowWhileIdle()。
- 應在特定時間后發(fā)生的用戶指定操作
使用不準確的警報。具體來說,調用 set()。
- 可以在一個時間窗口內發(fā)生的用戶指定的操作
使用不準確的警報。具體來說,調用 setWindow()。請注意,允許的最小窗口長度為 10 分鐘。
3.1.1.4 繼續(xù)使用精確鬧鐘的遷移步驟
應用程序必須在安排確切的警報之前檢查它們是否具有權限。如果應用程序沒有權限,則它們必須通過調用意圖向用戶請求權限。
這與請求特殊權限的標準工作流程相同:
- 應用程序應調用 AlarmManager.canScheduleExactAlarms() 以確認它具有適當的權限。
- 如果應用程序沒有權限,調用包含 ACTION_REQUEST_SCHEDULE_EXACT_ALARM 以及應用程序包名稱的意圖,以請求用戶授予權限。在您應用的 onResume() 方法中檢查用戶的決定。
- 偵聽在用戶授予權限時發(fā)送的 AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED 廣播。
- 如果用戶授予您的應用程序權限,您的應用程序可以設置確切的警報。相反,如果用戶拒絕了該權限,請優(yōu)雅地降低您的應用程序體驗,以便它在沒有受該權限保護的信息的情況下向用戶提供功能。
最佳實踐:使用alarmManager.canScheduleExactAlarms()首先檢查有無SCHEDULE_EXACT_ALARM權限,若無,則使用startActivity向用戶申請權限:
boolean canScheduleExactAlarms = mAlarmManager.canScheduleExactAlarms();
if(!canScheduleExactAlarms) {
Intent permissionIntent = new Intent();
permissionIntent.setAction("android.settings.REQUEST_SCHEDULE_EXACT_ALARM");
startActivity(permissionIntent);
}
復制
3.1.1.5 在拒絕權限時適當地降級
一些用戶會拒絕授予權限。在這種情況下,我們建議應用程序優(yōu)雅地降低體驗,并仍然通過識別其用例來努力提供最佳的回退用戶體驗。
3.1.1.6 例外
以下類型的應用始終允許調用 setExact() 或 setExactAndAllowWhileIdle() 方法:
- 使用平臺證書簽名的應用程序。
- 特權應用程序。
- 電源許可名單上的應用程序(如果您的應用程序符合要求,您可以使用 ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS intent 操作請求)。
3.1.1.7 預授權
SYSTEM_WELLBEING 的角色持有者將被預先授予 SCHEDULE_EXACT_ALARM。
3.1.1.8 測試指導
要測試此更改,請從系統(tǒng)設置中的特殊應用訪問頁面(設置 > 應用 > 特殊應用訪問 > 鬧鐘和提醒)禁用您的應用的鬧鐘和提醒權限,并觀察您的應用的行為。
3.1.2 當應用處于cached狀態(tài)時,動態(tài)注冊的廣播會入隊
Android 14中,當應用處于緩存狀態(tài)時,系統(tǒng)可以將上下文注冊的廣播放入到隊列中。這類似于Android 12(API級別31)為異步綁定器事務引入的排隊行為。在Manifest.xml文件中聲明的廣播不會排隊,并且應用程序會從緩存狀態(tài)中刪除以發(fā)送廣播。
當應用離開cached狀態(tài)時,例如回到前臺時,此時系統(tǒng)會分發(fā)排隊的廣播。并且某些廣播的多個實例會被合成一個。依據其他條件,例如系統(tǒng)運行狀況,應用會從緩存狀態(tài)中刪除,并將之前排隊的廣播進行分發(fā)。
3.1.3 三方App只能殺死自己的后臺進程
從 Android 14 開始,當App調用 killBackgroundProcesses() 時,該 API 只能殺死自己的后臺進程。
如果傳入其他App的包名,該方法對該App的后臺進程沒有影響,Logcat中會出現(xiàn)如下信息:
Invalid packageName: com.example.anotherapp復制
App不應使用 killBackgroundProcesses() API 或以其他方式嘗試影響其他App的進程生命周期,即使是在較舊的Android版本上。Android 旨在將緩存的App保留在后臺,并在系統(tǒng)需要內存時自動終止它們。如果應用不必要地殺死其他應用,它會降低系統(tǒng)性能并增加電池消耗,因為稍后需要完全重啟這些應用,這比恢復現(xiàn)有的緩存應用占用的資源要多得多。
3.1.4 安裝應用要求的最小targetSdk API級別
從Android 14開始,targetSdkVersion低于23的應用無法安裝。要求應用滿足這些最低目標API級別的要求可以提高用戶的安全性和隱私性。
惡意軟件通常針對較舊級別的API,繞過較新Android版本中引入的安全和隱私保護。例如,一些惡意應用使用targetSdkVersion 22,以避免受到Android 6.0 Marshmallow(API級別23)于2015年引入的運行時權限模型的影響。Android 14這一變化使惡意軟件很難避免安全和隱私方面的改進。較低API級別的應用將會安裝失敗,并且在日志中顯示以下消息:
INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 23, but found 7復制
在升級到Android 14的設備上,任何targetSdkVersion低于23的應用都會保持安裝狀態(tài)。
如果需要測試針對舊API級別的應用程序,請使用以下adb命令:
adb install --bypass-low-target-sdk-block FILENAME.apk復制
3.1.5 隱藏媒體所在應用包名
媒體存儲支持查詢OWNER_PACKAGE_NAME列,該列表示存儲媒體文件的應用包名。從Android 14開始要滿足以下兩個條件之一,才可以查詢到該列的值。
- 存儲媒體文件的應用程序的程序包名稱對其他應用程序可見
如果想要某程序包名讀其他應用可見,那么其他應用必須在Manifest.xml文件中添加<queries>,package的包名是想要申請對方app的包名。
例如,testmediastore想要查詢testmediastore2的信息,那么Mainfest.xml文件中<queries>的包名就是testmediastore2的包名。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.mimi.testmediastore">
//.../
<queries>
<package android:name="com.mimi.testmediastore2"/>
</queries>
//../
</manifest>復制
- 查詢媒體存儲的應用申請QUERY_ALL_PACKAGE權限。
3.1.6 常駐通知的變化
如果應用向用戶顯示常駐通知,Android 14 允許用戶關閉此類通知。
此更改適用于通過 Notification.Builder#setOngoing(true) 或 NotificationCompat.Builder#setOngoing(true) 設置 Notification.FLAG_ONGOING_EVENT 來阻止用戶手動刪除前臺通知的應用。 FLAG_ONGOING_EVENT 的行為已更改為使此類通知實際上可由用戶手動刪除。
在以下情況下,此類通知仍然不可關閉:
- 當手機被鎖定時
- 如果用戶選擇清除所有通知操作(這有助于防止意外解雇)
- 此外,此新行為不適用于以下用例中的不可關閉通知:
- 使用 CallStyle 綁定到真實呼叫的通知
- 使用 MediaStyle 創(chuàng)建的通知
- 設備策略控制器 (DPC) 和企業(yè)支持包
3.1.7 授權訪問部分照片或視頻
當Android 14上的應用程序請求在Android 13(API級別33)引入的權限READ_MEDIA_IMAGES 或READ_MEDIA_VIDEO時,可以選擇授權部分訪問權限。
注意:targetSDK≥33,Android 14上的應用都會受到此更改的影響。
3.1.7.1 新的彈窗顯示內容
Android 14之前:
訪問媒體圖片或視頻要申請權限READ_MEDIA_IMAGES和READ_MEDIA_VIDEO。當應用申請以上兩個權限任何一個的時候,彈出的權限框只有以下兩個選項。即允許(允許是允許所有照片)和不允許。
Android 14:
當申請權限READ_MEDIA_IMAGES和READ_MEDIA_VIDEO訪問媒體文件的時候,其彈框中有三個選項。Select photos and videos: 選擇部分圖片允許應用訪問
Allow all: 允許訪問所有圖片
Don't allow: 拒絕所有圖片的訪問
如果用戶選擇"SELECT PHOTOS AND VIDEOS",那么稍后應用再次請求READ_MEDIA_IMAGES或READ_MEDID_VIDEO,系統(tǒng)將顯示不同的對話框,讓用戶有機會授予完全訪問權限、保留當前選擇或授予其他照片和視頻。一段時間后,系統(tǒng)會直接顯示系統(tǒng)選擇器。
為了幫助應用程序支持新的更改,系統(tǒng)引入了一個新的權限READ_MEDIA_VISUAL_USER_SELECTED。根據您的應用程序是否使用新權限,系統(tǒng)的行為會有所不同。
注意:如果您的應用程序已經使用了照片選擇器,則無需采取任何措施來支持此更改。否則,請考慮使用照片選擇器,而不是采用此更改。
3.1.7.2 如果應用沒有適配
如果沒有聲明READ_MEDIA_VISUAL_USER_SELECTED權限,則會發(fā)生以下現(xiàn)象:READ_MEDIA_IMAGES和READ_MEDI_AVIDEO權限在應用會話期間授予,并且提供臨時權限授予和對用戶選擇的照片和視頻的臨時訪問。 當用戶主動殺死應用時,系統(tǒng)最終會拒絕這些權限。這種行為和其他一次性權限一樣。
如果應用以后要訪問其他照片和視頻,則必須手動再次請求READ_MEDIA_IMAGES或READ_MEDIA_VIDEO權限。該系統(tǒng)遵循與初始權限請求相同的流程,提示用戶選擇照片和視頻。
如果應用遵循最佳方案,則此更改不會影響應用。假設應用不保留URI訪問、不存儲系統(tǒng)權限狀態(tài)或者在權限更改后不刷新顯示的圖像集,則情況表現(xiàn)如此。然而,根據應用的不同情況,可能表現(xiàn)不會理想。
注意:你的應用不需要任何媒體權限,就可使用照片選擇器或任何系統(tǒng)意圖,例如ACTION_GET_CONTENT或ACTION_OPEN_DOCUMENT。
3.1.7.3 遷移以控制應用中的行為
如果聲明了READ_MEDIA_VISUAL_USER_SELECTED權限,并且用戶選擇了系統(tǒng)彈框中的"SELECT PHOTOS AND VIDEOS",接下來會出現(xiàn)以下情況:READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO權限都會被拒絕。
授予應用READ_MEDIA_VISUAL_USER_SELECTED權限,用來臨時訪問用戶已經選擇的照片和視頻。
如果想要申請其他的照片或視頻,你必須要再次申請READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO權限。
注意:在應用中創(chuàng)建一個UI界面,用戶必須在重新申請READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO權限前按下該界面。防止用戶再次看到系統(tǒng)彈框而感到驚訝。
READ_MEDIA_IMAGES和READ_MEDI_AVIDEO是用戶用來訪問照片和視頻媒體庫的其他唯一權限。聲明READ_MEDIA_VISUAL_USER_SELECTED會讓權限控制器意識到您的應用支持手動重新請求選擇更多照片和視頻。
為了防止用戶看到多個系統(tǒng)權限請求彈框,請在一次操作中申請 READ_MEDIA_VISUAL_USER_SELECTED、ACCESS_MEDIA_LOCATION 和訪問媒體的權限(READ_MEDIA_IMAGES或READ_MEDIA_VIDEO或兩個一起)。
3.1.7.4 設備升級時保留訪問照片和視頻的權限
如果應用所在設備從以前的安卓版本升級到Android 14版本,系統(tǒng)會保持對用戶照片和視頻的完全訪問權限,并自動授予應用一些權限。最終授予的權限取決于設備升級到Android 14之前應用所擁有的權限。
- 從Android 13升級
- 應用安裝在Android 13。
- 應用擁有READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO權限。
- 當設備升級到Android 14的時候,應用仍然處于安裝狀態(tài)。
滿足以上三個條件,應用仍然有權限訪問所有的照片和視頻。系統(tǒng)會自動授予應用READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO權限。
- 從Android 12或更早的版本升級到Android 14
- 應用安裝在Android 12或更早的版本。
- 應用擁有READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE 權限。
- 當設備升級到Android 14的時候,應用仍然處于安裝狀態(tài)。
滿足以上三個條件,應用仍然有權限訪問所有的照片和視頻。系統(tǒng)會自動授予應用READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO權限。
3.1.7.5 最佳方案
以下是使用READ_MEDIA_VISUAL_USER_SELECTED權限的最佳方案。
- 后臺媒體需要新的權限
當應用程序進行媒體處理,在后臺壓縮或上傳媒體文件,此時READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO權限處于被拒絕狀態(tài)。強烈建議添加對READ_MEDIA_VISUAL_USER_SELECTED的支持?;蛘邞脩撏ㄟ^使用InputStream或使用ContentResolver來進行查詢是否可以訪問特定的照片或視頻。
- 不要存儲權限狀態(tài)
不要通過SharedPreferences或DataStore永久存儲權限狀態(tài)。存儲的權限狀態(tài)可能和實際狀態(tài)不同步。權限狀態(tài)可以在權限重置、應用程序休眠、用戶啟動的應用程序設置更改或應用進入后臺更改。相反,請使用ContextCompat.checkSelfPermission()檢查存儲權限。
- 有時并不可以完全訪問所有的照片或視頻
基于Android 14引入的變化,應用可能只能訪問部分照片庫的內容。當應用查詢使用ContentResolver查詢緩存媒體的數據,但是緩存的數據可能不是最新的。
通過以下方法可以解決不要依賴存儲緩存,要借助ContentResolver直接查詢媒體庫
當應用在前臺時,將結果保存到存儲中。
- URI為臨時訪問
如果用戶在系統(tǒng)權限對話框中選擇"SELECT PHOTOS AND VIDEOS",則所選取的照片和視頻的訪問權限的時間是有限的。無論應用有沒有權限,應用都應該能處理無法訪問任何Uri的情況。
3.1.8 非線性縮放至 200%
從Android 14開始系統(tǒng)支持200%的字體縮放,為低視力用戶提供了符合Web Content Accessibility Guidelines (WCAG)的額外無障礙選項。
為了防止屏幕中大型文本元素縮放過大,系統(tǒng)應用非線性縮放曲線。這種縮放策略意味著大文本的縮放速率與小文本不同。非線性字體縮放有助于保持不同大小元素之間的比例層次,同時緩解高度線性文本縮放問題(例如文本被剪切或由于顯示尺寸過大而變的難以閱讀)。
3.1.8.1 使用非線性縮放測試應用
如果使用scaled pixel(sp)單位來定義文本大小,那么這些額外的選項和縮放改善將會自動應用于應用的文本。然而,應該在啟動最大字體大小(200%)的情況下執(zhí)行UI測試,確保應用正確使用字體大小,并且可在不影響可用性的情況下適應更大的字體。
啟動200%的字體大小,執(zhí)行以下步驟:
- 打開設置,找打“輔助功能”>“顯示文本和大小”。
- 找到字體尺寸選項,按下+按鈕知道最大的縮放尺寸
3.1.8.2 使用scaled pixel(sp)作為文本尺寸單位
請記住始終以sp為單位來指定文本大小。當應用程序使用sp單位時,Android可以應用用戶喜歡的文本大小并適當縮放。
不要使用sp作為view之間的距離或高度單位,以sp單位對于非線性縮放,縮放大小將不成比例。例如4sp+20sp并不等于24sp。
3.1.8.3 換算scaled pixel(sp)單位
使用TypedValue.applyDimension()方法將sp單位轉為pixels單位,并且使用TypedValue.deriveDimension()方法將pixels單位轉為sp單位。這些方法自動應用適當的非線性縮放曲線。
避免使用Configuration.fontScale或DisplayMetrics.scaledDensity。因為現(xiàn)在字體縮放是非線性的,這些值將變的不再準確。
3.2 行為變更:以Android 14為目標平臺的應用
3.2.1 必須申明前臺服務類型
為了幫助開發(fā)人員更有意識地定義面向用戶的前臺服務,Android 10 引入了 android:foregroundServiceType 屬性。
如果App以 Android 14 為目標平臺,則它必須指定適當的前臺服務類型。與之前的 Android 版本一樣,可以組合多種類型??晒┻x擇的前臺服務類型有:camera、connectedDevice、dataSync、health、location、mediaPlayback、mediaProjection、microphone、phoneCall、remoteMessaging、shortService、specialUse、systemExempted。
如果App中的使用場景與這些類型中的任何一種都不相關,我們強烈建議使用 WorkManager 或user-initiated data transfer jobs。
Android 14新增的前臺服務類型為health、remoteMessaging、shortService、specialUse 和 systemExempted 類型。
以下代碼片段提供了AndroidManifest.xml中前臺服務類型聲明的示例:
<manifest ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<application ...>
<service
android:name=".MyMediaPlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="false">
</service>
</application>
</manifest>復制
如果面向 Android 14 的應用未在AndroidManifest.xml中定義給定服務的類型,則系統(tǒng)將在為該服務調用 startForeground() 時拋出MissingForegroundServiceTypeException。
3.2.1.1 聲明使用前臺服務類型的新權限
如果以 Android 14 為目標平臺的應用使用前臺服務,則它們必須根據前臺服務類型聲明 Android 14 引入的特定權限。所有權限都定義為normal權限,并默認授予。用戶無法撤銷這些權限。如果調用 startForeground() 時未聲明適當的前臺服務類型權限,系統(tǒng)將拋出 SecurityException。
3.2.1.2 在運行時包括前臺服務類型
啟動前臺服務的應用的最佳做法是使用 startForeground() 的重載方法,可以在其中傳入前臺服務類型的按位整數??梢赃x擇傳遞一個或多個類型值。
通常,應該只聲明特定使用場景所需的類型。這使得更容易滿足系統(tǒng)對每種前臺服務類型的期望。如果前臺服務以多種類型啟動,則前臺服務必須遵守所有類型的平臺實施要求。
但是,如果啟動使用以下任何類型的前臺服務,則每次為該服務調用 startForeground() 時都應始終包含這些類型:
FOREGROUND_SERVICE_TYPE_CAMERA、FOREGROUND_SERVICE_TYPE_LOCATION、FOREGROUND_SERVICE_TYPE_MICROPHONE。
如果調用中未指定前臺服務類型,它將默認為AndroidManifest.xml中定義的值。
簡單來說,新特性要求開發(fā)者在使用前臺服務時,在manifest中聲明前臺服務類型和所需權限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<application
...
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="mediaPlayback" />復制
也可以在啟動前臺服務時聲明類型,但此處所聲明的類型應是manifest中類型的子集,且最終前臺服務類型跟隨startForeground中的聲明。
MyService#onStartCommand:
startForeground(222, notification, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);復制
從而啟動一個前臺服務。
3.2.1.3 系統(tǒng)運行時檢查
系統(tǒng)檢查是否正確使用了前臺服務類型,并確認應用已請求正確的運行時權限或使用所需的 API。例如,系統(tǒng)期望使用前臺服務類型 FOREGROUND_SERVICE_TYPE_LOCATION 類型的應用請求 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION。
這意味著應用在向用戶請求權限和啟動前臺服務時必須遵循非常特定的操作順序。在應用嘗試調用 startForeground() 之前,必須請求并授予權限。在前臺服務啟動后請求適當權限的應用必須更改此操作順序并在啟動前臺服務之前請求權限。
注意:如果應用不滿足啟動前臺服務的所有運行時要求,系統(tǒng)會在為該服務調用 startForeground() 后拋出 SecurityException。這會阻止前臺服務啟動,可能導致正在運行的前臺服務從前臺進程狀態(tài)中刪除,并可能導致App崩潰。
3.2.1.4 預期的使用場景與相應的服務類型檢查
為了使用給定的前臺服務類型,必須在清單文件中聲明特定的權限,必須滿足特定的運行時要求,并且應用必須滿足該類型的一組預期使用場景。以下部分解釋了必須聲明的權限、運行時要求以及每種類型的預期使用場景。
3.2.2 OpenJDK 17 更新
對正則表達式的更改:現(xiàn)在不允許無效的組引用(Invalid group references),以更緊密地遵循 OpenJDK 的語義。您可能會看到 java.util.regex.Matcher 類拋出IllegalArgumentException的新情況,因此請務必測試應用中使用正則表達式的區(qū)域。要在測試時啟用或禁用此更改,請使用兼容性框架工具切換DISALLOW_INVALID_GROUP_REFERENCE 標志。
UUID 處理:java.util.UUID.fromString() 方法現(xiàn)在在驗證輸入參數時進行更嚴格的檢查,因此可能會在反序列化期間出現(xiàn) IllegalArgumentException。要在測試時啟用或禁用此更改,請使用兼容性框架工具切換 ENABLE_STRICT_VALIDATION 標志。
ProGuard 問題:在某些情況下,如果嘗試使用 ProGuard 縮小、混淆和優(yōu)化應用,則添加 java.lang.ClassValue 類會導致出現(xiàn)問題。該問題源于 Kotlin 庫,該庫根據Class.forName("java.lang.ClassValue") 是否返回類來更改運行時行為。如果應用是針對沒有可用 java.lang.ClassValue 類的舊版本runtime開發(fā)的,則這些優(yōu)化可能會從繼承自 java.lang.ClassValue 的類中刪除 computeValue 方法。
3.2.3 對于隱式意圖和掛起意圖的限制
對于針對Android 14的應用程序,Android通過以下方式限制應用程序向內部組件發(fā)送隱式意圖:
- 隱式意圖僅發(fā)送給導出的組件。應用程序必須使用顯示意圖發(fā)送給未導出的組件,或者將組件標記為導出狀態(tài)。
- 如果一個應用創(chuàng)建了一個可變的掛起意圖,而該意圖沒有指定組件或包名,那么系統(tǒng)會拋出一個異常。
這些變化可防止惡意應用程序攔截應用程序內部組件使用的隱式意圖。
例如,下面是應用在manifest文件中聲明的意圖過濾器:
<activity
android:name=".AppActivity"
android:exported="false">
<intent-filter>
<action android:name="com.example.action.APP_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>復制
如果應用嘗試使用隱式意圖去啟動這個活動,則會拋出異常:
// Throws an exception when targeting Android 14.
context.startActivity(new Intent("com.example.action.APP_ACTION"));復制
為了啟動沒有導出的活動,應用必須使用顯示意圖:
// This makes the intent explicit.
Intent explicitIntent =
new Intent("com.example.action.APP_ACTION")
explicitIntent.setPackage(context.getPackageName());
context.startActivity(explicitIntent);復制
3.2.4 動態(tài)注冊的廣播接收器必須聲明導出標志
針對Android 14平臺,在其上面使用上下文注冊接收器的應用或服務需要聲明一個標志,以表明接收器是否導出到其他應用或設備。標志為RECEIVER_EXPORTED或RECEIVER_NOT_EXPORTED。這一要求通過利用Android 13引入的有關接收器的功能,有助于保護應用程序免收安全漏洞的影響。
例如,上下文注冊的廣播接收器聲明RECEIVER_EXPORTED或RECEIVER_NOT_EXPORTED:
// 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);復制
如果應用利用Context#registerReceiver方法注冊了一個接收器,僅對于系統(tǒng)廣播,那么該應用不需要聲明以上標志。
3.2.5 更安全的動態(tài)代碼加載
如果App以 Android 14 為目標平臺并使用動態(tài)代碼加載 (DCL),則所有動態(tài)加載的文件都必須標記為只讀。否則,系統(tǒng)會拋出異常。我們建議應用盡可能避免動態(tài)加載代碼,因為這樣做會大大增加應用因代碼注入或代碼篡改而受到危害的風險。
如果必須動態(tài)加載代碼,請使用以下方法將動態(tài)加載的文件(例如 DEX、JAR 或 APK 文件)在文件打開后和寫入任何內容之前立即設置為只讀:
File jar = new File("DYNAMICALLY_LOADED_FILE.jar");
try (FileOutputStream os = new FileOutputStream(jar)) {
// Set the file to read-only first to prevent race conditions
jar.setReadOnly();
// Then write the actual file content
} catch (IOException e) { ... }
PathClassLoader cl = new PathClassLoader(jar, parentClassLoader);復制
處理已存在的動態(tài)加載文件
為防止現(xiàn)有動態(tài)加載文件拋出異常,我們建議在嘗試在應用中再次動態(tài)加載文件之前刪除并重新創(chuàng)建這些文件。重新創(chuàng)建文件時,請按照前面的指導在寫入時將文件標記為只讀。或者,可以將現(xiàn)有文件重新標記為只讀,但在這種情況下,我們強烈建議首先驗證文件的完整性(例如,通過根據可信值檢查文件的簽名),以幫助保護應用免受惡意操作。
3.2.6 防止zip文件路徑穿透攻擊
對于針對 Android 14 的應用,Android 通過以下方式防止 Zip 路徑穿透漏洞:如果 zip 文件條目名稱包含“..”或以“/”開頭,則 ZipFile(String) 和 ZipInputStream.getNextEntry() 會拋出 ZipException。
應用可以通過調用 dalvik.system.ZipPathValidator.clearCallback() 選擇關閉此驗證。
3.2.7 后臺啟動Activity的附加限制
對于目標平臺是Android 14的應用,系統(tǒng)進一步限制了應用在后臺啟動Activity的權限。目的是為了防止惡意程序濫用API從后臺啟動破壞性活動來保護用戶。
3.2.7.1 系統(tǒng)允許后臺應用啟動Activity的情況
Android 14新的限制主要是限制這些情況。
后臺啟動Activity限制的例外情況主要為:
- 應用具有可見窗口,例如前臺Activity。
- 應用在前臺任務的返回棧中擁有Activity。
- 應用在Recents屏幕上現(xiàn)有任務的返回棧中擁有Activity。
- 應用的某個Activity剛在不就前啟動
- 應用最近為某個Activity調用了finish()。這僅適用于在調用finish()時,應用在前臺或前臺任務的返回棧中擁有Activity的情況。
- 應用具有受系統(tǒng)約束的服務。此情況僅適用于以下服務,這些服務可能需要啟動界面:AccessibilityService、AutofillService、CallRedirectionService、HostApduService、InCallService、TileService、VoiceInteractionService 和 VrListenerService。
- 應用中的某個服務受另一個可見應用約束。請注意,綁定到服務的應用必須保持可見,以便后臺應用成功啟動Activity。
- 應用中某個服務受到另一個可見應用約束。請注意,綁定到服務的應用必須保持可見,以便后臺應用成功啟動Activity。
- 應用收到系統(tǒng)的PendingIntent通知。對于服務和廣播接收器的掛起Intent,應用可在該掛起Intent發(fā)送幾秒后啟動Activity。
- 應用收到另一個可見應用發(fā)送的PendingIntent。
- 應用收到它應該在其中啟動界面的系統(tǒng)廣播。包括ACTION_NEW_OUTGOINF_CALL和SECRET_CODE_ACTION。應用可在廣播發(fā)送幾秒后啟動Activity。
- 應用通過CompanionDeviceManager API與配套硬件設備相關聯(lián)。此API支持應用啟動API,以響應用戶在配對設備上執(zhí)行的操作。
- 應用是在設備所有者模式下運行的設備政策控制器。示例用例包括完全托管的企業(yè)設備,以及數字標識牌和自助服務終端等專用設備。
- 用戶已向應用授予SYSTEM_ALERT_WINDOW權限。
3.2.7.2 Android 14新增的兩個限制條件
3.2.7.2.1 新增限制一
當后臺應用使用PendingIntent#()send()方法,發(fā)送PendingIntent的時候,其相關的參數ActivityOptions應該設置setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)。
使用說明:
// A app創(chuàng)建pendingIntent
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0,
notificationIntent,
PendingIntent.FLAG_MUTABLE,
null);
//通過將pendingIntent發(fā)送到B app
//B app收到創(chuàng)建的pendingcontent,然后執(zhí)行send()方法發(fā)送,同時構建ActivityOptions,
//設置相應的模式
PendingIntent pendingIntent = intent.getParcelableExtra("pending");
ActivityOptions options
= ActivityOptions
.makeBasic()
.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
.setPendingIntentCreatorBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
pendingIntent.send(context, 0, null, null, null, null, options.toBundle()); 復制
3.2.7.2.2 新增限制二
當一個可見應用程序使用bindService()方法綁定另一個后臺應用程序的服務時,如果想要授予綁定的后臺服務所在應用啟動Activity,必須要在綁定服務的時候,即調用bindService()方法的時候包含BIND_ALLOW_ACTIVITY_STARTS。
使用說明:
targetSdk<Android 14,前臺應用綁定后臺服務啟動Activity需要BIND_AUTO_CREATE
bindService(intent, coon, BIND_AUTO_CREATE);
targetSdk≥Android 14,前臺應用綁定后臺服務要啟動Activity,需要在之前的基礎上
添加BIND_ALLOW_ACTIVITY_STARTS標志。
bindService(intent, coon, BindServiceFlags.of(BIND_ALLOW_ACTIVITY_STARTS|BIND_AUTO_CREATE));復制
3.2.8 加強對non-SDK API的限制
從 Android 9(API 級別 28)開始,Android 平臺對應用能使用的非 SDK 接口實施了限制。只要應用引用非 SDK 接口或嘗試使用反射或 JNI 來獲取其句柄,這些限制就適用。這些限制旨在幫助提升用戶體驗和開發(fā)者體驗,為用戶降低應用發(fā)生崩潰的風險,同時為開發(fā)者降低緊急發(fā)布的風險。
如果應用不以 Android 14 為目標平臺,其中一些更改可能不會立即產生影響。然而,雖然應用目前可以使用一些非 SDK 接口(取決于應用的 TargetSDK),但使用任何非 SDK 方法或字段總是會帶來破壞應用的高風險。
如果不確定應用是否使用非 SDK 接口,可以使用 StrictMode API或veridex進行測試。如果應用依賴于非 SDK 接口,應該開始計劃遷移到 SDK 替代方案。盡管如此,我們了解到某些應用具有使用非 SDK 接口的有效使用場景。如果找不到使用非 SDK 接口實現(xiàn)應用功能的替代方案,可以向Google請求一個新的公共 API。
3.2.9 收緊全屏intent通知權限
在 Android 11(API level 30)中,任何應用程序都可以使用 Notification.Builder.setFullScreenIntent 在手機鎖定時發(fā)送全屏intent。您可以通過在 AndroidManifest 中聲明 USE_FULL_SCREEN_INTENT 權限在應用安裝時自動授予此權限。
全屏intent通知專為需要用戶立即關注的極高優(yōu)先級通知而設計,例如來電或用戶設置的鬧鐘。從 Android 14 開始,允許使用此權限的應用僅限于提供通話和鬧鐘的應用。
在用戶更新到 Android 14 之前,此權限對安裝在手機上的應用程序保持啟用狀態(tài)。用戶可以打開和關閉此權限。
您可以使用新的 API NotificationManager.canUseFullScreenIntent 來檢查App是否具有權限;如果沒有,App可以使用新的意圖 ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT 來啟動設置頁面,用戶可以在其中授予權限。
3.2.10 數據安全信息更加可見
3.2.10.1 權限方面的考慮
對于某些權限,系統(tǒng)運行時權限對話框現(xiàn)在包含一個可點擊的部分,突出顯示您的應用程序的數據共享操作。系統(tǒng)對話框的這一部分包含如下信息,例如您的應用可能決定與第三方共享數據的原因,以及將用戶鏈接到他們可以控制您的應用數據訪問的位置。
3.2.10.2 系統(tǒng)通知
如果用戶在App中分享了他們的位置,并且App隨后通過以下方式之一擴大了其位置共享的使用范圍,則用戶會在 30 天內看到系統(tǒng)通知:
- App開始與第三方共享位置數據。
- App開始共享位置數據以用于廣告相關目的。
當用戶點擊此通知時,他們將被帶到一個新的位置數據共享更新頁面,該頁面顯示了進行相關更改的應用程序的詳細列表,以及更改每個應用程序權限設置的簡便方法。下圖顯示了此流程的示例。
新的位置數據共享更新頁面可從設備的Settings > Privacy或者Settings > Security & Privacy頁面永久訪問,并顯示最近增加的位置數據共享。
當某些已安裝的應用程序更改其數據共享范圍時出現(xiàn)的系統(tǒng)通知
4.遷移指南
在每個Android版本中,都會引入新功能和行為更改,其目的是使Android更有用、更安全且性能更高。在許多情況下,應用在新版本上可直接使用,完全符合預期,但是在某些情況下,需要更新應用以適配平臺的變化。
源碼發(fā)布到AOSP(Android開源項目),用戶就可以開始使用新平臺。因此讓應用做好準備,按用戶預期的方式運行,并理想地利用新功能和API發(fā)揮平臺最大優(yōu)勢。
遷移有兩個典型的階段,可以同時進行:
- 確保應用兼容性(在Android 14最終版本發(fā)布前)
- 針對新平臺的功能和API調整應用(在最終版本發(fā)布后盡快進行)
4.1 確保與Android 14的兼容
您必須測試現(xiàn)有應用在Android 14上的運行表現(xiàn),確保用戶在最新Android版本獲得良好的體驗。有些平臺的更改可能會影響到應用的表現(xiàn)。因此,有必要進行全面測試應用并進行必要的調整。
通常您可以調整應用并發(fā)布更新,而無需更改應用的targetSdkVersion。同樣,根據應用的構建方式及其使用平臺功能的不同,您可能也不需要使用新的API或更改應用的compileSdkVersion。
在開始測試強,請務必熟悉應用行為變更。即使不更改應用的targetSdkVersion,這些變更也會影響到您的應用。
執(zhí)行兼容性測試
大多數情況下,測試Android 14的兼容性與普通應用程序的測試類似。這也是回顧核心應用程序質量指南和測試最佳實踐的時機。
要進行測試,請在運行Android 14的設備安裝您當前發(fā)布的應用,并在查找問題的同時完成所有流程和功能測試。為了集中精力測試,請查看Android 14中引入的所有應用程序的行為變化。這些變化可能會影響您的應用并可能導致應用崩潰。
同時還要確保檢查和測試受限制的非SDK接口的使用。您應該將應用使用的任何受限接口替換為公共SDK或等效的NDK接口。留意突出顯示這些訪問權限的logcat警告,并使用StrictMode方法 detectNonSdkApiUsage()來捕獲它們。
最后,請務必完整測試應用中的庫和SDK,確保它們在Android 14上面如期運行,并遵循隱私權、性能、UX、用戶體驗、數據處理和權限方面的最佳實踐。如果您遇到問題,請嘗試更新到最新版本的SDK,或者聯(lián)系SDK開發(fā)者尋求幫助。
當您完成測試并更新后,我們建議您立即發(fā)布兼容的應用。這樣可以盡早讓您的用戶測試應用,并幫助用戶順利過渡到Android 14。
4.2 更新應用的目標平臺并使用新的API進行構建
一旦發(fā)布應用兼容版本后,下一步通過更新targeSdkVersion并利用Android 14新API和功能來添加對Android 14的全面支持。準備就緒后,即可開始進行這些更新。
當您計劃全面支持Android 14的時,請查看以Android 14為目標平臺的應用的行為變更。這些針對性的行為變更可能會導致功能問題,然后需要解決這些問題。在某些情況下,這些變更需要進行大量的開發(fā)工作,因此我們建議您盡早了解并解決這些問題。為確定影響您的應用的具體的行為變更,請使用兼容性開關來測試已啟用所選變更的應用。
以下是全面支持Android 14的步驟。
- 獲取SDK、更改目標平臺并使用新API進行構建
如需要測試完整的Android 14的支持,請使用最新預覽版的Android Studio下載Android 14SDK,以及其他所需要的任何工具。接下來,更新應用的targetSdkVersion和compileSdkVersion,然后重新編譯該應用。有關詳細信息,請參閱SDK設置指南。
- 測試Android 14應用
編譯應用并將其安裝在運行Android 14的設備上后,開始測試,確保應用在Android 14平臺上正常運行。某些行為變換僅在應用以新平臺為目標平臺時適用,因此在開始之前需要查看這些行為變更。
與基本測試一樣,完成所有流程和功能以查找問題。將測試重點放在以Android 14為目標平臺的行為變更上。您還可以根據核心應用質量指南和測試最佳實踐來檢查應用。
確保檢查和測試可能使用的受限非SDK接口的使用。留意高亮顯示這些訪問權限的logcat警告,并使用StrictMode方法 detectNonSdkApiUsage()以編程方式來捕獲它們。
最后,請務必完整測試應用中的庫和SDK,確保它們在Android 14上面如期運行,并遵循隱私權、性能、UX、用戶體驗、數據處理和權限方面的最佳實踐。如果您遇到問題,請嘗試更新到最新版本的SDK,或者聯(lián)系SDK開發(fā)者尋求幫助。
- 切換應用兼容性測試開關進行測試
Android 14包含兼容性開關,可讓您更輕松地在應用中測試針對性的行為變更。對于可調試的應用程序,切換開關可讓您:在不實際更改應用程序的targetSdkVersion的情況下測試針對性的變更。您可以切換開關強制啟用特定的針對性行為變更,以評估對現(xiàn)有應用的影響。
僅針對特定變更進行測試。您可以使用切換開關停用除了要測試的變更之外的所有針對性變更,而不必一次性處理所有針對性變更。
通過adb管理切換開關。您可以使用adb命令在自動測試環(huán)境中啟用和停用可切換的變更。
使用標準變更ID更快地進行調試。每個可切換的變更都具有唯一ID和名稱,可用于在日志輸出中快速調試出根本原因。
在您準備更改應用目標平臺時,或者在積極開發(fā)以便支持Android 14時,切換開關將會十分有用。如需了解更多信息,請參閱兼容性框架變更(Android 14)。