
本文原作者:戀貓de小郭,原文發布於: GSYTech。
Android 12 需要更新適配點並不多,本篇主要介紹最常見的兩個需要適配的點: android:exported 和SplashScreen。android:exportedhttps://developer.android.google.cn/guide/topics/manifest/activity-element#exported
SplashScreenhttps://developer.android.google.cn/guide/topics/ui/splash-screen
「它主要是設置 Activity 是否可由其他應用的組件啟動」,"true"則表示可以,而 "false" 表示不可以。若為 "false",則Activity只能由同一應用的組件或使用同一用戶ID的不同應用啟動。當然不止是 Activity,Service 和 Receiver 也會有 exported 的場景。
一般情況下如果使用了 intent-filter,則不能將 exported 設置為 "false",不然在 Activity 被調用時系統會拋出 ActivityNotFoundException 異常。
相反如果沒有intent-filter,那就不應該把Activity的exported設置為 true,這可能會在安全掃描時被定義為安全漏洞。
而在 Android 12 的平台上,也就是使用 targetSdkVersion 31 時,那麼您就需要注意:如果 Activity、Service 或 Receiver 使用 intent-filter,並且未顯式聲明 android:exported 的值,App 將會無法安裝。這時候您可能會選擇去 AndroidManifest 一個一個手動修改,但是如果您使用的 SDK 或者第三方庫沒有支持怎麼辦?或者您想要打出不同 target 平台的包?這時候下面這段 gradle 腳本可以給您省心:com.android.tools.build:gradle:3.4.3 以下版本/** * 修改 Android 12 因為 exported 的構建問題 */android.applicationVariants.all { variant -> variant.outputs.all { output -> output.processResources.doFirst { pm -> String manifestPath = output.processResources.manifestFile def manifestFile = new File(manifestPath) def xml = new XmlParser(false, true).parse(manifestFile) def exportedTag = "android:exported" ///指定 space def androidSpace = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android') def nodes = xml.application[0].'*'.findAll { //挑選要修改的節點,沒有指定的 exported 的才需要增加 (it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'service') && it.attribute(androidSpace.exported) == null } ///添加 exported,默認 false nodes.each { def isMain = false it.each { if (it.name() == "intent-filter") { it.each { if (it.name() == "action") { if (it.attributes().get(androidSpace.name) == "android.intent.action.MAIN") { isMain = true println("......................MAIN FOUND......................") } } } } } it.attributes().put(exportedTag, "${isMain}") } PrintWriter pw = new PrintWriter(manifestFile) pw.write(groovy.xml.XmlUtil.serialize(xml)) pw.close() } }}com.android.tools.build:gradle:4.1.0 以上版本/** * 修改 Android 12 因為 exported 的構建問題 */android.applicationVariants.all { variant -> variant.outputs.each { output -> def processManifest = output.getProcessManifestProvider().get() processManifest.doLast { task -> def outputDir = task.multiApkManifestOutputDirectory File outputDirectory if (outputDir instanceof File) { outputDirectory = outputDir } else { outputDirectory = outputDir.get().asFile } File manifestOutFile = file("$outputDirectory/AndroidManifest.xml") println("----------- ${manifestOutFile} ----------- ") if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) { def manifestFile = manifestOutFile ///這裡第二個參數是 false ,所以 namespace 是展開的,所以下面不能用 androidSpace,而是用 nameTag def xml = new XmlParser(false, false).parse(manifestFile) def exportedTag = "android:exported" def nameTag = "android:name" ///指定 space //def androidSpace = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android') def nodes = xml.application[0].'*'.findAll { //挑選要修改的節點,沒有指定的 exported 的才需要增加 //如果 exportedTag 拿不到可以嘗試 it.attribute(androidSpace.exported) (it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'service') && it.attribute(exportedTag) == null } ///添加 exported,默認 false nodes.each { def isMain = false it.each { if (it.name() == "intent-filter") { it.each { if (it.name() == "action") { //如果 nameTag 拿不到可以嘗試 it.attribute(androidSpace.name) if (it.attributes().get(nameTag) == "android.intent.action.MAIN") { isMain = true println("......................MAIN FOUND......................") } } } } } it.attributes().put(exportedTag, "${isMain}") } PrintWriter pw = new PrintWriter(manifestFile) pw.write(groovy.xml.XmlUtil.serialize(xml)) pw.close() } } }}這段腳本您可以直接放到 app/build.gradle 下執行,也可以單獨放到一個 gradle 文件之後 apply 引入,它的作用就是:在打包過程中檢索所有沒有設置 exported 的組件,給他們動態配置上exported。這裡有個特殊需要注意的是,因為啟動 Activity 默認就是需要被 Launcher 打開的,所以 "android.intent.action.MAIN" 需要exported 設置為 true。(PS: 應該是用 LAUNCHER 類別,這裡故意用 MAIN)
如果有需要,還可以自己增加判斷設置了"intent-filter"的才配置exported。Android 12 新增加了 SplashScreen 的 API,它包括啟動時的進入應用的動作、顯示應用的圖標畫面,以及展示應用本身的過渡效果。
最好是矢量的可繪製對象,當然它可以是靜態或動畫形式。啟動畫面動畫機制由進入動畫和退出動畫組成。
進入動畫由系統視圖到啟動畫面組成,這由系統控制且不可自定義。退出動畫由隱藏啟動畫面的動畫運行組成。如果要對其進行自定義,可以通過 SplashScreenView 自定義。對其進行自定義https://developer.android.google.cn/guide/topics/ui/splash-screen#customize-animation
SplashScreenViewhttps://developer.android.google.cn/reference/android/window/SplashScreenView

更詳細的介紹這裡就不展開了,有興趣的可以自己看官方的資料:https://developer.android.google.cn/guide/topics/ui/splash-screen首先不管您的 TargetSDK 什麼版本,當您運行到 Android 12 的手機上時,所有的 App 都會增加 SplashScreen 的功能。如果您什麼都不做,那 App 的 Launcher 圖標會變成 SplashScreen 界面的那個圖標,而對應的原主題下 windowBackground 屬性指定的顏色,就會成為 SplashScreen 界面的背景顏色。這個啟動效果在所有應用的冷啟動和熱啟動期間會出現。
關於如何遷移和使用 SplashScreen 可以查閱官方詳細文檔:https://developer.android.google.cn/guide/topics/ui/splash-screen而正常情況下我們可以做的就是:
1. 升級 compileSdkVersion 31、targetSdkVersion 31 & buildToolsVersion '31.0.0'
2. 添加依賴 implementation "androidx.core:core-splashscreen:1.0.0-alpha02"
3. 增加 values-v31 的目錄
4. 添加 styles.xml 對應的主題,例如:
<resources> <style name="LaunchTheme" parent="Theme.SplashScreen"> <item name="windowSplashScreenBackground">@color/splashScreenBackground</item> <!--<item name="windowSplashScreenAnimatedIcon">@drawable/splash</item>--> <item name="windowSplashScreenAnimationDuration">500</item> <item name="postSplashScreenTheme">@style/AppTheme</item></style></resources>5. 給您的啟動Activity添加這個主題,不同目錄下使用不同主題來達到適配效果。
Android 12 更改了可以完全自定義通知外觀和行為,以前自定義通知能夠使用整個通知區域並提供自己的布局和樣式,現在它行為變了。使用 TargetSDK 為 31 的 App,包含自定義內容視圖的通知將不再使用完整通知區域;而是使用系統標準模板。此模板可確保自定義通知在所有狀態下都與其他通知長得一模一樣,例如在收起狀態下的通知圖標和展開功能,以及在展開狀態下的通知圖標、應用名稱和收起功能,與 Notification.DecoratedCustomViewStyle 的行為幾乎完全相同。
Android App Links 是一種特殊類型的 DeepLink,用於讓 Web 直接在 Android 應用中打開相應對應 App 內容而無需用戶選擇應用。使用它需要執行以下步驟:
如何使用可查閱:
https://developer.android.google.cn/training/app-links/verify-site-associations#auto-verification
使用 TargetSDK 為 31 的 App,系統對 Android App Links 的驗證方式進行了一些調整,這些調整會提升應用鏈接的可靠性。
Android App Linkshttps://developer.android.google.cn/training/app-links/verify-site-associations#auto-verification
如果您的App是依靠AndroidAppLinks驗證在應用中打開網頁鏈接,那麼在為AndroidAppLinks驗證添加intent過濾器時,請確保使用正確的格式,尤其需要注意的是確保這些intent-filter包含BROWSABLE類別並支持https方案。大致位置
使用 TargetSDK 為 31 的 App,用戶可以請求應用只能訪問大致位置信息。
如果App請求ACCESS_COARSE_LOCATION但未請求ACCESS_FINE_LOCATION那麼不會有任何影響。
TargetSDK 為 31 的 App 請求 ACCESS_FINE_LOCATION 運行時權限,還必須請求 ACCESS_COARSE_LOCATION 權限。當 App 同時請求這兩個權限時,系統權限對話框將為用戶提供以下新選項:
SameSite Cookie
Cookie 的 SameSite 屬性決定了它是可以與任何請求一起發送,還是只能與同站點請求一起發送。沒有 SameSite 屬性的 Cookie 被視為 SameSite=Lax。
帶有 SameSite=None 的 Cookie 還必須指定 Secure 屬性,這意味着它們需要安全的上下文,需要通過 HTTPS 發送。
站點的 HTTP 版本和 HTTPS 版本之間的鏈接現在被視為跨站點請求,因此除非將 Cookie 正確標記為 SameSite=None; Secure,否則 Cookie 不會被發送。
在WebViewdevtools中切換界面標誌webview-enable-modern-cookie-same-site,可以在測試設備上手動啟用SameSite行為。WebView devtoolshttps://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/developer-ui.md#launching-webview-devtools
切換界面標誌 webview-enable-modern-cookie-same-sitehttps://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/developer-ui.md#Flag-UI
Android 12 在 Android 11 (API 級別 30) 中引入的自動重置權限行為的基礎上進行了擴展:
https://developer.android.google.cn/training/permissions/requesting#auto-reset-permissions-unused-apps
如果 TargetSDK 為 31 的 App 用戶幾個月不打開,則系統會自動重置授予的所有權限並將 App 置於休眠狀態。
更多可以查閱:
https://developer.android.google.cn/topic/performance/app-hibernationPendingIntent 如果沒有指定 FLAG_IMMUTABLE 或 FLAG_MUTABLE 會直接報錯。(TargetSDK 31 下)
大致需要注意的就是這些,基本上其實除了 exproted 和 SplashScreen 之外,其他基本都不怎麼需要適配,事實上 SplashScreen 我個人覺得會很遭產品嫌棄,畢竟 Material Design 在國內的待遇確實有點慘,沒辦法去掉SplashScreen 這點估計需要和產品扯皮一段時間,不過產品和設計一般沒有 Android 手機,何況 Android 12,所以日後再說吧~"開發者說·DTalk" 面向
中國開發者們徵集 Google 移動應用 (apps & games)相關的產品/技術內容。歡迎大家前來分享您對移動應用的行業洞察或見解、移動開發過程中的心得或新發現、以及應用出海的實戰經驗總結和相關產品的使用反饋等。我們由衷地希望可以給這些出眾的中國開發者們提供更好展現自己、充分發揮自己特長的平台。我們將通過大家的技術內容着重選出優秀案例進行谷歌開發技術專家 (GDE)的推薦。
點擊屏末|閱讀原文|即刻報名參與"開發者說·DTalk"
