Android 12 Beta 版
本文主要摘取字Google官方文檔關(guān)于 Android 12 Beta 版相關(guān)修改和變更介紹,手動(dòng)整理,如有錯(cuò)誤,歡迎指正
@[TOC](Android 12 Beta 版 )
行為變更:所有應(yīng)用
Android 12 平臺(tái)包含一些行為變更,這些變更可能會(huì)影響您的應(yīng)用。以下行為變更將影響在 Android 12 上運(yùn)行的所有應(yīng)用,無(wú)論采用哪種 targetSdkVersion 都不例外。您應(yīng)該測(cè)試您的應(yīng)用,然后根據(jù)需要進(jìn)行修改,以適當(dāng)?shù)刂С诌@些變更。
用戶體驗(yàn)
滾動(dòng)效果
滾動(dòng)事件的行為在 Android 12 中發(fā)生了變化
在搭載 Android 12 及更高版本的設(shè)備上,滾動(dòng)事件的視覺(jué)行為發(fā)生了變化。
在 Android 11 及更低版本中,滾動(dòng)事件會(huì)使視覺(jué)元素發(fā)光。在 Android 12 及更高版本中,發(fā)生拖動(dòng)事件時(shí),視覺(jué)元素會(huì)拉伸和反彈;發(fā)生快速滑動(dòng)事件時(shí),它們會(huì)快速滑動(dòng)和反彈:


新的滾動(dòng)行為會(huì)影響拖動(dòng)和快速滑動(dòng)動(dòng)畫(huà)。
該行為會(huì)應(yīng)用于使用 EdgeEffect 的所有應(yīng)用,并且適用于以下類中的所有內(nèi)容:
- RecyclerView
- ListView
- ScrollView
- NestedScrollView
- HorizontalScrollView
- ViewPager
- ViewPager2
視覺(jué)效果對(duì)垂直滾動(dòng)和水平滾動(dòng)都適用。由于它默認(rèn)應(yīng)用于未停用滾動(dòng)的所有應(yīng)用,因此可以為用戶提供更一致的界面體驗(yàn)。
最佳做法
為了確保新的滾動(dòng)體驗(yàn)與您的應(yīng)用完美搭配,請(qǐng)遵循以下最佳做法:
- 適當(dāng)調(diào)整滾動(dòng)容器的大小,使其子視圖能夠容納在邊界內(nèi)。
- 增加滾動(dòng)拉伸時(shí),對(duì) EdgeEffect.onPull(deltaDistance, displacement) 中的 deltaDistance 使用正值;減少拉伸時(shí),使用負(fù)值。
- 向下滾動(dòng)時(shí)捕捉動(dòng)畫(huà)。
拉伸 EdgeEffect 的用法
EdgeEffect
添加了兩個(gè)用于實(shí)現(xiàn)拉伸滾動(dòng)效果的 API。
float getDistance()
float onPullDistance(float deltaDistance, float displacement)
為了利用拉伸滾動(dòng)提供最佳用戶體驗(yàn),請(qǐng)執(zhí)行以下操作:
- 當(dāng)用戶在釋放動(dòng)畫(huà)過(guò)程中釋放并輕觸內(nèi)容時(shí),將輕觸注冊(cè)為“捕捉”。用戶停止動(dòng)畫(huà)并再次開(kāi)始操控拉伸。
- 當(dāng)用戶沿拉伸的相反方向移動(dòng)手指時(shí),釋放拉伸,直到其完全消失,然后開(kāi)始滾動(dòng)。
- 當(dāng)用戶在拉伸過(guò)程中快速滑動(dòng)時(shí),快速滑動(dòng) EdgeEffect 以增強(qiáng)拉伸效果。
捕捉動(dòng)畫(huà)
當(dāng)用戶捕捉活動(dòng)的拉伸動(dòng)畫(huà)時(shí),EdgeEffect.isFinished() 會(huì)返回 false。這表示拉伸應(yīng)由輕觸動(dòng)作操控。在大多數(shù)容器中,這在 onInterceptTouchEvent() 中檢測(cè):
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
...
when (action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN ->
...
isBeingDragged = !edgeEffectBottom.isFinished() ||
!edgeEffectTop.isFinished()
...
}
return isBeingDragged
}
在前面的示例中,當(dāng) mIsBeingDragged 為 true 時(shí),onInterceptTouchEvent() 返回 true,因此這對(duì)于在子級(jí)有機(jī)會(huì)消耗事件之前消耗事件已經(jīng)足夠了。
釋放滾動(dòng)效果
務(wù)必在滾動(dòng)之前釋放拉伸效果,以防止將拉伸應(yīng)用于滾動(dòng)內(nèi)容:
override fun onTouchEvent(ev: MotionEvent): Boolean {
val activePointerIndex = ev.actionIndex
when (ev.getActionMasked()) {
MotionEvent.ACTION_MOVE ->
val x = ev.getX(activePointerIndex)
val y = ev.getY(activePointerIndex)
var deltaY = y - mLastMotionY
val pullDistance = deltaY / height
val displacement = x / width
if (deltaY < 0f && mEdgeEffectTop.distance > 0f) {
deltaY -= height * mEdgeEffectTop
.onPullDistance(pullDistance, displacement);
}
if (deltaY > 0f && mEdgeEffectBottom.distance > 0f) {
deltaY += height * mEdgeEffectBottom
.onPullDistance(-pullDistance, 1 - displacement);
}
...
}
拖動(dòng)時(shí),在將觸摸事件傳遞給嵌套滾動(dòng)或拖動(dòng)滾動(dòng)內(nèi)容之前,必須消耗 EdgeEffect 的拉取距離。在前面的代碼示例中,當(dāng)正在顯示邊緣效果且可以通過(guò)動(dòng)作將其釋放時(shí),getDistance() 會(huì)返回一個(gè)正值。當(dāng)觸摸事件釋放拉伸時(shí),它首先由 EdgeEffect 消耗,這樣就可以在顯示其他效果(如嵌套滾動(dòng))之前完全釋放。您可以使用 getDistance() 了解釋放當(dāng)前效果所需的拉取距離。
onPullDistance() 與 onPull() 的不同之處在于返回所傳遞增量的已消耗量。onPull() 以前允許發(fā)光效果的總距離為負(fù)值。在 Android 12 及更高版本中,如果在 getDistance() 為 0 時(shí)向 onPull() 或 onPullDistance() 傳遞負(fù)的 deltaDistance 值,則拉伸不會(huì)發(fā)生任何變化。
停用
可以在 XML 布局文件中或以編程方式停用滾動(dòng):
<!-- Via markup -->
<ScrollView
...
android:overScrollMode="never"
...
或者,在代碼中設(shè)置:
<!-- Programmatically-->
...
recyclerview.overScrollMode = View.OVER_SCROLL_NEVER
...
行為變更:以 Android 12 為目標(biāo)平臺(tái)的應(yīng)用
與早期版本一樣,Android 12 包含一些行為變更,這些變更可能會(huì)影響您的應(yīng)用。以下行為變更僅影響以 Android 12 或更高版本為目標(biāo)平臺(tái)的應(yīng)用。如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái),您應(yīng)該修改自己的應(yīng)用以適當(dāng)?shù)刂С诌@些行為(如果適用)。
用戶體驗(yàn)
畫(huà)中畫(huà)行為改進(jìn)
Android 12 針對(duì)畫(huà)中畫(huà) (PiP) 模式引入了行為改進(jìn)。
自定義通知
Android 12 更改了完全自定義通知的外觀和行為。以前,自定義通知能夠使用整個(gè)通知區(qū)域并提供自己的布局和樣式。由此產(chǎn)生的反模式可能會(huì)令用戶困惑,或在不同設(shè)備上引發(fā)布局兼容性問(wèn)題。
對(duì)于以 Android 12 為目標(biāo)平臺(tái)的應(yīng)用,包含自定義內(nèi)容視圖的通知將不再使用完整通知區(qū)域;相反,系統(tǒng)會(huì)應(yīng)用標(biāo)準(zhǔn)模板。此模板可確保自定義通知在所有狀態(tài)下都與其他通知相同,例如,在收起狀態(tài)下的通知圖標(biāo)和展開(kāi)功能,以及在展開(kāi)狀態(tài)下的通知圖標(biāo)、應(yīng)用名稱和收起功能。此行為與 Notification.DecoratedCustomViewStyle 的行為幾乎完全相同。
通過(guò)這種方式,Android 12 通過(guò)為用戶提供可看到且熟悉的通知展開(kāi)功能,使所有通知保持外觀一致且易于瀏覽。
下圖顯示了標(biāo)準(zhǔn)模板中的自定義通知:

以下示例展示了在收起狀態(tài)和展開(kāi)狀態(tài)下呈現(xiàn)的自定義通知:


Android 12 中的變更會(huì)影響某些定義 Notification.Style 的自定義子類的應(yīng)用,或使用 Notification.Builder 的方法 setCustomContentView(RemoteViews)、setCustomBigContentView(RemoteViews) 和 setCustomHeadsUpContentView(RemoteViews) 的應(yīng)用。
如果你的應(yīng)用使用的是完全自定義的通知,最好盡快使用新模板進(jìn)行測(cè)試
- 啟用自定義通知變更:
- 將應(yīng)用的 targetSdkVersion 變更為 S 以啟用新行為。
- 重新編譯。
- 在搭載 Android 12 的設(shè)備或模擬器上安裝您的應(yīng)用。
- 測(cè)試所有使用自定義視圖的通知,確保這些通知在通知欄中看起來(lái)符合預(yù)期。在測(cè)試時(shí),請(qǐng)考慮以下注意事項(xiàng)并進(jìn)行必要的調(diào)整:
- 自定義視圖的尺寸已更改。一般來(lái)說(shuō),提供給自定義通知的高度比之前小。在收起狀態(tài)下,自定義內(nèi)容的最大高度已從 106dp 減少到 48dp。此外,水平空間也減小了。
- 對(duì)于以 Android 12 為目標(biāo)平臺(tái)的應(yīng)用,所有通知都是可展開(kāi)的。通常,這意味著,如果您使用的是 setCustomContentView,則還需要使用 setBigCustomContentView,以確保收起狀態(tài)和展開(kāi)狀態(tài)保持一致。
- 為了確?!案?dòng)通知”狀態(tài)看起來(lái)符合您的預(yù)期,請(qǐng)勿忘記將通知渠道的重要性提升至“高”(在屏幕中彈出)。
Android App Links 驗(yàn)證的變更
對(duì)于以 Android 12 為目標(biāo)平臺(tái)的應(yīng)用,系統(tǒng)對(duì) Android App Links 的驗(yàn)證方式進(jìn)行了一些更改。這些變更可以提升應(yīng)用鏈接體驗(yàn)的可靠性,并且能夠增強(qiáng)應(yīng)用開(kāi)發(fā)者和最終用戶的控制能力。
如果以 Android 12 為目標(biāo)平臺(tái)并且依靠 Android App Links 驗(yàn)證在您的應(yīng)用中打開(kāi)網(wǎng)頁(yè)鏈接,請(qǐng)更新 Android App Links 聲明,以支持更改后的驗(yàn)證流程。您也可以手動(dòng)調(diào)用網(wǎng)域驗(yàn)證來(lái)測(cè)試聲明的可靠性。
隱私設(shè)置
大致位置
使用以 Android 12 為目標(biāo)平臺(tái)的應(yīng)用時(shí),用戶可以請(qǐng)求應(yīng)用只能訪問(wèn)大致位置信息。
<table> <tr> <td bgcolor= #DBE4FF><font color=#01579B size =3 face ="黑體" > 注意:如果您的應(yīng)用請(qǐng)求 ACCESS_COARSE_LOCATION 但未請(qǐng)求 ACCESS_FINE_LOCATION,則此變更不會(huì)影響您的應(yīng)用。</font></td></tr></table>
如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái)并且請(qǐng)求 ACCESS_FINE_LOCATION 運(yùn)行時(shí)權(quán)限,則您還必須請(qǐng)求 ACCESS_COARSE_LOCATION 權(quán)限。您必須在單個(gè)運(yùn)行時(shí)請(qǐng)求中包含這兩項(xiàng)權(quán)限。
當(dāng)您的應(yīng)用同時(shí)請(qǐng)求 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 時(shí),系統(tǒng)權(quán)限對(duì)話框?qū)橛脩籼峁┮韵滦逻x項(xiàng),如圖 1 所示:
- 確切:提供 ACCESS_FINE_LOCATION 權(quán)限提供的位置精確度。
- 大致:提供 ACCESS_COARSE_LOCATION 權(quán)限提供的位置精確度。
Android 12 基于最近平臺(tái)對(duì)位置權(quán)限模型所做的變更,包括后臺(tái)位置信息權(quán)限和單次授權(quán)。當(dāng)應(yīng)用以 Android 12 為目標(biāo)平臺(tái)時(shí),用戶可以請(qǐng)求應(yīng)用僅檢索大致位置信息,即使應(yīng)用請(qǐng)求 ACCESS_FINE_LOCATION 運(yùn)行時(shí)權(quán)限也是如此。
如果您的應(yīng)用請(qǐng)求 ACCESS_COARSE_LOCATION 但未請(qǐng)求 ACCESS_FINE_LOCATION,則本頁(yè)介紹的變更不會(huì)帶來(lái)任何影響。
圖 1 顯示了您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái)且僅請(qǐng)求 ACCESS_COARSE_LOCATION 時(shí)顯示的面向用戶的對(duì)話框。
為了更好地尊重用戶隱私,建議您僅請(qǐng)求 ACCESS_COARSE_LOCATION。即使您只能訪問(wèn)大致位置信息,也可以滿足大多數(shù)用例的要求。
如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái)并且您請(qǐng)求 ACCESS_FINE_LOCATION 權(quán)限,則還必須請(qǐng)求 ACCESS_COARSE_LOCATION 權(quán)限。您必須在單個(gè)運(yùn)行時(shí)請(qǐng)求中包含這兩項(xiàng)權(quán)限。如果您嘗試僅請(qǐng)求 ACCESS_FINE_LOCATION,則系統(tǒng)會(huì)忽略該請(qǐng)求,并在 Logcat 中記錄以下錯(cuò)誤消息:
ACCESS_FINE_LOCATION must be requested with ACCESS_COARSE_LOCATION。
用戶在大致位置與確切位置之間進(jìn)行選擇
當(dāng)您的應(yīng)用同時(shí)請(qǐng)求 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 時(shí),系統(tǒng)權(quán)限對(duì)話框?qū)橛脩籼峁┮韵滦逻x項(xiàng):
- 確切:提供 ACCESS_FINE_LOCATION 權(quán)限提供的位置信息精確度。
- 大致:提供 ACCESS_COARSE_LOCATION 權(quán)限提供的位置信息精確度。
圖 2 顯示該對(duì)話框包含這兩個(gè)新選項(xiàng)的視覺(jué)提示,以幫助用戶做出選擇。用戶確定位置信息精確度后,他們可以點(diǎn)按三個(gè)按鈕中的一個(gè)來(lái)選擇權(quán)限授予的時(shí)長(zhǎng)。這些按鈕與搭載 Android 11(API 級(jí)別 30)的設(shè)備上的位置權(quán)限對(duì)話框中顯示的按鈕相同。
在 Android 12 中,用戶可以轉(zhuǎn)到系統(tǒng)設(shè)置,以設(shè)置任何應(yīng)用的首選位置信息精確度,而不管該應(yīng)用的目標(biāo) SDK 版本是什么。即使您的應(yīng)用安裝在搭載 Android 11 或更低版本的設(shè)備上,然后升級(jí)到 Android 12,也是如此。如果用戶從權(quán)限對(duì)話框或在系統(tǒng)設(shè)置中將應(yīng)用的位置信息訪問(wèn)權(quán)限從確切位置降級(jí)到大致位置,則系統(tǒng)會(huì)重啟應(yīng)用的進(jìn)程。因此,遵循有關(guān)請(qǐng)求運(yùn)行時(shí)權(quán)限的最佳做法特別重要。
用戶的選擇會(huì)影響權(quán)限授予
下表顯示了系統(tǒng)根據(jù)用戶在運(yùn)行時(shí)權(quán)限對(duì)話框中選擇的選項(xiàng)向您的應(yīng)用授予的權(quán)限:
| 確切 | 大致 | |
|---|---|---|
| 僅在使用該應(yīng)用時(shí)允許 | ACCESS_FINE_LOCATION 和ACCESS_COARSE_LOCATION | ACCESS_COARSE_LOCATION |
| 僅限這一次 | ACCESS_FINE_LOCATION 和ACCESS_COARSE_LOCATION | ACCESS_COARSE_LOCATION |
| 拒絕 | 無(wú)位置權(quán)限 | 無(wú)位置權(quán)限 |
如需確定系統(tǒng)已向您的應(yīng)用授予的權(quán)限,請(qǐng)查看權(quán)限請(qǐng)求的返回值。您可以在類似于下面的代碼中使用 Jetpack 庫(kù),也可以使用平臺(tái)庫(kù),在這種情況下,您自行管理權(quán)限請(qǐng)求代碼。
val locationPermissionRequest = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
when {
permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
// Precise location access granted.
}
permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
// Only approximate location access granted.
} else -> {
// No location access granted.
}
}
}
// ...
// Before you perform the actual permission request, check whether your app
// already has the permissions, and whether your app needs to show a permission
// rationale dialog. For more details, see Request permissions.
locationPermissionRequest.launch(arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION))
用戶的選擇還會(huì)影響后臺(tái)位置信息
如果系統(tǒng)向您的應(yīng)用授予 ACCESS_BACKGROUND_LOCATION 權(quán)限,則用戶在位置權(quán)限對(duì)話框中的選擇也適用于后臺(tái)位置信息。
例如,如果用戶向您的應(yīng)用授予 ACCESS_BACKGROUND_LOCATION 權(quán)限,但僅授予在前臺(tái)訪問(wèn)大致位置信息的權(quán)限,則您的應(yīng)用在后臺(tái)也只有大致位置信息的訪問(wèn)權(quán)限。
升級(jí)到確切位置
如果您的應(yīng)用當(dāng)前依賴于使用 ACCESS_FINE_LOCATION 權(quán)限訪問(wèn)確切位置,則大致位置可能會(huì)影響您的應(yīng)用。
在讓用戶將應(yīng)用的訪問(wèn)權(quán)限升級(jí)到確切位置之前,請(qǐng)考慮應(yīng)用的用例是否確實(shí)需要這一級(jí)別的精確度。如果您的應(yīng)用需要通過(guò)藍(lán)牙或 Wi-Fi 將某個(gè)設(shè)備與附近的設(shè)備配對(duì),請(qǐng)考慮使用配套設(shè)備配對(duì)或新的藍(lán)牙權(quán)限,而不是請(qǐng)求 ACCESS_FINE_LOCATION 權(quán)限。
如需請(qǐng)求用戶將應(yīng)用的位置信息訪問(wèn)權(quán)限從大致位置升級(jí)到確切位置,請(qǐng)執(zhí)行以下操作:
- 說(shuō)明權(quán)限請(qǐng)求的原因
- 再次同時(shí)請(qǐng)求 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 權(quán)限。由于用戶已允許系統(tǒng)向您的應(yīng)用授予大致位置信息訪問(wèn)權(quán)限,因此這次系統(tǒng)對(duì)話框有所不同,如圖 3 和圖 4 所示:
測(cè)試您的應(yīng)用如何處理大致位置信息
如需評(píng)估您是否需要更新您的應(yīng)用以支持用戶可配置的位置信息精確度,請(qǐng)完成本部分中所述的測(cè)試。
處理對(duì)話框中的大致位置信息請(qǐng)求
對(duì)于用戶要求在新對(duì)話框中讓您的應(yīng)用具有大致位置信息訪問(wèn)權(quán)限的請(qǐng)求,如需檢查您的應(yīng)用如何處理此類請(qǐng)求,請(qǐng)執(zhí)行以下操作:
- 同時(shí)請(qǐng)求 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION。
- 在顯示的對(duì)話框中(圖 2),選擇頂部附近的大致,以及底部附近的僅在使用該應(yīng)用時(shí)允許或僅限這一次。
- 檢查應(yīng)用的用例是否仍按預(yù)期工作,即使您的應(yīng)用只有大致位置信息訪問(wèn)權(quán)限也是如此。
處理系統(tǒng)設(shè)置中的大致位置信息降級(jí)
對(duì)于用戶要求在系統(tǒng)設(shè)置中將您的應(yīng)用的位置信息訪問(wèn)權(quán)限從確切位置更改為大致位置的請(qǐng)求,如需檢查您的應(yīng)用如何處理此類請(qǐng)求,請(qǐng)執(zhí)行以下操作:
- 同時(shí)請(qǐng)求 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION。
- 在顯示的對(duì)話框中(圖 2),選擇頂部附近的確切,以及底部附近的僅在使用該應(yīng)用時(shí)允許或僅限這一次。
- 轉(zhuǎn)到系統(tǒng)設(shè)置中應(yīng)用的權(quán)限屏幕。
- 在位置權(quán)限屏幕上,關(guān)閉使用確切位置。圖 5 中顯示了此選項(xiàng)。
與任何權(quán)限降級(jí)一樣,系統(tǒng)會(huì)重啟應(yīng)用的進(jìn)程。 - 檢查應(yīng)用的用例是否仍按預(yù)期工作,即使您的應(yīng)用只有大致位置信息訪問(wèn)權(quán)限也是如此。
處理系統(tǒng)設(shè)置中的確切位置升級(jí)
對(duì)于用戶要求在系統(tǒng)設(shè)置中將您的應(yīng)用的位置信息訪問(wèn)權(quán)限從大致位置更改為確切位置的請(qǐng)求,如需檢查您的應(yīng)用如何處理此類請(qǐng)求,請(qǐng)執(zhí)行以下操作:
- 同時(shí)請(qǐng)求 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION。
- 在顯示的對(duì)話框中(圖 2),選擇頂部附近的大致,以及底部附近的僅在使用該應(yīng)用時(shí)允許或僅限這一次。
- 轉(zhuǎn)到系統(tǒng)設(shè)置中應(yīng)用的權(quán)限屏幕。
- 在位置權(quán)限屏幕上,開(kāi)啟使用確切位置,如圖 5 所示。
由于此權(quán)限更改是升級(jí),因此系統(tǒng)不會(huì)重啟您的應(yīng)用。 - 檢查您的應(yīng)用是否可以在其基于位置信息的用例中接收更準(zhǔn)確的位置數(shù)據(jù)。
應(yīng)用休眠
Android 12 在 Android 11(API 級(jí)別 30)中引入的自動(dòng)重置權(quán)限行為的基礎(chǔ)上進(jìn)行了擴(kuò)展。如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái)并且用戶有幾個(gè)月未與您的應(yīng)用互動(dòng),則系統(tǒng)會(huì)自動(dòng)重置授予的所有權(quán)限并將您的應(yīng)用置于休眠狀態(tài)。
<table> <tr> <td bgcolor= #DBE4FF><font color=#AA00FF face ="黑體" > 注意:如果您的應(yīng)用是實(shí)現(xiàn) DeviceAdminService 或者在設(shè)備所有者或個(gè)人資料所有者操作模式下運(yùn)行的設(shè)備政策控制器,則系統(tǒng)絕不會(huì)將您的應(yīng)用置于休眠狀態(tài)。</font></td></tr></table>
處于休眠狀態(tài)的應(yīng)用具有以下特征:
- 系統(tǒng)會(huì)針對(duì)存儲(chǔ)空間而非性能進(jìn)行優(yōu)化。應(yīng)用緩存中的任何文件都會(huì)被移除。
- 應(yīng)用無(wú)法從后臺(tái)運(yùn)行作業(yè)或提醒。
- 應(yīng)用無(wú)法接收推送通知,包括通過(guò) Firebase Cloud Messaging 發(fā)送的高優(yōu)先級(jí)消息。
當(dāng)用戶下次與應(yīng)用互動(dòng)時(shí),應(yīng)用會(huì)退出休眠狀態(tài),并且可以再次創(chuàng)建作業(yè)、提醒和通知。不過(guò),您需要重新調(diào)度在應(yīng)用進(jìn)入休眠狀態(tài)之前調(diào)度的所有作業(yè)、提醒和通知。此工作流與用戶從系統(tǒng)設(shè)置中手動(dòng)強(qiáng)行停止應(yīng)用時(shí)的工作流類似。為了更輕松地支持此工作流,請(qǐng)使用 WorkManager。您還可以在 ACTION_BOOT_COMPLETED 廣播接收器中添加重新調(diào)度邏輯,系統(tǒng)會(huì)在您的應(yīng)用離開(kāi)休眠狀態(tài)時(shí)以及設(shè)備啟動(dòng)后調(diào)用它。
請(qǐng)求用戶停用休眠
如果您預(yù)計(jì)應(yīng)用中的某個(gè)用例會(huì)受休眠的影響,您可以向用戶發(fā)送請(qǐng)求,讓其準(zhǔn)許應(yīng)用免于休眠和自動(dòng)重置權(quán)限。如果用戶希望應(yīng)用主要在后臺(tái)運(yùn)行,即使用戶不與應(yīng)用互動(dòng),應(yīng)用也能正常工作,例如,當(dāng)應(yīng)用執(zhí)行以下一項(xiàng)或多項(xiàng)操作時(shí),這種豁免很有用:
- 通過(guò)定期報(bào)告家庭成員的位置來(lái)保障家庭安全。
- 在設(shè)備與應(yīng)用的服務(wù)器之間同步數(shù)據(jù)。
- 與智能設(shè)備(如電視)通信。
- 與配套設(shè)備(如手表)配對(duì)。
如需請(qǐng)求豁免,請(qǐng)調(diào)用包含 Intent.ACTION_APPLICATION_DETAILS_SETTINGS intent 操作的 intent。在顯示的屏幕中,用戶可以關(guān)閉名為撤消權(quán)限并釋放空間的選項(xiàng)。
<table> <tr> <td bgcolor= #DBE4FF><font color=#01579B size =3 face ="黑體" > 注意:在調(diào)用 intent 之前,不妨考慮向用戶顯示一個(gè)指導(dǎo)界面,以便用戶了解為什么您的應(yīng)用將他們引導(dǎo)至系統(tǒng)設(shè)置。</font></td></tr></table>
測(cè)試休眠行為
如需將應(yīng)用置于休眠狀態(tài)以進(jìn)行測(cè)試,請(qǐng)執(zhí)行以下操作:
- 在設(shè)備上啟用該行為:
adb shell device_config put app_hibernation app_hibernation_enabled true
- 更改應(yīng)用的狀態(tài),使其進(jìn)入休眠狀態(tài)。包含 --global 標(biāo)志的命令會(huì)強(qiáng)制應(yīng)用進(jìn)入“完全休眠”狀態(tài),以模擬系統(tǒng)已為多用戶設(shè)備上的所有用戶將應(yīng)用置于休眠狀態(tài)的情況。
adb shell cmd app_hibernation set-state PACKAGE-NAME true && \
adb shell cmd app_hibernation set-state --global PACKAGE-NAME true
移動(dòng)傳感器有采樣率限制
為了保護(hù)有關(guān)用戶的潛在敏感信息,如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái),則系統(tǒng)會(huì)對(duì)來(lái)自某些移動(dòng)傳感器和位置傳感器的數(shù)據(jù)的刷新率施加限制。這些數(shù)據(jù)包括由設(shè)備的加速度計(jì)、陀螺儀和地磁場(chǎng)傳感器記錄的值。
刷新率限制取決于您訪問(wèn)傳感器數(shù)據(jù)的方式:
- 如果您調(diào)用 registerListener() 方法,則傳感器采樣率限制為 200 Hz。對(duì)于 registerListener() 方法的所有過(guò)載變體都是如此。
- 如果您使用 SensorDirectChannel 類,則傳感器采樣率限制為 RATE_NORMAL,通常約為 50 Hz。
如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái)并且需要以較高的采樣率收集移動(dòng)傳感器數(shù)據(jù),則您必須聲明 HIGH_SAMPLING_RATE_SENSORS 權(quán)限。否則,如果您的應(yīng)用嘗試在未聲明此權(quán)限的情況下以較高的采樣率收集移動(dòng)傳感器數(shù)據(jù),就會(huì)發(fā)生 SecurityException。
<table> <tr> <td bgcolor= #DBE4FF><font color=#01579B size =3 face ="黑體" > 注意:如果用戶使用 Android 12 中的新設(shè)備切換開(kāi)關(guān)關(guān)閉麥克風(fēng)訪問(wèn)權(quán)限,則移動(dòng)傳感器和位置傳感器會(huì)有采樣率限制。無(wú)論您是否聲明 HIGH_SAMPLING_RATE_SENSORS 權(quán)限,此采樣率限制都會(huì)生效。</font></td></tr></table>
數(shù)據(jù)訪問(wèn)審核
在 Android 11(API 級(jí)別 30)中引入的數(shù)據(jù)訪問(wèn)審核 API 可讓您根據(jù)應(yīng)用的用例創(chuàng)建歸因標(biāo)記。這些標(biāo)記可讓您更輕松地確定應(yīng)用的哪一部分執(zhí)行特定類型的數(shù)據(jù)訪問(wèn)。
如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái),您必須使用以下代碼段中所示的格式在應(yīng)用的清單文件中聲明這些歸因標(biāo)記。如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái)并且您嘗試使用未在應(yīng)用的清單文件中聲明的某個(gè)歸因標(biāo)記,則系統(tǒng)會(huì)為您創(chuàng)建一個(gè) null 標(biāo)記并在 Logcat 中記錄一條消息。
<manifest ...>
<!-- The value of "android:tag" must be a literal string, and the
value of "android:label" must be a resource. The value of
"android:label" should be user-readable. -->
<attribution android:tag="sharePhotos"
android:label="@string/share_photos_attribution_label" />
...
</manifest>
WebView 中的現(xiàn)代 SameSite Cookie
Android 的 WebView 組件基于為 Google 的 Chrome 瀏覽器提供支持的開(kāi)源項(xiàng)目 Chromium。在過(guò)去一年中,Chromium 變更了對(duì)第三方 Cookie 的處理方式,目的是為了更好地保護(hù)用戶的安全和隱私,并賦予用戶更高的透明度和控制權(quán)。這些變更已面向很多 Chrome 用戶發(fā)布,從 Android 12 開(kāi)始,這些變更將應(yīng)用于 WebView 中。
Cookie 的 SameSite 屬性決定了它是可以與任何請(qǐng)求一起發(fā)送,還是只能與同站點(diǎn)請(qǐng)求一起發(fā)送。Android 12 中的 WebView 基礎(chǔ)版本(版本 89.0.4385.0)包含以下隱私保護(hù)方面的變更,旨在改善對(duì)第三方 Cookie 的默認(rèn)處理方式,并幫助防止意外跨站點(diǎn)共享:
- 沒(méi)有 SameSite 屬性的 Cookie 被視為 SameSite=Lax。
- 帶有 SameSite=None 的 Cookie 還必須指定 Secure 屬性,這意味著它們需要安全的上下文,并應(yīng)通過(guò) HTTPS 發(fā)送。
- 站點(diǎn)的 HTTP 版本和 HTTPS 版本之間的鏈接現(xiàn)在被視為跨站點(diǎn)請(qǐng)求,因此除非將 Cookie 正確標(biāo)記為 SameSite=None; Secure,否則 Cookie 不會(huì)被發(fā)送。
對(duì)于開(kāi)發(fā)者而言,一般指導(dǎo)意見(jiàn)是識(shí)別關(guān)鍵用戶流中的跨站點(diǎn) Cookie 依賴項(xiàng),并確保在需要時(shí)使用適當(dāng)?shù)闹碉@式設(shè)置 SameSite 屬性。您必須顯式指定允許在不同網(wǎng)站上運(yùn)行的 Cookie,或適用于從 HTTP 切換到 HTTPS 進(jìn)行同站點(diǎn)導(dǎo)航的 Cookie。
ADB 備份限制
為了幫助保護(hù)私有應(yīng)用數(shù)據(jù),Android 12 更改了 adb backup 命令的默認(rèn)行為。對(duì)于以 Android 12 為目標(biāo)平臺(tái)的應(yīng)用,當(dāng)用戶運(yùn)行 adb backup 命令時(shí),從設(shè)備導(dǎo)出的其他任何系統(tǒng)數(shù)據(jù)都不包含應(yīng)用數(shù)據(jù)。
如果您的測(cè)試或開(kāi)發(fā)工作流程依賴于使用 adb backup 的應(yīng)用數(shù)據(jù),現(xiàn)在您可以選擇通過(guò)在應(yīng)用的清單文件中將 android:debuggable 設(shè)置為 true 來(lái)導(dǎo)出應(yīng)用數(shù)據(jù)。
<table> <tr> <td bgcolor=#FEEFE3><font color=#D26B4A size =3 face ="黑體" > 注意:為了幫助保護(hù)應(yīng)用數(shù)據(jù),請(qǐng)務(wù)必在發(fā)布應(yīng)用前將 android:debuggable 設(shè)置為 false。</font></td></tr></table>
安全
更安全的組件導(dǎo)出
如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái),且包含使用 intent 過(guò)濾器的 activity、服務(wù)或廣播接收器,您必須為這些應(yīng)用組件顯式聲明 android:exported 屬性。
<table> <tr> <td bgcolor=#FCE8E6><font color=#E35251 size =3 face ="黑體" > 警告:如果 activity、服務(wù)或廣播接收器使用 intent 過(guò)濾器,并且未顯式聲明 android:exported 的值,則您的應(yīng)用將無(wú)法在搭載 Android 12 的設(shè)備上進(jìn)行安裝。</font></td></tr></table>
以下代碼段顯示了一個(gè)服務(wù)示例,該服務(wù)包含 intent 過(guò)濾器并針對(duì) Android 12 進(jìn)行了正確配置:
<service android:name="com.example.app.backgroundService"
android:exported="false">
<intent-filter>
<action android:name="com.example.app.START_BACKGROUND" />
</intent-filter>
</service>
Android Studio 中的消息
如果您的應(yīng)用包含使用 intent 過(guò)濾器的 activity、服務(wù)或廣播接收器但未聲明 android:exported,系統(tǒng)會(huì)顯示以下警告消息,具體取決于您使用的 Android Studio 版本:
Android Studio 2020.3.1 Canary 11 或更高版本
系統(tǒng)會(huì)顯示以下消息:
- 清單文件中會(huì)顯示以下 lint 警告:
When using intent filters, please specify android:exported as well
- 當(dāng)您嘗試編譯應(yīng)用時(shí),系統(tǒng)會(huì)顯示以下 build 錯(cuò)誤消息:
to specify an explicit value for android:exported when the corresponding \
component has an intent filter defined.```
**較低的 Android Studio 版本**
如果您嘗試安裝應(yīng)用,Logcat 會(huì)顯示以下錯(cuò)誤消息
```bash
Installation did not succeed.
The application could not be installed: INSTALL_FAILED_VERIFICATION_FAILURE
List of apks:
[0] '.../build/outputs/apk/debug/app-debug.apk'
Installation failed due to: 'null'
待處理 intent 可變性
如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái),您必須為您的應(yīng)用創(chuàng)建的每個(gè) PendingIntent 對(duì)象指定可變性。這項(xiàng)額外的要求可提高應(yīng)用的安全性。
如需聲明特定 PendingIntent 對(duì)象是否可變,請(qǐng)分別使用 PendingIntent.FLAG_MUTABLE 或 PendingIntent.FLAG_IMMUTABLE 標(biāo)志。如果您的應(yīng)用嘗試在不設(shè)置任一可變性標(biāo)志的情況下創(chuàng)建 PendingIntent 對(duì)象,系統(tǒng)會(huì)拋出 IllegalArgumentException,并在 Logcat 中顯示以下消息:
PACKAGE_NAME: Targeting S+ (version 10000 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.
盡可能創(chuàng)建不可變的待處理 intent
在大多數(shù)情況下,您的應(yīng)用應(yīng)創(chuàng)建不可變的 PendingIntent 對(duì)象,如以下代碼段所示。如果 PendingIntent 對(duì)象不可變,則應(yīng)用無(wú)法修改 intent 來(lái)調(diào)整調(diào)用 intent 的結(jié)果。
val pendingIntent = PendingIntent.getActivity(applicationContext,
REQUEST_CODE, intent,
/* flags */ PendingIntent.FLAG_IMMUTABLE)
然而,某些應(yīng)用需要?jiǎng)?chuàng)建可變的 PendingIntent 對(duì)象:
- 通知中的直接回復(fù)操作需要變更與回復(fù)關(guān)聯(lián)的 PendingIntent 對(duì)象中的剪輯數(shù)據(jù)。通常,您可以通過(guò)將 FILL_IN_CLIP_DATA 作為標(biāo)志傳遞給 fillIn() 的方法請(qǐng)求此變更。
- 如果您的應(yīng)用使用 PendingIntent 將對(duì)話放在氣泡中,則 intent 應(yīng)該可變,以便系統(tǒng)可以應(yīng)用正確的標(biāo)志,例如 FLAG_ACTIVITY_MULTIPLE_TASK 和 FLAG_ACTIVITY_NEW_DOCUMENT。
如果您的應(yīng)用創(chuàng)建了可變的 PendingIntent 對(duì)象,強(qiáng)烈建議您使用顯式 intent 并填寫(xiě) ComponentName。如此一來(lái),每當(dāng)其他應(yīng)用調(diào)用 PendingIntent 并將控制權(quán)傳回您的應(yīng)用時(shí),您的應(yīng)用中的同一組件總是會(huì)啟動(dòng)。
測(cè)試待處理的 intent 可變性變更
如需確定您的應(yīng)用是否缺少可變性聲明,請(qǐng)?jiān)?Android Studio 中查找以下 lint 警告:
Warning: Missing PendingIntent mutability flag [UnspecifiedImmutableFlag]
在開(kāi)發(fā)者預(yù)覽版計(jì)劃期間,您可以通過(guò)關(guān)閉 PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED 應(yīng)用兼容性標(biāo)志來(lái)停用此系統(tǒng)行為以進(jìn)行測(cè)試。
不安全的 intent 啟動(dòng)
為了提高平臺(tái)安全性,Android 12 提供了一種調(diào)試功能,如果您的應(yīng)用以不安全的方式啟動(dòng) intent,此功能便會(huì)發(fā)出警告。例如,您的應(yīng)用可能會(huì)以不安全的方式啟動(dòng)從 URI 重新創(chuàng)建的 intent,或者以不安全的方式啟動(dòng)下一部分定義的嵌套 intent。
嵌套 intent 簡(jiǎn)介
嵌套 intent 是在其他 intent 中作為 extra 傳遞的 intent。如果您的應(yīng)用執(zhí)行以下兩項(xiàng)操作,就會(huì)發(fā)生 StrictMode 違規(guī)行為。
- 您的應(yīng)用從已傳遞的 intent 的 extra 中解封嵌套 intent。
- 您的應(yīng)用立即使用該嵌套 intent 啟動(dòng)應(yīng)用組件,例如將 intent 傳遞給 startActivity()、startService() 或 bindService()。
配置應(yīng)用以檢測(cè)不安全的 intent 啟動(dòng)
如需檢查您的應(yīng)用中是否有不安全的 intent 啟動(dòng),請(qǐng)?jiān)谂渲?VmPolicy 時(shí)調(diào)用 detectUnsafeIntentLaunch(),如以下代碼段所示。如果您的應(yīng)用檢測(cè)到 StrictMode 違規(guī)行為,您可能需要停止應(yīng)用的執(zhí)行以保護(hù)潛在的敏感信息。
<table> <tr> <td bgcolor= #DBE4FF><font color=#01579B size =3 face ="黑體" >注意:如果您的應(yīng)用以 Android 12 為目標(biāo)平臺(tái),并在其 VmPolicy 定義中使用 detectAll() 方法,系統(tǒng)將自動(dòng)調(diào)用 detectUnsafeIntentLaunch() 方法。</font></td></tr></table>
fun onCreate() {
StrictMode.setVmPolicy(VmPolicy.Builder()
// Other StrictMode checks that you've previously added.
// ...
.detectUnsafeIntentLaunch()
.penaltyLog()
// Consider also adding penaltyDeath()
.build())
}
更負(fù)責(zé)地使用 intent
您的應(yīng)用可能會(huì)啟動(dòng) intent,以便在應(yīng)用內(nèi)的各個(gè)組件之間導(dǎo)航,或代表其他應(yīng)用執(zhí)行操作。為了在這兩種情況下最大限度地降低出現(xiàn) StrictMode 違規(guī)行為的可能性,請(qǐng)執(zhí)行以下操作:
- 僅復(fù)制 intent 中的必要 extra,并執(zhí)行任何必要的清理和驗(yàn)證。您的應(yīng)用可能會(huì)將 extra 從一個(gè) intent 復(fù)制到用于啟動(dòng)新組件的另一個(gè) intent。當(dāng)您的應(yīng)用調(diào)用 putExtras(Intent) 或 putExtras(Bundle) 時(shí),就會(huì)發(fā)生這種情況。如果您的應(yīng)用執(zhí)行這些操作其中之一,請(qǐng)僅復(fù)制接收組件所期望的 extra。如果另一個(gè) intent(它接收副本)啟動(dòng)一個(gè)未導(dǎo)出的組件,請(qǐng)先清理和驗(yàn)證 extra,然后再將其復(fù)制到啟動(dòng)該組件的 intent。
- 嵌套 intent 的內(nèi)部啟動(dòng):確保這些組件不會(huì)被導(dǎo)出。
Log()
// Consider also adding penaltyDeath()
.build())
}
更負(fù)責(zé)地使用 intent
您的應(yīng)用可能會(huì)啟動(dòng) intent,以便在應(yīng)用內(nèi)的各個(gè)組件之間導(dǎo)航,或代表其他應(yīng)用執(zhí)行操作。為了在這兩種情況下最大限度地降低出現(xiàn) StrictMode 違規(guī)行為的可能性,請(qǐng)執(zhí)行以下操作:
- 僅復(fù)制 intent 中的必要 extra,并執(zhí)行任何必要的清理和驗(yàn)證。您的應(yīng)用可能會(huì)將 extra 從一個(gè) intent 復(fù)制到用于啟動(dòng)新組件的另一個(gè) intent。當(dāng)您的應(yīng)用調(diào)用 putExtras(Intent) 或 putExtras(Bundle) 時(shí),就會(huì)發(fā)生這種情況。如果您的應(yīng)用執(zhí)行這些操作其中之一,請(qǐng)僅復(fù)制接收組件所期望的 extra。如果另一個(gè) intent(它接收副本)啟動(dòng)一個(gè)未導(dǎo)出的組件,請(qǐng)先清理和驗(yàn)證 extra,然后再將其復(fù)制到啟動(dòng)該組件的 intent。
- 嵌套 intent 的內(nèi)部啟動(dòng):確保這些組件不會(huì)被導(dǎo)出。
- 嵌套 intent 的跨應(yīng)用啟動(dòng):使用 PendingIntent 代替嵌套 intent。如此一來(lái),當(dāng) PendingIntent 從包含它的 Intent 中解封時(shí),應(yīng)用組件可以使用調(diào)用進(jìn)程的身份啟動(dòng) PendingIntent。該配置允許提供程序應(yīng)用向調(diào)用應(yīng)用的任何組件(包括未導(dǎo)出的組件)發(fā)送回調(diào)。
性能
前臺(tái)服務(wù)啟動(dòng)限制
以 Android 12 為目標(biāo)平臺(tái)的應(yīng)用再也無(wú)法在后臺(tái)運(yùn)行時(shí)啟動(dòng)前臺(tái)服務(wù),但一些特殊情況除外。如果應(yīng)用嘗試在后臺(tái)運(yùn)行時(shí)啟動(dòng)前臺(tái)服務(wù),則會(huì)引發(fā)異常(少數(shù)特殊情況除外)。當(dāng)您的應(yīng)用在后臺(tái)運(yùn)行時(shí),請(qǐng)考慮使用 WorkManager 來(lái)計(jì)劃和啟動(dòng)工作。
精確的鬧鐘權(quán)限
為了鼓勵(lì)應(yīng)用節(jié)省系統(tǒng)資源,Android 12 要求為以 Android 12 為目標(biāo)平臺(tái)且設(shè)置精確的鬧鐘的應(yīng)用配置“鬧鐘和提醒”特殊應(yīng)用訪問(wèn)權(quán)限。
如需獲取這種特殊應(yīng)用訪問(wèn)權(quán)限,請(qǐng)?jiān)谇鍐沃姓?qǐng)求 SCHEDULE_EXACT_ALARM 權(quán)限。
精確的鬧鐘只能用于面向用戶的功能,如可接受的用例部分中所述的某種情況。
用戶和系統(tǒng)均可撤消“鬧鐘和提醒”特殊應(yīng)用訪問(wèn)權(quán)限。為您的應(yīng)用撤消“鬧鐘和提醒”特殊應(yīng)用訪問(wèn)權(quán)限后,您的應(yīng)用會(huì)停止,并且將來(lái)的所有精確的鬧鐘都會(huì)取消。
向您的應(yīng)用授予“鬧鐘和提醒”特殊應(yīng)用訪問(wèn)權(quán)限后,系統(tǒng)會(huì)向其發(fā)送 ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED 廣播。您的應(yīng)用應(yīng)實(shí)現(xiàn)廣播接收器,以便執(zhí)行以下操作:
- 確認(rèn)您的應(yīng)用仍具有特殊應(yīng)用訪問(wèn)權(quán)限。為此,請(qǐng)調(diào)用 canScheduleExactAlarms()。
- 根據(jù)應(yīng)用的當(dāng)前狀態(tài),重新調(diào)度它所需的任何精確的鬧鐘。此邏輯應(yīng)與您的應(yīng)用接收 ACTION_BOOT_COMPLETED 廣播時(shí)所執(zhí)行的操作類似。
如果您的應(yīng)用嘗試使用設(shè)置精確的鬧鐘的 API 但未被授予特殊應(yīng)用訪問(wèn)權(quán)限,會(huì)發(fā)生 SecurityException。
<table> <tr> <td bgcolor=#FEEFE3><font color=#D26B4A size =3 face ="黑體" > 注意:當(dāng)系統(tǒng)觸發(fā)應(yīng)用設(shè)置的精確的鬧鐘時(shí),設(shè)備會(huì)消耗大量的資源(如電池續(xù)航時(shí)間),特別是在它必須離開(kāi)低電耗模式才能觸發(fā)鬧鐘時(shí)。此外,系統(tǒng)無(wú)法輕松地對(duì)這些請(qǐng)求進(jìn)行批處理,以便更高效地使用資源。</font></td></tr></table>
考慮一下應(yīng)用的用例是否確實(shí)需要精確的鬧鐘,如可接受的用例部分中所述的某種情況。如需執(zhí)行時(shí)間更長(zhǎng)的工作或需要接入網(wǎng)絡(luò)的工作,請(qǐng)使用 WorkManager 或 JobScheduler。如需在設(shè)備處于低電耗模式的同時(shí)執(zhí)行工作,請(qǐng)使用 setAndAllowWhileIdle() 創(chuàng)建不精確的鬧鐘,并從該鬧鐘啟動(dòng)作業(yè)。
精確的鬧鐘和不精確的鬧鐘
您的應(yīng)用在調(diào)用如下方法時(shí)設(shè)置精確的鬧鐘:
相比之下,您在調(diào)用如下方法時(shí)設(shè)置不精確的鬧鐘:
此權(quán)限的可接受用例
只有在應(yīng)用中面向用戶的功能需要精確計(jì)時(shí)的操作時(shí),例如在以下情況下,應(yīng)用才應(yīng)使用精確的鬧鐘,并聲明關(guān)聯(lián)的權(quán)限和廣播接收器:
- 您的應(yīng)用是鬧鐘應(yīng)用或計(jì)時(shí)器應(yīng)用。
- 您的應(yīng)用可讓用戶調(diào)度精確計(jì)時(shí)的操作,如任務(wù)和事件的通知。
Android 12 認(rèn)為精確的鬧鐘是對(duì)時(shí)間敏感的緊急中斷。因此,精確的鬧鐘不受新的前臺(tái)服務(wù)啟動(dòng)限制的影響。
讓用戶授予應(yīng)用訪問(wèn)權(quán)限
如有必要,您可以將用戶引導(dǎo)至系統(tǒng)設(shè)置中的鬧鐘和提醒屏幕,如圖 2 所示。為此,請(qǐng)完成以下步驟:
- 在應(yīng)用的界面中,向用戶解釋為什么您的應(yīng)用需要調(diào)度精確的鬧鐘。
- 調(diào)用包含 ACTION_REQUEST_SCHEDULE_EXACT_ALARM intent 操作的 intent。
啟用行為變更
如需啟用行為變更以進(jìn)行測(cè)試,請(qǐng)執(zhí)行以下某項(xiàng)操作:
- 在開(kāi)發(fā)者選項(xiàng)設(shè)置屏幕中,選擇應(yīng)用兼容性變更。在顯示的屏幕上,點(diǎn)按應(yīng)用的名稱,然后開(kāi)啟 REQUIRE_EXACT_ALARM_PERMISSION。
- 在開(kāi)發(fā)機(jī)器上的終端窗口中,運(yùn)行以下命令:
adb shell am compat enable REQUIRE_EXACT_ALARM_PERMISSION PACKAGE_NAME
通知 trampoline 限制
當(dāng)用戶與通知互動(dòng)時(shí),某些應(yīng)用會(huì)啟動(dòng)一個(gè)應(yīng)用組件來(lái)響應(yīng)通知點(diǎn)按操作,該應(yīng)用組件最終會(huì)啟動(dòng)用戶最終看到并與之互動(dòng)的 activity。此應(yīng)用組件被稱為通知 trampoline。
為了改進(jìn)應(yīng)用性能和用戶體驗(yàn),以 Android 12 為目標(biāo)平臺(tái)的應(yīng)用無(wú)法從用作通知 trampoline 的服務(wù)或廣播接收器中啟動(dòng) activity。換言之,當(dāng)用戶點(diǎn)按通知或通知中的操作按鈕時(shí),您的應(yīng)用無(wú)法在服務(wù)或廣播接收器內(nèi)調(diào)用 startActivity()。
當(dāng)您的應(yīng)用嘗試從充當(dāng)通知 trampoline 的服務(wù)或廣播接收器啟動(dòng) activity 時(shí),系統(tǒng)會(huì)阻止該 activity 啟動(dòng),并在 Logcat 中顯示以下消息:
Indirect notification activity start (trampoline) from PACKAGE_NAME, \
this should be avoided for performance reasons.
識(shí)別哪些應(yīng)用組件充當(dāng)通知 trampoline
測(cè)試您的應(yīng)用時(shí),點(diǎn)按通知后,您可以識(shí)別哪個(gè)服務(wù)或廣播接收器在您的應(yīng)用中充當(dāng)通知 trampoline。為此,請(qǐng)查看以下終端命令的輸出:
adb shell dumpsys activity service \
com.android.systemui/.dump.SystemUIAuxiliaryDumpService
輸出的某一部分包含文本“NotifInteractionLog”。此部分包含識(shí)別因點(diǎn)按通知而啟動(dòng)的組件所需的信息。
更新應(yīng)用
如果您的應(yīng)用從充當(dāng)通知 trampoline 的服務(wù)或廣播接收器啟動(dòng) activity,請(qǐng)完成以下遷移步驟:
- 創(chuàng)建一個(gè)與用戶點(diǎn)按通知后看到的 activity 關(guān)聯(lián)的 PendingIntent 對(duì)象。
- 在構(gòu)建通知的過(guò)程中,使用您在上一步中創(chuàng)建的 PendingIntent 對(duì)象。
如需識(shí)別 activity 的來(lái)源,例如為了執(zhí)行日志記錄,請(qǐng)?jiān)诎l(fā)布通知時(shí)使用 extra。對(duì)于集中式日志記錄,請(qǐng)使用 ActivityLifecycleCallbacks 或 Jetpack 生命周期觀察器。
切換行為
在開(kāi)發(fā)者預(yù)覽版計(jì)劃期間測(cè)試您的應(yīng)用時(shí),您可以使用 NOTIFICATION_TRAMPOLINE_BLOCK 應(yīng)用兼容性標(biāo)志啟用和停用此限制。
備份和恢復(fù)
對(duì)于在 Android 12 上運(yùn)行且以其為目標(biāo)平臺(tái)的應(yīng)用,備份和恢復(fù)的工作方式發(fā)生了變化。Android 備份和恢復(fù)有兩種形式:
- 云端備份:用戶數(shù)據(jù)存儲(chǔ)在用戶的 Google 云端硬盤中,以便之后可以在相應(yīng)設(shè)備或新設(shè)備上恢復(fù)。
- 設(shè)備到設(shè)備 (D2D) 傳輸:用戶數(shù)據(jù)直接從用戶的舊設(shè)備發(fā)送到其新設(shè)備,如通過(guò)使用數(shù)據(jù)線。
如需詳細(xì)了解如何備份和恢復(fù)數(shù)據(jù),請(qǐng)參閱通過(guò)自動(dòng)備份功能備份用戶數(shù)據(jù)和使用 Android Backup Service 備份鍵值對(duì)。
D2D 傳輸功能變更
對(duì)于在 Android 12 及更高版本上運(yùn)行且以其為目標(biāo)平臺(tái)的應(yīng)用:
指定 android:allowBackup="false" 會(huì)禁止備份到 Google 云端硬盤,但不會(huì)停用應(yīng)用的 D2D 傳輸這種方式。
使用 XML 配置機(jī)制指定包含和排除規(guī)則不再影響 D2D 傳輸,不過(guò)仍影響 Google 云端硬盤備份。如需指定 D2D 傳輸?shù)囊?guī)則,您必須使用下一部分中所述的新配置。
新的包含和排除格式
在 Android 12 及更高版本上運(yùn)行且以其為目標(biāo)平臺(tái)的應(yīng)用對(duì) XML 配置使用不同的格式。這種格式要求您分別為云端備份和 D2D 傳輸指定包含和排除規(guī)則,從而明確區(qū)分 Google 云端硬盤備份和 D2D 傳輸。
(可選)您還可以使用它來(lái)指定備份的規(guī)則,在這種情況下,系統(tǒng)會(huì)忽略舊配置。
<table> <tr> <td bgcolor= #DBE4FF><font color=#01579B size =3 face ="黑體" >注意:如果您使用新配置格式,您的應(yīng)用將使用新行為,即使您尚未以 Android 12 為目標(biāo)平臺(tái)也是如此。</font></td></tr></table>
XML 格式變更
下面是 Android 11 及更低版本中用于備份和恢復(fù)配置的格式:
<full-backup-content>
<include domain=["file" | "database" | "sharedpref" | "external" |
"root"] path="string"
requireFlags=["clientSideEncryption" | "deviceToDeviceTransfer"] />
<exclude domain=["file" | "database" | "sharedpref" | "external" |
"root"] path="string" />
</full-backup-content>
下面以粗體顯示了格式的變更。
<data-extraction-rules>
<cloud-backup [disableIfNoEncryptionCapabilities="true|false"]>
...
<include domain=["file" | "database" | "sharedpref" | "external" |
"root"] path="string"/>
...
<exclude domain=["file" | "database" | "sharedpref" | "external" |
"root"] path="string"/>
...
</cloud-backup>
<device-transfer>
...
<include domain=["file" | "database" | "sharedpref" | "external" |
"root"] path="string"/>
...
<exclude domain=["file" | "database" | "sharedpref" | "external" |
"root"] path="string"/>
...
</device-transfer>
</data-extraction-rules>
配置的每一部分(<cloud-backup> 和 <device-transfer>)包含僅適用于該特定傳輸類型的規(guī)則。例如,這樣可讓您從 Google 云端硬盤備份中排除某個(gè)文件或目錄,同時(shí)仍在 D2D 傳輸過(guò)程中傳輸該文件或目錄。如果您的文件太大而無(wú)法備份到云端,但可以在設(shè)備之間毫無(wú)問(wèn)題地傳輸,這可能會(huì)很有用。
如果您沒(méi)有為某種特定的備份模式指定規(guī)則(例如,如果缺少 <device-transfer> 部分),則系統(tǒng)會(huì)為除 no-backup 和 cache 目錄之外的所有內(nèi)容完全啟用該模式,如備份的文件中所述。
您的應(yīng)用可以在 <cloud-backup> 部分中設(shè)置 disableIfNoEncryptionCapabilities 標(biāo)志,以確保只有在可以加密時(shí)(例如,當(dāng)用戶具有鎖定屏幕時(shí))備份操作才會(huì)發(fā)生。如果用戶的設(shè)備無(wú)法支持加密,設(shè)置此約束條件可阻止將備份內(nèi)容發(fā)送到云端,但由于不會(huì)將 D2D 傳輸內(nèi)容發(fā)送到服務(wù)器,因此即使在不支持加密的設(shè)備上,D2D 傳輸也會(huì)繼續(xù)執(zhí)行。
應(yīng)用的清單標(biāo)志
通過(guò)在清單文件中使用 android:dataExtractionRules 屬性,將您的應(yīng)用指向新的 XML 配置。當(dāng)您指向新的 XML 配置時(shí),系統(tǒng)會(huì)忽略指向舊配置的 android:fullBackupContent 屬性。以下代碼示例展示了新的清單文件條目:
<application
...
<!-- The below attribute is ignored. -->
android:fullBackupContent="old_config.xml"
<!-- You can point to your new configuration using the new
dataExtractionRules attribute . -->
android:dataExtractionRules="new_config.xml"
...>
</application>
連接性
并發(fā)點(diǎn)對(duì)點(diǎn) + 互聯(lián)網(wǎng)連接
從 Android 12 開(kāi)始,支持并發(fā)點(diǎn)對(duì)點(diǎn)和互聯(lián)網(wǎng)連接的設(shè)備可以保持與對(duì)等設(shè)備和互聯(lián)網(wǎng)提供的主要網(wǎng)絡(luò)的并發(fā) Wi-Fi 連接,從而使用戶體驗(yàn)更加順暢。系統(tǒng)會(huì)自動(dòng)為以 API 級(jí)別 31 及更高級(jí)別為目標(biāo)的所有應(yīng)用啟用此功能。以較低 API 級(jí)別為目標(biāo)的應(yīng)用仍會(huì)體驗(yàn)舊行為,即在連接到對(duì)等設(shè)備之前會(huì)斷開(kāi)主要 Wi-Fi 網(wǎng)絡(luò)。
兼容性
WifiManager.getConnectionInfo() 只能返回一個(gè)網(wǎng)絡(luò)的 WifiInfo。因此,該 API 的行為在 Android 12 中從以下幾個(gè)方面發(fā)生了變化:
- 如果只有一個(gè) Wi-Fi 網(wǎng)絡(luò)可用,則返回其 WifiInfo。
- 如果有多個(gè) Wi-Fi 網(wǎng)絡(luò)可用,并且發(fā)起調(diào)用的應(yīng)用觸發(fā)了點(diǎn)對(duì)點(diǎn)連接,則返回與對(duì)等設(shè)備對(duì)應(yīng)的 WifiInfo。
- 如果有多個(gè) Wi-Fi 網(wǎng)絡(luò)可用,并且發(fā)起調(diào)用的應(yīng)用未觸發(fā)點(diǎn)對(duì)點(diǎn)連接,則返回互聯(lián)網(wǎng)提供的主要連接的 WifiInfo。
為了在支持雙并發(fā) Wi-Fi 網(wǎng)絡(luò)的設(shè)備上提供更好的用戶體驗(yàn),我們建議所有應(yīng)用(特別是觸發(fā)點(diǎn)對(duì)點(diǎn)連接的應(yīng)用)脫離調(diào)用 WifiManager.getConnectionInfo(),而改用 NetworkCallback.onCapabilitiesChanged(),以獲取與用于注冊(cè) NetworkCallback 的 NetworkRequest 匹配的所有 WifiInfo 對(duì)象。從 Android 12 開(kāi)始,棄用了 getConnectionInfo()。
以下代碼示例展示了如何在 NetworkCallback 中獲取 WifiInfo:
val networkCallback = object : ConnectivityManager.NetworkCallback() {
...
override fun onCapabilitiesChanged(
network : Network,
networkCapabilities : NetworkCapabilities) {
val transportInfo = networkCapabilities.getTransportInfo()
if (transportInfo !is WifiInfo) return
val wifiInfo : WifiInfo = transportInfo
...
}
}
為 NFC 付款啟用屏幕關(guān)閉
在以 Android 12 及更高版本為目標(biāo)平臺(tái)的應(yīng)用中,您可以通過(guò)將 requireDeviceScreenOn 設(shè)置為 false,在設(shè)備屏幕未打開(kāi)的情況下啟用 NFC 付款。
供應(yīng)商庫(kù)
供應(yīng)商提供的原生共享庫(kù)
如果應(yīng)用以 Android 12 或更高版本為目標(biāo)平臺(tái),默認(rèn)情況下無(wú)法訪問(wèn)由芯片供應(yīng)商或設(shè)備制造商提供的非 NDK 原生共享庫(kù)。只有在使用 <uses-native-library> 標(biāo)記明確請(qǐng)求時(shí),才能訪問(wèn)這些庫(kù)。
如果應(yīng)用以 Android 11 或更低版本為目標(biāo)平臺(tái),則無(wú)需使用 <uses-native-library> 標(biāo)記。在這種情況下,任何原生共享庫(kù)均可訪問(wèn),而不管它是否為 NDK 庫(kù)。
更新后的非 SDK 限制
Android 12 包含更新后的受限制非 SDK 接口列表(基于與 Android 開(kāi)發(fā)者之間的協(xié)作以及最新的內(nèi)部測(cè)試)。在限制使用非 SDK 接口之前,我們會(huì)盡可能確保有可用的公開(kāi)替代方案。
如果您的應(yīng)用并非以 Android 12 為目標(biāo)平臺(tái),其中一些變更可能不會(huì)立即對(duì)您產(chǎn)生影響。然而,雖然您目前仍可以使用一些非 SDK 接口(具體取決于應(yīng)用的目標(biāo) API 級(jí)別),但只要您使用任何非 SDK 方法或字段,終歸存在導(dǎo)致應(yīng)用出問(wèn)題的顯著風(fēng)險(xiǎn)。
如果您不確定自己的應(yīng)用是否使用了非 SDK 接口,則可以測(cè)試您的應(yīng)用來(lái)進(jìn)行確認(rèn)。如果您的應(yīng)用依賴于非 SDK 接口,您應(yīng)該開(kāi)始計(jì)劃遷移到 SDK 替代方案。然而,我們知道某些應(yīng)用具有使用非 SDK 接口的有效用例。如果您無(wú)法為應(yīng)用中的某項(xiàng)功能找到使用非 SDK 接口的替代方案,應(yīng)請(qǐng)求新的公共 API。
廢棄
隨著每個(gè)版本的發(fā)布,特定的 Android API 可能會(huì)過(guò)時(shí)或需要進(jìn)行重構(gòu),以提供更好的開(kāi)發(fā)者體驗(yàn)或支持新的平臺(tái)功能。在這些情況下,Android 將正式廢棄過(guò)時(shí)的 API,并引導(dǎo)開(kāi)發(fā)者改用新的 API。
廢棄意味著我們已結(jié)束對(duì)這些 API 的正式支持,但它們將繼續(xù)可供開(kāi)發(fā)者使用。本頁(yè)重點(diǎn)介紹此 Android 版本中廢棄的一些 API。如需查看廢棄的其他 API,請(qǐng)參閱 API 差異報(bào)告。
RenderScript
從 Android 12 開(kāi)始,廢棄了 RenderScript API。它們將繼續(xù)正常運(yùn)行,但我們預(yù)計(jì)設(shè)備和組件制造商會(huì)逐漸停止提供硬件加速支持。為充分利用 GPU 加速功能,我們建議停止使用 RenderScript。
Android 播放列表
廢棄了 Android 播放列表。不再維護(hù)該 API,但為了兼容性而保留了當(dāng)前的功能。
我們建議以 m3u 文件的形式讀取和保存播放列表。
廢棄了 Display API
Android 設(shè)備有許多不同的外形規(guī)格,如大屏設(shè)備、平板電腦和可折疊設(shè)備。為了針對(duì)每種設(shè)備適當(dāng)?shù)爻尸F(xiàn)內(nèi)容,您的應(yīng)用需要確定屏幕或顯示屏尺寸。隨著時(shí)間的推移,Android 提供了不同的 API 來(lái)檢索此信息。在 Android 11 中,我們引入了 WindowMetrics API 并廢棄了以下方法:
在 Android 12 中,我們繼續(xù)建議使用 WindowMetrics,并且正在逐步廢棄以下方法:
應(yīng)用應(yīng)使用 WindowMetrics API 查詢其窗口的邊界,或使用 Configuration.densityDpi 查詢當(dāng)前的密度。
請(qǐng)注意,Jetpack WindowManager 庫(kù)包含一個(gè) WindowMetrics 類,該類支持 Android 4.0.1(API 級(jí)別 14)及更高版本。
示例
下面是一些關(guān)于如何使用 WindowMetrics 的示例。
首先,確保您的應(yīng)用可使其 activity 完全可調(diào)整大小。
activity 應(yīng)依賴于來(lái)自 activity 上下文的 WindowMetrics 來(lái)執(zhí)行任何與界面相關(guān)的工作,特別是 WindowManager.getCurrentWindowMetrics()。
如果您的應(yīng)用創(chuàng)建了 MediaProjection,則必須正確地調(diào)整邊界的大小,因?yàn)橥队皶?huì)捕捉顯示內(nèi)容。如果應(yīng)用完全可調(diào)整大小,則 activity 上下文會(huì)返回正確的邊界。
WindowMetrics projectionMetrics = activityContext
.getSystemService(WindowManager.class).getMaximumWindowMetrics();
如果應(yīng)用并非完全可調(diào)整大小,則必須從 WindowContext 實(shí)例查詢邊界,并使用 WindowManager.getMaximumWindowMetrics() 檢索應(yīng)用可用的最大顯示區(qū)域的 WindowMetrics。
Context windowContext = mContext.createWindowContext(mContext.getDisplay(),
TYPE_APPLICATION, null /* options */);
WindowMetrics projectionMetrics = windowContext.getWindowManager()
.getMaximumWindowMetrics();
<table> <tr> <td bgcolor= #DBE4FF><font color=#01579B size =3 face ="黑體" >注意:使用 MediaProjection 的任何庫(kù)也應(yīng)遵循此建議,并查詢應(yīng)用窗口的相應(yīng) WindowMetrics。</font></td></tr></table>