Android14(U)適配攻略
1. 升級編譯環(huán)境
開發(fā)環(huán)境
準(zhǔn)備Flamingo 或者最新Bate 版本的Android Studio
準(zhǔn)備Android 14 的設(shè)備((https://developer.android.google.cn/about/versions/14/devices?hl=zh-cn))
gradle配置
classpath 'com.android.tools.build:gradle:8.0.0'
distributionUrl=https://services.gradle.org/distributions/gradle-8.0.0-bin.zip
設(shè)置Android 14 SDK
android {
? ? compileSdkPreview = "UpsideDownCake"
? ? ...
? ? defaultConfig {
? ? ? ? targetSdkPreview = "UpsideDownCake"
? ? }
}
生成BuildConfig和AIDL 關(guān)聯(lián)文件
buildFeatures {
buildConfig = true
aidl = true
}
R文件傳遞
android.nonTransitiveRClass=false
View的id區(qū)分時使用Switc case
使用 if()else if() 替換 switch case 或者在Gradle.properties中添加android.nonFinalResIds=false
設(shè)置命名空間:刪除Manifest下的package標(biāo)簽,將包名信息添加到gradle的android路徑下
android {
namespace = "com.xx.xx"
}
項目中使用CompileOnly 編譯的庫需要在混淆文件中添加間接引用的類TargetSdkVersion >=34的修改
對隱式 intent 和待處理 intent 的限制
對于以 Android 14 為目標(biāo)平臺的應(yīng)用,Android 會通過以下方式限制應(yīng)用向內(nèi)部應(yīng)用組件發(fā)送隱式 intent:
- 隱式 intent 只能傳送到導(dǎo)出的組件。應(yīng)用
必須使用顯式 intent 傳送到未導(dǎo)出的組件帶包名的去跳轉(zhuǎn),或將該組件標(biāo)記為已導(dǎo)出。 - 如果應(yīng)用通過未指定組件或軟件包的 intent 創(chuàng)建可變待處理 intent,系統(tǒng)現(xiàn)在會拋出異常。
這些變更可防止惡意應(yīng)用攔截意在供應(yīng)用內(nèi)部組件使用的隱式 intent。
<activity
android:name=".AppActivity"
android:exported="false"> //false 不可導(dǎo)出 true可導(dǎo)出
<intent-filter>
<action android:name="com.example.action.APP_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Intent explicitIntent =new Intent("com.example.action.APP_ACTION")
//跳轉(zhuǎn)不可導(dǎo)出,需要設(shè)置包名
explicitIntent.setPackage(context.getPackageName());
context.startActivity(explicitIntent);在運(yùn)行時注冊的廣播接收器必須指定導(dǎo)出行為
在 Android 14 上,運(yùn)行時通過 Context#registerReceiver() 動態(tài)注冊廣播接收器,需要設(shè)置標(biāo)記 RECEIVER_EXPORTED 或 RECEIVER_NOT_EXPORTED ,標(biāo)識是否導(dǎo)出該廣播,避免應(yīng)用程序出現(xiàn)安全漏洞,如果注冊的是系統(tǒng)廣播,則不需要指定標(biāo)記。
三方SDK兼容修改方式:
如果三方SDK適配困難可以先在Activity或者Application中復(fù)寫廣播注冊方法,如果有未添加是否可導(dǎo)出標(biāo)記可以手動添加上
@Override
public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED或者Context.RECEIVER_NOT_EXPORTED);
}
@Override
public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter, int flags) {
Intent intent=null;
try {
boolean flagExported = (flags & Context.RECEIVER_EXPORTED) != 0;
boolean flagNotExported = (flags & Context.RECEIVER_NOT_EXPORTED) != 0;
if(!flagExported && !flagNotExported){
intent = super.registerReceiver(receiver, filter, flags|Context.RECEIVER_EXPORTED或者Context.RECEIVER_NOT_EXPORTED);
return intent;
}
intent = super.registerReceiver(receiver, filter,flag);
} catch (Throwable e) {
}
return intent;
}
。。。。。其他多參方法補(bǔ)上EXPORTED 參數(shù)安全的動態(tài)代碼加載(插件化/熱更新)
new DexClassLoad(x,x,x,)
DexFile.loadFile(x) 都會拋異常
在動態(tài)文件(例如 DEX、JAR 或 APK 文件)打開并寫入任何內(nèi)容之前立即將其設(shè)為只讀,插件化、熱修復(fù)涉及ClassLoad去加載代碼的地方,如果文件有寫權(quán)限會立即報錯。
將新下載的文件屬性修改成只讀文件,如果是已存在的文件最好可以先刪除然后重新下載并且設(shè)置只讀權(quán)限。
public static void modifyFileOnlyRead(String apkPath) {
if (Util.isAndroidU()) {
try {
File file = new File(apkPath);
boolean b = file.setReadOnly();
if (file.canWrite()) {
Runtime.getRuntime().exec("chmod 400 " + apkPath);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}壓縮路徑遍歷
對于以 Android 14 為目標(biāo)平臺的應(yīng)用,Android 會通過以下方式防止 Zip 路徑遍歷漏洞:如果 Zip 文件條目名稱包含“..”或以“/”開頭,ZipFile(String) 和 ZipInputStream.getNextEntry() 會拋出 ZipException。
應(yīng)用可以通過調(diào)用 dalvik.system.ZipPathValidator.clearCallback() 選擇停用此驗證。
從后臺啟動Activity的附加限制
系統(tǒng)進(jìn)一步限制了應(yīng)用在后臺啟動 Activity 的時間:
- 當(dāng)應(yīng)用使用
PendingIntent#send()發(fā)送PendingIntent以及類似行為時,如果應(yīng)用想要授予其自己的后臺 service 啟動權(quán)限以啟動 pending intent,則該應(yīng)用現(xiàn)在必須選擇加入一個ActivityOptions,具體為帶有setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED) - 當(dāng)一個可見應(yīng)用使用
bindService()綁定另一個在后臺運(yùn)行的應(yīng)用的服務(wù)時,如果該可見應(yīng)用想要將其自己的后臺 activity 啟動權(quán)限授予綁定服務(wù),則它現(xiàn)在必須選擇加入BIND_ALLOW_ACTIVITY_STARTS標(biāo)志。
啟動前臺服務(wù)必須設(shè)置前臺服務(wù)類型
如果有使用startForeground() 在清單文件中必須要指定服務(wù)的前臺類型,例如音頻播放類的可以使用mediaPlayback
為了幫助開發(fā)者更有目的地定義面向用戶的前臺服務(wù),Android 10 在 <service> 元素內(nèi)引入了 android:foregroundServiceType 屬性。
如果您的應(yīng)用以 Android 14 為目標(biāo)平臺,則必須指定適當(dāng)?shù)那芭_服務(wù)類型。與以前的 Android 版本一樣,可組合使用多個類型。下面列出了可供選擇的前臺服務(wù)類型:
- camera
- connectedDevice
- dataSync
- health
- location
- mediaPlayback
- mediaProjection
- microphone
- phoneCall
- remoteMessaging
- shortService
- specialUse
- systemExempted
如果應(yīng)用中的用例與這些類型均不相關(guān),強(qiáng)烈建議您遷移邏輯以使用 WorkManager 或用戶發(fā)起的數(shù)據(jù)傳輸作業(yè)。
Android 14 中新增了 health, remoteMessaging, shortService, specialUse 和 systemExempted 類型。
以下代碼段提供了一個清單中的前臺服務(wù)類型聲明示例:
<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 為目標(biāo)平臺的應(yīng)用未在清單中定義給定服務(wù)的類型,系統(tǒng)會在調(diào)用 startForeground() 時引發(fā) MissingForegroundServiceTypeException
Open JDK 17
Android 14 將繼續(xù)更新 Android 的核心庫,以與最新 OpenJDK LTS 版本中的功能保持一致,包括適合應(yīng)用和平臺開發(fā)者的庫更新和 Java 17 語言支持。
以下變更可能會影響應(yīng)用兼容性:
- 對正則表達(dá)式的更改:現(xiàn)在,為了更嚴(yán)格地遵循 OpenJDK 的語義,不允許無效的組引用。您可能會看到
java.util.regex.Matcher類拋出IllegalArgumentException的新情況,因此請務(wù)必測試應(yīng)用中使用正則表達(dá)式的情形。如需在測試期間啟用或停用此變更,請使用兼容性框架工具切換DISALLOW_INVALID_GROUP_REFERENCE標(biāo)志。 - UUID 處理:現(xiàn)在,驗證輸入?yún)?shù)時,
java.util.UUID.fromString()方法會執(zhí)行更嚴(yán)格的檢查,因此您可能會在反序列化期間看到IllegalArgumentException。如需在測試期間啟用或停用此變更,請使用兼容性框架工具切換ENABLE_STRICT_VALIDATION標(biāo)志。 - ProGuard 問題:有時,在您嘗試使用 ProGuard 縮減、混淆和優(yōu)化應(yīng)用時,添加
java.lang.ClassValue類會導(dǎo)致問題。問題源自 Kotlin 庫,該庫會根據(jù)Class.forName("java.lang.ClassValue")是否會返回類更改運(yùn)行時行為。如果您的應(yīng)用是根據(jù)沒有java.lang.ClassValue類的舊版運(yùn)行時開發(fā)的,則這些優(yōu)化可能會將computeValue方法從派生自java.lang.ClassValue的類中移除。
Android 14 中有關(guān)限制非 SDK 接口的更新
非SDK API 表 (官網(wǎng)下載文件)
使用 Lint工具檢查
TargetSdkVersion >=33的修改
默認(rèn)拒絕設(shè)定精確的鬧鐘
Android 14 開始 ,targetSdkVersion>=33的新安裝用戶 SCHEDULE_EXACT_ALARM 權(quán)限默認(rèn)拒絕,在使用
setExact()setExactAndAllowWhileIdle()setAlarmClock()
時,APP會拋出異常,可在調(diào)用上述方法之前使用 canScheduleExactAlarms()
判斷是否有鬧鐘權(quán)限,并且設(shè)置AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED 監(jiān)聽前臺廣播并對其做出正確反應(yīng) ,系統(tǒng)會在用戶授予權(quán)限時發(fā)送該廣播。
三種種方式修改:
public static void openAlarmPage(Context context){
//可定義廣播監(jiān)聽 SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED 是否開啟
Intent intent = new Intent();
intent.setAction(ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
context. startActivity(intent);
}Menifest申明
<uses-permissionandroid:name="android.permission.USE_EXACT_ALARM" />這個普通權(quán)限不需要運(yùn)行時申請,但是需要注意隱私聲明public void start () { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { alarmManager.setExact(xxx, xxx, "TAG", new AlarmManager.OnAlarmListener() { @Override public void onAlarm() { start(); } }, xx); } }
授予對照片和視頻的部分訪問權(quán)限
只授予所選中媒體文件的權(quán)限
針對Android 13 及以上用戶 Android 14 以上新增的權(quán)限 READ_MEDIA_VISUAL_USER_SELECTED 僅允許授予選中媒體文件的訪問權(quán)限。如需要訪問所有媒體文件需要同步申請READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO .
TargetSdkVersion==xxx 不區(qū)分版本對所有應(yīng)用的更改
TargetSdkVersion<23的應(yīng)用無法在Android U上安裝
安全性,降低低版本權(quán)限問題導(dǎo)致APP數(shù)據(jù)泄露,升級TargetSdkVersion >=23
INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 23, but found 7
如果需要調(diào)試低版本Target的APP可以使用命令行安裝
adb install --bypass-low-target-sdk-block app.apk
應(yīng)用進(jìn)入緩存時,上下文注冊的廣播將加入隊列
應(yīng)用進(jìn)入緩存狀態(tài)(例如進(jìn)入后臺) 上下文注冊的廣播會加入隊列,等待應(yīng)用退出緩存狀態(tài)時,會一次性將多個在緩存隊列的廣播一次性發(fā)送, 存在廣播合并的情況, 例如監(jiān)聽屏幕開啟/關(guān)閉/開啟/關(guān)閉, 應(yīng)用只能接受開啟-關(guān)閉 中間的狀態(tài)會丟失.,廣播也可能由于系統(tǒng)原因會從緩存隊列刪除. 此項變更讓廣播接收變得不那么靠譜,可能會導(dǎo)致APP資源得不到銷毀引起內(nèi)存泄露
應(yīng)用只能終止自己的后臺進(jìn)程
從 Android 14 開始,當(dāng)您的應(yīng)用調(diào)用 killBackgroundProcesses() 時,該 API 只能終止您自己應(yīng)用的后臺進(jìn)程。(影響第三方殺進(jìn)程的應(yīng)用例如(360手機(jī)衛(wèi)士))
用戶可以關(guān)閉不可關(guān)閉的通知
這項變更適用于通過 Notification.Builder#setOngoing(true) 或 NotificationCompat.Builder#setOngoing(true) 設(shè)置 Notification.FLAG_ONGOING_EVENT 來阻止用戶關(guān)閉前臺通知的應(yīng)用。FLAG_ONGOING_EVENT 的行為已發(fā)生變化,使用戶實際上能夠關(guān)閉此類通知。
在以下情況下,此類通知仍不可關(guān)閉:
- 當(dāng)手機(jī)處于鎖定狀態(tài)時
如果用戶選擇全部清除通知操作(有助于防止意外關(guān)閉)
此外,這一新行為不適用于以下用例中的不可關(guān)閉通知:
使用
MediaStyle創(chuàng)建的通知(notification.setStyle(ew NotificationCompat.MediaStyle()))安全和隱私用例的政策限制使用
企業(yè)設(shè)備政策控制器 (DPC) 和支持軟件包
新增、改善功能
截屏監(jiān)聽
添加權(quán)限: 可以監(jiān)聽截屏但是截屏文件還需要使用常規(guī)方法自己獲取
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
final Activity.ScreenCaptureCallback screenCaptureCallback =
new Activity.ScreenCaptureCallback() {
@Override
public void onScreenCaptured() {
// Add logic to take action in your app.
}
};
@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);
}
@Override
protected void onStop() {
super.onStop();
unregisterScreenCaptureCallback(screenCaptureCallback);
}禁止當(dāng)前ACtivity截屏可以添加下面的flag
activity.getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
Path 路徑可查詢過程中的點位信息
Android 的 Path API 是一種強(qiáng)大且靈活的機(jī)制,可用于創(chuàng)建和渲染矢量圖形,能夠描邊或填充路徑,根據(jù)線段、二次曲線或立方曲線構(gòu)建路徑,執(zhí)行布爾運(yùn)算以獲取更復(fù)雜的形狀,或同時執(zhí)行所有這些操作。但是無法了解 Path 對象中實際包含的內(nèi)容;該對象的內(nèi)部信息在創(chuàng)建后對于調(diào)用方是不透明的。
如需創(chuàng)建 Path,可以調(diào)用 moveTo()、lineTo() 和 cubicTo() 等方法來添加路徑片段。但無法詢問該路徑有哪些片段,因此必須在創(chuàng)建時保留該信息。
從 Android 14 開始,可以查詢路徑以了解其內(nèi)部內(nèi)容。首先,需要使用 Path.getPathIterator API 獲取 PathIterator 對象:
Path path = new Path();path.moveTo(1.0F, 1.0F); path.lineTo(2.0F, 2.0F);path.close(); PathIterator pathIterator = path.getPathIterator();
接下來,可以調(diào)用 PathIterator 逐個遍歷片段,并檢索每個片段的所有必要數(shù)據(jù)。以下示例使用了 PathIterator.Segment 對象,它會打包數(shù)據(jù)
while (pathIterator.hasNext()) { PathIterator.Segment segment = pathIterator.next();
Log.i(LOG_TAG, "segment: " + segment.getVerb() + ", " + segment.getPoints());} PathIterator 還有一個非分配版 next(),可以在其中傳入緩沖區(qū)來保存點數(shù)據(jù)。
查詢 Path 數(shù)據(jù)的一個重要用例是插值。例如,大家可能想在兩個不同的路徑之間添加動畫(或變形)。為了進(jìn)一步簡化該用例,Android 14 針對 Path 還有一個新的 interpolate() 方法。假設(shè)兩個路徑具有相同的內(nèi)部結(jié)構(gòu),interpolate() 方法會使用該插值結(jié)果創(chuàng)建一個新的 Path。以下示例返回了一個形狀介于 path 和 otherPath 之間的一半(線性插值為 0.5)的路徑:
Path interpolatedResult = new Path();
if (path.isInterpolatable(otherPath)) {
path.interpolate(otherPath, 0.5F, interpolatedResult);
}單應(yīng)用語言偏好/語法優(yōu)化/地區(qū)偏好
功能和 API 概覽 | Android 開發(fā)者 | Android Developers (google.cn)
位置權(quán)限新增標(biāo)簽描述
權(quán)限彈窗是會顯示描述信息,需要在清單文件中添加
在應(yīng)用的資源目錄/etc/ASL/asl-locations.xml新增配置, <ASL-locations> <ASL package="com.xx.xx">【Path to ASL XML} </ASL> </ASL-locations>
新增用戶初始化Job權(quán)限RUN_USER_INITIATED_JOBS
Android 14 引入了android.permision.RUN_USER_INITIATED_JOBS權(quán)限,允許一個應(yīng)用執(zhí)行用戶初始化Job權(quán)限,此權(quán)限可以被用手動關(guān)閉或者被系統(tǒng)撤銷,應(yīng)用需要targestsdk升級到U后來請求此權(quán)限
新增喚醒屏幕權(quán)限
Android 14 引入了android.permision.TURN_SCREEN_ON權(quán)限,目標(biāo)平臺為Android 14及以上的應(yīng)用使用喚醒屏幕時,需要在AndroidManifest文件中配置這個權(quán)限。另外喚醒屏幕權(quán)限有一個對應(yīng)的appops權(quán)限AppOpsManager.OP_TURN_SCREEN_ON,這個權(quán)限在Android T上是默認(rèn)開啟,在Android U上對目標(biāo)平臺為Android 14及以上的應(yīng)用默認(rèn)關(guān)閉。
不申請權(quán)限調(diào)用下面API 沒有效果無法喚醒屏幕
PowerManager powerManager = (PowerManager) Context.getSystemService( Context.POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock( PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "testName"); wakeLock.acquire(3000)
字體放大可以到200%
功能和 API 概覽 | Android 開發(fā)者 | Android Developers (google.cn)
到此這篇關(guān)于Android14(U)適配攻略的文章就介紹到這了,更多相關(guān)Android14(U)適配攻略內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android自定義View實現(xiàn)游戲搖桿鍵盤的方法示例
Android進(jìn)階過程中有一個繞不開的話題——自定義View。最近在做項目中又遇到了,所以下面這篇文章主要給大家介紹了利用Android自定義View實現(xiàn)游戲搖桿鍵盤的相關(guān)資料,操作方式類似王者榮耀的搖桿操作,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面來一起看看吧。2017-07-07
Android開發(fā)中Looper.prepare()和Looper.loop()
Looper用于封裝了android線程中的消息循環(huán),默認(rèn)情況下一個線程是不存在消息循環(huán)(message loop)的,具體調(diào)用方法大家可以通過本文學(xué)習(xí)2016-11-11
Android系統(tǒng)中的藍(lán)牙連接程序編寫實例教程
這篇文章主要介紹了Android系統(tǒng)中的藍(lán)牙連接程序編寫實例教程,包括藍(lán)牙的設(shè)備查找及自動配對等各種基礎(chǔ)功能的實現(xiàn),十分給力,需要的朋友可以參考下2016-04-04

