據(jù)上一篇文又是一個月過去了,雖說金九銀十,但今年的氛圍實在是太冷清了,能有一份工就不錯了吧。但愿美元加息早點結(jié)束,經(jīng)濟(jì)早點好起來~
上一篇所說內(nèi)容是所有 App 安裝到 Android14 設(shè)備上的影響和需要注意的內(nèi)容,本篇接下來就要介紹當(dāng) targetSdkVersion 升級到 34 時,App 需要注意和修改的地方。
1. 核心功能變更
1.1 前臺服務(wù)類型
在 targetSdkVersion >= 34 的情況下,必須為應(yīng)用內(nèi)的每個前臺服務(wù)(Foreground Service)指定至少一種前臺服務(wù)類型。
什么是前臺服務(wù)?
前臺服務(wù)(Foreground Service)是一種特殊類型的服務(wù),用于執(zhí)行與用戶當(dāng)前活動相關(guān)的長時間運行的任務(wù),這些服務(wù)會在系統(tǒng)狀態(tài)欄中顯示通知,以告知用戶應(yīng)用正在前臺執(zhí)行任務(wù),并且正在使用系統(tǒng)資源。在 Android12(API級別31)及更高版本的設(shè)備上,系統(tǒng)對短時間運行的前臺服務(wù)進(jìn)行了優(yōu)化。系統(tǒng)會等待10秒,然后才顯示與前臺服務(wù)相關(guān)聯(lián)的通知,以改善用戶體驗,減少即時通知的干擾。使用時需要在 Manifest 文件中申請 android.permission.FOREGROUND_SERVICE
權(quán)限。
前臺服務(wù)類型是在 Android10 引入的,通過 android:foregroundServiceType
可以指定 <service> 的服務(wù)類型,可供選擇的前臺服務(wù)類型有:
- camera:需要在后臺時繼續(xù)訪問攝像頭,比如支持多任務(wù)處理的視頻聊天應(yīng)用。
- connectedDevice:與需要藍(lán)牙、NFC、IR、USB 或網(wǎng)絡(luò)連接的外部設(shè)備進(jìn)行交互。
- dataSync:數(shù)據(jù)傳輸操作,例如:數(shù)據(jù)上傳或下載、備份與恢復(fù)操作、導(dǎo)入或?qū)С霾僮?、獲取數(shù)據(jù)、本地文件處理、通過網(wǎng)絡(luò)在設(shè)備和云之間傳輸數(shù)據(jù)。(這種類型可能會在后續(xù) Android 版本中廢棄,建議使用
WorkManager
或 user-initiated data transfer jobs 替換) -
health
:用于任何需要長期運行的用例,以支持健身類應(yīng)用程序,如運動追蹤器。 - location:需要位置訪問的長時間運行的用例,例如導(dǎo)航和位置共享。
- mediaPlayback:需要在后臺持續(xù)播放音頻或視頻,或在 Android TV 上支持?jǐn)?shù)字視頻錄制(DVR)功能。
- mediaProjection:使用
MediaProjection
API 可以將內(nèi)容投影到非主顯示器或外部設(shè)備。這些內(nèi)容不一定是專門的媒體內(nèi)容。 - microphone:需要持續(xù)在后臺 (如錄音機(jī)或通信應(yīng)用程序) 進(jìn)行麥克風(fēng)捕獲。
- phoneCall:需要持續(xù)使用
ConnectionService
API 的場景。 -
remoteMessaging
:將短信從一臺設(shè)備轉(zhuǎn)移到另一臺設(shè)備。在用戶切換設(shè)備時,幫助確保用戶消息任務(wù)的連續(xù)性。 -
shortService
:需要快速完成不能打斷或推遲的重要工作;有 5 個特點:1)只能運行較短的時長,大概 3 分鐘;2)不支持粘性前臺服務(wù);3)無法啟動其他前臺服務(wù);4)不需要另外申請?zhí)囟愋偷臋?quán)限,但少不了FOREGROUND_SERVICE
權(quán)限;5)正在運行的前臺服務(wù)不能在 shortService 類型之間切換。超時之后會調(diào)用Service.onTimeout()
,這個 API 是 Android14 新增的,為了避免 ANR 建議實現(xiàn)onTimeout
回調(diào)。 -
specialUse
:如果不是上述所有類型所包含的,則使用這個類型。除了聲明FOREGROUND_SERVICE_TYPE_SPECIAL_USE
前臺服務(wù)類型外,還應(yīng)該在 Manifest 中聲明用例。即需要在 <service> 元素中指定元素,如下所示。在 Google Play Console 中提交應(yīng)用時,這些值和相應(yīng)的用例將被審查。
// code 1
<service android:name="fooService" android:foregroundServiceType="specialUse">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="foo"/>
</service>
-
systemExempted
:預(yù)留給系統(tǒng)應(yīng)用程序和特定的系統(tǒng)集成,以繼續(xù)使用前臺服務(wù)。普通 App 開發(fā)者不用管。
另外,上述 13 種類型中,做有彩色標(biāo)記
的是 Android14 上新增的;其他的則是之前就有的。
舉個常見的后臺播放的例子,先在 Manifest 文件中申明權(quán)限,并設(shè)置好 foregroundServiceType
:
// code 2
<manifest ...>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<application ...>
<service
android:name=".MusicPlayerService"
android:foregroundServiceType="mediaPlayback"
android:exported="false">
</service>
</application>
</manifest>
然后再去實現(xiàn) MusicPlayerService 類,主要就是在 onStartCommand
回調(diào)中打開通知并開始播放音樂:
// code 3
class MusicPlayerService : Service() {
private var mediaPlayer: MediaPlayer? = null
override fun onBind(intent: Intent?): IBinder? {
return null
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val NOTIFICATION_CHANNEL_ID = "com.example.foregroundservice"
val notificationManager = NotificationManagerCompat.from(this)
// If the notification supports a direct reply action, use
// PendingIntent.FLAG_MUTABLE instead.
val pendingIntent: PendingIntent =
Intent(this, NotificationFullActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(
this, 0, notificationIntent,
PendingIntent.FLAG_IMMUTABLE
)
}
// 創(chuàng)建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name: CharSequence = "Notification Channel Name"
val description = "Description of Notification Channel"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance)
channel.description = description
notificationManager.createNotificationChannel(channel)
}
// 構(gòu)建通知
val builder: NotificationCompat.Builder =
NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_lock_idle_alarm)
.setContentTitle("音樂播放中")
.setContentText("藝術(shù)家 - 音樂")
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
// 啟動通知
val notificationId = 1 // 每個通知的唯一標(biāo)識符
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
notificationManager.notify(notificationId, builder.build())
}
// Notification ID cannot be 0.
startForeground(notificationId, builder.build())
// 播放音樂
mediaPlayer = MediaPlayer.create(this, R.raw.music1)
mediaPlayer?.isLooping = true
mediaPlayer?.start()
return START_STICKY
}
}
最后就是啟動這個 Service 了:
// code 4
requireActivity().startForegroundService(Intent(requireActivity(), MusicPlayerService::class.java))
如果沒在 Manifest 文件中寫明類型,那么在調(diào)用 startForeground()
方法時將會拋出 MissingForegroundServiceTypeException
異常。
對于上面的示例代碼需要額外注意的是,在 Android13 及以上的手機(jī)上彈出 Notification 通知時,需要動態(tài)申請 android.permission.POST_NOTIFICATIONS
權(quán)限,當(dāng)然還需要需要創(chuàng)建一個 NotificationChannel 渠道,這在 Android8 及以上就已經(jīng)要求了。
再說回前臺服務(wù),上述每個前臺服務(wù)類型所需要的權(quán)限是不一樣的,并且這些權(quán)限都被定義成了普通權(quán)限,在默認(rèn)情況下是已經(jīng)授予的,用戶不能撤銷這些權(quán)限。例如 Camera 服務(wù)類型,需要在 Manifest 文件中聲明 FOREGROUND_SERVICE_CAMERA
權(quán)限,并在運行時申請 Camera 權(quán)限。其他的服務(wù)類型都是如此:
Foreground Service Type | Manifest requirements | Runtime requirements |
---|---|---|
Camera | FOREGROUND_SERVICE_CAMERA | CAMERA |
Connected device | FOREGROUND_SERVICE_CONNECTED_DEVICE | 在 Manifest 聲明 CHANGE_NETWORK_STATE or CHANGE_WIFI_STATE or CHANGE_WIFI_MULTICAST_STATE or NFC or TRANSMIT_IR 或者請求運行時權(quán)限 BLUETOOTH_CONNECT or BLUETOOTH_ADVERTISE or BLUETOOTH_SCAN or UWB_RANGING 或者調(diào)用 UsbManager.requestPermission() |
Data sync | FOREGROUND_SERVICE_DATA_SYNC | - |
Health | FOREGROUND_SERVICE_HEALTH | 在 Manifest 聲明 HIGH_SAMPLING_RATE_SENSORS 或申請運行時權(quán)限 (BODY_SENSORS or ACTIVITY_RECOGNITION) |
Location | FOREGROUND_SERVICE_LOCATION | ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION |
MediaPlayback | FOREGROUND_SERVICE_MEDIA_PLAYBACK | - |
Media projection | FOREGROUND_SERVICE_MEDIA_PROJECTION | 需要在之前調(diào)用 createScreenCaptureIntent() |
Microphone | FOREGROUND_SERVICE_MICROPHONE | RECORD_AUDIO |
Phone call | FOREGROUND_SERVICE_PHONE_CALL | 在 Manifest 聲明 MANAGE_OWN_CALLS |
Remote messaging | FOREGROUND_SERVICE_REMOTE_MESSAGING | - |
Short service | - | - |
Special use | FOREGROUND_SERVICE_SPECIAL_USE | - |
System exempted | FOREGROUND_SERVICE_SYSTEM_EXEMPTED | - |
當(dāng)然,肯定有特殊情況。如果自己的前臺服務(wù)與上面提到的這 13 種都不太相關(guān),那么官方建議將這些服務(wù)遷移到 WorkManager
或者 user-initiated data transfer jobs.
user-initiated data transfer jobs 就是由用戶發(fā)起的數(shù)據(jù)傳輸任務(wù)。此 API 是 Android14 新增的,適用于需要由用戶發(fā)起的持續(xù)時間較長的數(shù)據(jù)傳輸,例如從遠(yuǎn)程服務(wù)器下載文件。這些任務(wù)需要在通知欄中顯示一個通知,會立即啟動,并且可能在系統(tǒng)條件允許的情況下長時間運行。我們可以同時運行多個由用戶發(fā)起的數(shù)據(jù)傳輸作業(yè)。
小結(jié):如果目前應(yīng)用中已用到了前臺服務(wù),且 targetSdkVersion 想升到 34,那么就得添加這個前臺服務(wù)的類型;否則不用管。
1.2 藍(lán)牙連接的權(quán)限變更
在 Android14 上,調(diào)用 BluetoothAdapter
的 getProfileConnectionState()
API 時必須申請 BLUETOOTH_CONNECT
權(quán)限,以前不是必須的,現(xiàn)在必須在 Manifest 文件中聲明,并且在運行時向用戶申請該權(quán)限。
很明顯 Android 這幾年逐漸在回收一些系統(tǒng)權(quán)限,對于開發(fā)者來說更加麻煩了,但有利于廣大的使用者。
1.3 OpenJDK 17 更新
Android14 繼續(xù)更新 Android 的核心庫,使其與最新的 OpenJDK LTS 版本的特性、功能保持一致,包括對庫的更新以及對應(yīng)用和平臺開發(fā)人員的 Java17 語言的支持。以下的一些變化可能會影響應(yīng)用的兼容性:
- 正則表達(dá)式的變更:有些正則表達(dá)式已經(jīng)更改,及時檢查應(yīng)用中使用了正則表達(dá)式的地方,查看是否出錯。可以在開發(fā)者選項中關(guān)閉兼容模式,方便將有問題的地方查找出來,具體的兼容模式開關(guān)在 系統(tǒng) > 高級 > 開發(fā)者選項 > 應(yīng)用兼容性變更 這里(原生系統(tǒng)在這里,其他廠商就不好說了),并在 list 中選中自己的 App 即可關(guān)閉或打開。在這里就是需要在 list 中滑到最底下的
Enabled for targetSdkVersion >= 34
的地方,找到 DISALLOW_INVALID_GROUP_REFERENCE 選項切換; - UUID 處理:在驗證輸入?yún)?shù)時,
java.util.UUID.fromString()
方法會進(jìn)行更嚴(yán)格的檢查,因此可能會在反序列化時拋出IllegalArgumentException
異常。自測方法同上,需要在 應(yīng)用兼容性變更 下把 ENABLE_STRICT_VALIDATION 選項切換一下; - ProGuard 出現(xiàn)的問題:在一些情況下使用
ProGuard
進(jìn)行壓縮,混淆,優(yōu)化代碼時,在添加了java.lang.ClassValue
之后會出現(xiàn)問題。此問題是因為一個 Kotlin 庫改變了運行時的行為,即在執(zhí)行Class.forName("java.lang.ClassValue")
是否會返回一個 class 而引發(fā)的,如果應(yīng)用是針對沒有java.lang.ClassValue
的舊版本開發(fā)的,那么這些優(yōu)化會從java.lang.ClassValue
派生的類中刪除computeValue
方法。
小結(jié):JDK17 雖然會向下兼容,但有空還是升級一下比較好,畢竟有許多新的寫法和優(yōu)化。
2. 安全性
Android14 對安全性也有了更高的要求,這也是近幾年來 Google 一直在關(guān)注的方向。
2.1 對隱式 Intent 和 PendingIntent 的限制
隱式 Intent(Implicit Intent)是 Android 應(yīng)用程序組件之間進(jìn)行通信的一種機(jī)制,它不明確指定要啟動哪個組件,而是聲明要執(zhí)行的操作。系統(tǒng)會查找能夠處理這個操作的組件,并啟動它們。隱式 Intent 主要用于在應(yīng)用程序內(nèi)或與其他應(yīng)用程序之間觸發(fā)各種操作,如啟動活動、啟動服務(wù)、發(fā)送廣播等。比較常見的例子就是先在 Manifest 文件中設(shè)置某個 Activity 中 intent-filter 的 action,然后可以通過設(shè)置啟動 Intent 中的 action 來匹配這個 Activity 從而啟動它.
- 隱式 Intent 只能傳遞給
android:exported="true"
的組件(四大組件:Activity、Service···)。所以在 App 中使用 Intent 傳遞數(shù)據(jù)要么使用顯式 Intent 傳遞給android:exported="false"
的組件;要么使用隱式 Intent 傳遞給android:exported="true"
的組件。當(dāng)然顯式 Intent 傳給 exported=“true” 肯定也是可以的。 - 一個可變的
PendingIntent
必須設(shè)置 packageName,否則會拋出異常。
舉個栗子:
// code 5
<!-- android:exported 設(shè)置為false,隱式 Intent 無法啟動 -->
<activity
android:name=".AppActivity"
android:exported="false">
<intent-filter>
<action android:name="com.example.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
// code 6
// Throws an exception when targeting Android 14. 直接用隱式的 Intent 調(diào)用不管用
context.startActivity(Intent("com.example.action.SEND"))
// This makes the intent explicit. 當(dāng)設(shè)置了 intent 中的 package 參數(shù)時就可以了
val explicitIntent =
Intent("com.example.action.SEND")
explicitIntent.apply {
`package` = context.packageName
}
context.startActivity(explicitIntent)
小結(jié):這個變更得重視,特別是 android:exported 設(shè)置為 false 的組件,啟動這些組件可能會崩潰,需要修改。這個更新還是為了安全,因為這些更改可以防止惡意應(yīng)用攔截應(yīng)用內(nèi)部組件使用的隱式 Intent 。
2.2 動態(tài)廣播接收器必須指定導(dǎo)出的行為
動態(tài)注冊的廣播接收器必須設(shè)置一個標(biāo)記,用于表明接收器是否被導(dǎo)出到設(shè)備上的所有 App。標(biāo)記位是 RECEIVER_EXPORTED
或 RECEIVER_NOT_EXPORTED
。早在 Android13 就引入了這個功能,可以讓應(yīng)用程序指定一個已注冊的廣播接收器是否應(yīng)該被導(dǎo)出,并對設(shè)備上的其他應(yīng)用可見。
只不過在 Android14 上變成了“必須設(shè)置”。而在以前的 Android 版本中,設(shè)備上的任何應(yīng)用都可以向動態(tài)注冊的廣播接收器發(fā)送未受保護(hù)的廣播,除非該接收器有簽名許可。
舉個栗子,在 A 應(yīng)用中注冊 AlarmReceiver 并發(fā)送廣播:
// code 7
val filter = IntentFilter("alarmReceiver_custom_action")
val listenToBroadcastsFromOtherApps = true
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
ContextCompat.RECEIVER_EXPORTED // 該接收器對其他應(yīng)用開放
} else {
ContextCompat.RECEIVER_NOT_EXPORTED // 該接收器不對其他應(yīng)用開放
}
// 這里的 registerReceiver 方法必須設(shè)置 receiverFlags 參數(shù)
registerReceiver(requireContext(), AlarmReceiver(), filter, receiverFlags)
// 發(fā)送廣播
val intent = Intent("alarmReceiver_custom_action") // 方式1
//val intent = Intent(requireActivity(), AlarmReceiver::class.java) // 方式2
requireActivity().sendBroadcast(intent)
在其他的應(yīng)用中只能通過 code7 中的方式1發(fā)送廣播,如果 A 應(yīng)用的 listenToBroadcastsFromOtherApps
設(shè)置為 true,那么在 A 應(yīng)用就能收到其他應(yīng)用通過方式1發(fā)送的廣播信息了,否則無法收到。
在實踐中還發(fā)現(xiàn),如果 A 應(yīng)用也通過方式1發(fā)送自己應(yīng)用內(nèi)部的廣播,且設(shè)置 ContextCompat.RECEIVER_NOT_EXPORTED,那么這個廣播是無法收到的,感興趣的同學(xué)可以試試。
如果應(yīng)用程序只是通過 Context#registerReceiver
方法 (比如 Context#registerReceiver()
)為系統(tǒng)廣播注冊接收器,那么它可以不在注冊接收器時指定該標(biāo)志。
小結(jié):動態(tài)廣播的注冊方法改了,需要設(shè)置是否對其他應(yīng)用可見,這跟 android:exported 的設(shè)置是一樣的道理。其實本地廣播和全局廣播的功能和這個一樣,只不過在 targetSdkVersion >= 34 上更加重視了。
2.3 更安全的動態(tài)代碼加載
所有動態(tài)加載的文件都必須標(biāo)記為只讀。否則,系統(tǒng)將拋出異常。官方建議應(yīng)用盡可能避免動態(tài)加載代碼,因為這樣做會大大增加應(yīng)用被代碼注入或代碼篡改破壞的風(fēng)險。
如必須動態(tài)加載代碼,則需要將動態(tài)加載的文件(如 DEX、JAR 或 APK 文件)在文件打開并寫入任何內(nèi)容之前設(shè)置為只讀:
// code 8
val jar = File("DYNAMICALLY_LOADED_FILE.jar")
val os = FileOutputStream(jar)
os.use {
// Set the file to read-only first to prevent race conditions
jar.setReadOnly()
// Then write the actual file content
}
val cl = PathClassLoader(jar.absolutePath, parentClassLoader)
此外,為防止系統(tǒng)對現(xiàn)在已有的動態(tài)加載文件拋出異常,官方建議先刪除并重新創(chuàng)建文件,然后再嘗試在應(yīng)用中重新動態(tài)加載這些文件。重新創(chuàng)建文件時,請按照上述指南在寫入時將文件標(biāo)記為只讀?;蛘?,可將現(xiàn)有文件重新標(biāo)記為只讀,但在這種情況下,官方建議先驗證文件的完整性(例如,對照可信值檢查文件的簽名)以保護(hù)應(yīng)用免遭惡意操作的影響。
2.4 Zip 路徑遍歷
針對 Android14 的應(yīng)用,Android 系統(tǒng)通過以下方式防止 Zip 路徑遍歷的漏洞:如果 zip 文件條目名稱包含 “…” 或以 “/” 開頭,則 ZipFile(String)
和 ZipInputStream.getNextEntry()
會拋出一個 ZipException
異常。
如果不想拋出異常且文件名稱又不能改,可以通過調(diào)用 dalvik.system.ZipPathValidator.clearCallback()
選擇退出驗證。當(dāng)然這是不推薦的。
Zip 路徑遍歷漏洞:指惡意攻擊者通過構(gòu)造含有 “…/” 或以 “/” 開頭的文件路徑,在解壓縮 Zip 文件時可以訪問 Zip 文件之外的文件系統(tǒng)上的任意文件或目錄,從而對應(yīng)用程序造成安全風(fēng)險的漏洞。
2.5 后臺啟動 Activity 新增限制
在 Android14 上系統(tǒng)進(jìn)一步限制了 App 從后臺啟動 Activity 的情況:
- 當(dāng) App 使用
PendingIntent#send()
或類似方法發(fā)送PendingIntent
時,必須選擇是否要授予自己的后臺 Activity 啟動的權(quán)限來發(fā)送PendingIntent
。如果選擇授權(quán),則需要通過setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
方法返回并傳遞一個ActivityOptions
對象。 - 當(dāng)一個前臺可見應(yīng)用使用
bindService()
方法綁定另一個后臺應(yīng)用的 Service 時,這個可見應(yīng)用現(xiàn)在必須選擇是否將自己的后臺 Activity 啟動權(quán)限授予被綁定的服務(wù)。如果選擇授權(quán),應(yīng)用在調(diào)用bindService()
方法時需要設(shè)置BIND_ALLOW_ACTIVITY_STARTS
標(biāo)志。
這些變化擴(kuò)展了現(xiàn)有的限制集,通過防止惡意應(yīng)用程序濫用 API 從后臺啟動破壞性 Activity 來保護(hù)用戶。
小結(jié):針對后臺啟動控制得更嚴(yán)格了,如果項目中有相關(guān)邏輯,建議跑一跑看能否后臺啟動,如有問題再對照上面的內(nèi)容進(jìn)行修改,硬啃實在是不知道說的啥意思。。。
3. 有關(guān)限制非 SDK 接口的更新
Android14 更新了受限的非 SDK 接口列表(基于與 Android 開發(fā)者之間的協(xié)作以及最新的內(nèi)部測試使用的 API 列表)。在限制使用非 SDK 接口之前,官方會盡可能確保有可用的公開替代方案。
如果應(yīng)用并非以 Android14 為目標(biāo)平臺,其中一些變更可能不會立即對應(yīng)用產(chǎn)生影響。但只要 App 使用任何非 SDK 方法或字段,終歸存在導(dǎo)致應(yīng)用出問題的顯著風(fēng)險。
一般而言,公共 SDK 接口是在 Android 框架 軟件包索引(https://developer.android.google.cn/reference/packages) 中記錄的那些接口。非 SDK 接口的處理是 API 抽象出來的實現(xiàn)細(xì)節(jié),因此這些接口可能會在不另行通知的情況下隨時發(fā)生更改。
如果不確定自己的應(yīng)用是否使用了非 SDK 接口,則可以在 Debug 模式下運行測試 App,如果該應(yīng)用訪問了某些非 SDK 接口,系統(tǒng)就會輸出一條日志消息??梢詸z查應(yīng)用的日志消息,查找以下詳細(xì)信息:
1)聲明的類、名稱和類型(采用 Android 運行時所使用的格式);
2)訪問方式:鏈接、反射或 JNI;
3)所訪問的非 SDK 接口屬于哪個名單;
還可以使用 adb logcat 來查看這些日志消息,這些消息顯示在所運行應(yīng)用的 PID 下。舉例而言,日志中可能包含如下條目:
Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)
如果應(yīng)用依賴于非 SDK 接口,應(yīng)該開始計劃遷移到 SDK 的替代方案。如果無法為應(yīng)用中的某項功能找到使用非 SDK 接口的替代方案,應(yīng)向官方請求新的公共 API。
如需查看 Android14 的所有非 SDK 接口的完整列表,可下載查看以下文件:hiddenapi-flags.csv(https://dl.google.com/developers/android/udc/non-sdk/hiddenapi-flags.csv?hl=zh-cn),這個表格文件內(nèi)容很多,可用于查詢。
小結(jié):普通應(yīng)用開發(fā)者一般情況下也不會用到非 SDK 接口,這個可忽略。
以上就是本篇的所有內(nèi)容,可以看出,現(xiàn)有的 App 如果直接將 targetSdkVersion 升級到 34(Android14)的話還是有些地方需要注意并進(jìn)行修改測試的。如果還想了解 Android14 新增了哪些功能,歡迎關(guān)注我,咱們下篇見!
更多內(nèi)容,歡迎關(guān)注公眾號:修之竹
或者查看 修之竹的 Android 專輯文章來源:http://www.zghlxwxcb.cn/news/detail-773430.html
贊人玫瑰,手留余香!歡迎點贊、轉(zhuǎn)發(fā)~ 轉(zhuǎn)發(fā)請注明出處~文章來源地址http://www.zghlxwxcb.cn/news/detail-773430.html
參考文獻(xiàn)
- Android 14 官方文檔 https://developer.android.com/about/versions/14
- https://developer.android.google.cn/about/versions/14/behavior-changes-14?hl=zh-cn
- Android 14 快速適配要點; 戀貓de小郭; https://juejin.cn/post/7231835495557890106?searchId=202307240025039D8229C74EA62159077B
- https://developer.android.google.cn/guide/components/foreground-services
- https://developer.android.com/about/versions/14/changes/user-initiated-data-transfers?hl=zh-cn
- https://developer.android.google.cn/about/versions/14/changes/fgs-types-required
- https://developer.android.google.cn/about/versions/13/features#runtime-receivers
- https://developer.android.google.cn/about/versions/14/changes/non-sdk-14?hl=zh-cn
到了這里,關(guān)于Android14 適配之——targetSdkVersion 升級到 34 需要注意些什么?的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!