Seperti rilis sebelumnya, Android 14 menyertakan perubahan perilaku yang dapat memengaruhi aplikasi Anda. Perubahan perilaku berikut ini berlaku khusus bagi aplikasi yang menargetkan Android 14 (API level 34) atau yang lebih tinggi. Jika aplikasi menargetkan Android 14 atau yang lebih tinggi, Anda harus memodifikasi aplikasi untuk mendukung perilaku ini dengan benar, jika berlaku.
Pastikan Anda meninjau daftar perubahan perilaku yang memengaruhi semua aplikasi
yang berjalan di Android 14, terlepas dari
targetSdkVersion
aplikasi.
Fungsi inti
Jenis layanan latar depan wajib diisi
如果您的应用以 Android 14(API 级别 34)或更高版本为目标平台,则必须为应用中的每个前台服务至少指定一项前台服务类型。您应选择一个能代表应用用例的前台服务类型。系统需要特定类型的前台服务满足特定用例。
如果应用中的用例与这些类型均不相关,强烈建议您迁移逻辑以使用 WorkManager 或用户发起的数据传输作业。
Penerapan izin BLUETOOTH_CONNECT di BluetoothAdapter
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,Android 14 会在调用 BluetoothAdapter
getProfileConnectionState()
方法时强制执行 BLUETOOTH_CONNECT
权限。
此方法已要求 BLUETOOTH_CONNECT
权限,但未强制执行。确保您的应用在应用的 AndroidManifest.xml
文件中声明 BLUETOOTH_CONNECT
,如以下代码段所示,并在调用 getProfileConnectionState
之前检查用户是否已授予相应权限。
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Update OpenJDK 17
Android 14 将继续更新 Android 的核心库,以与最新 OpenJDK LTS 版本中的功能保持一致,包括适合应用和平台开发者的库更新和 Java 17 语言支持。
以下变更可能会影响应用兼容性:
- 对正则表达式的更改:现在,为了更严格地遵循 OpenJDK 的语义,不允许无效的组引用。您可能会看到
java.util.regex.Matcher
类抛出IllegalArgumentException
的新情况,因此请务必测试应用中使用正则表达式的情形。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换DISALLOW_INVALID_GROUP_REFERENCE
标志。 - UUID 处理:现在,验证输入参数时,
java.util.UUID.fromString()
方法会执行更严格的检查,因此您可能会在反序列化期间看到IllegalArgumentException
。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换ENABLE_STRICT_VALIDATION
标志。 - ProGuard 问题:有时,在您尝试使用 ProGuard 缩减、混淆和优化应用时,添加
java.lang.ClassValue
类会导致问题。问题源自 Kotlin 库,该库会根据Class.forName("java.lang.ClassValue")
是否会返回类更改运行时行为。如果您的应用是根据没有java.lang.ClassValue
类的旧版运行时开发的,则这些优化可能会将computeValue
方法从派生自java.lang.ClassValue
的类中移除。
JobScheduler memperkuat perilaku callback dan jaringan
Sejak diperkenalkan, JobScheduler mengharapkan aplikasi Anda untuk kembali dari
onStartJob
atau onStopJob
dalam beberapa detik. Sebelum Android 14,
jika tugas berjalan terlalu lama, tugas akan dihentikan dan gagal secara diam-diam.
Jika aplikasi Anda menargetkan Android 14 (API level 34) atau yang lebih tinggi dan
melampaui waktu yang diberikan di thread utama, aplikasi akan memicu ANR
dengan pesan error "Tidak ada respons untuk onStartJob
" atau
"Tidak ada respons untuk onStopJob
".
ANR ini mungkin disebabkan oleh 2 skenario:
Akun Layanan 1. Ada pekerjaan yang memblokir thread utama, mencegah callback onStartJob
atau onStopJob
agar tidak dieksekusi dan diselesaikan dalam batas waktu yang diharapkan.
2. Developer menjalankan pekerjaan pemblokiran dalam callback
JobScheduler onStartJob
atau onStopJob
, sehingga callback tidak
selesai dalam batas waktu yang diharapkan.
Untuk mengatasi #1, Anda harus men-debug lebih lanjut apa yang memblokir thread utama
ketika ANR terjadi, Anda dapat melakukannya menggunakan
ApplicationExitInfo#getTraceInputStream()
untuk mendapatkan batu nisan
pelacakan saat ANR terjadi. Jika Anda dapat mereproduksi ANR secara manual,
Anda dapat merekam pelacakan sistem dan memeriksa pelacakan menggunakan
Android Studio atau Perfetto untuk lebih memahami apa yang sedang berjalan di
thread utama saat ANR terjadi.
Perhatikan bahwa hal ini dapat terjadi saat menggunakan JobScheduler API secara langsung
atau menggunakan WorkManager library androidx.
Untuk mengatasi #2, pertimbangkan untuk bermigrasi ke WorkManager, yang menyediakan
dukungan untuk menggabungkan pemrosesan apa pun di onStartJob
atau onStopJob
atau dalam thread asinkron.
JobScheduler
juga memperkenalkan persyaratan untuk mendeklarasikan
Izin ACCESS_NETWORK_STATE
jika menggunakan setRequiredNetworkType
atau
Batasan setRequiredNetwork
. Jika aplikasi Anda tidak mendeklarasikan
Izin ACCESS_NETWORK_STATE
saat menjadwalkan tugas dan menargetkan
Android 14 atau yang lebih baru, hal ini akan menghasilkan SecurityException
.
API peluncuran Kartu
对于以 Android 14 及更高版本为目标平台的应用,
TileService#startActivityAndCollapse(Intent)
已弃用,现在会抛出
调用时抛出异常。如果您的应用从功能块启动 activity,请使用
TileService#startActivityAndCollapse(PendingIntent)
。
Privasi
Akses sebagian ke foto dan video
Android 14 memperkenalkan Akses Foto yang Dipilih, yang memungkinkan pengguna memberikan akses ke gambar dan video tertentu di galeri mereka, bukan memberikan akses ke semua media dari jenis tertentu.
Perubahan ini hanya diaktifkan jika aplikasi Anda menargetkan Android 14 (API level 34) atau yang lebih tinggi. Jika Anda belum menggunakan pemilih foto, sebaiknya terapkan di aplikasi Anda untuk memberikan pengalaman yang konsisten dalam memilih gambar dan video yang juga meningkatkan privasi pengguna tanpa harus meminta izin penyimpanan apa pun.
Jika Anda mengelola pemilih galeri Anda sendiri menggunakan izin penyimpanan dan perlu
mempertahankan kontrol penuh atas implementasi Anda, sesuaikan implementasi Anda
untuk menggunakan izin READ_MEDIA_VISUAL_USER_SELECTED
baru. Jika aplikasi Anda
tidak menggunakan izin baru, sistem akan menjalankan aplikasi Anda dalam mode
kompatibilitas.
Pengalaman pengguna
Notifikasi Intent layar penuh yang aman
Dengan Android 11 (API level 30), aplikasi apa pun dapat menggunakan
Notification.Builder.setFullScreenIntent
untuk mengirim intent
layar penuh saat ponsel terkunci. Anda dapat memberikannya secara otomatis saat penginstalan aplikasi dengan
mendeklarasikan izin USE_FULL_SCREEN_INTENT
di
AndroidManifest.
Notifikasi intent layar penuh dirancang untuk notifikasi dengan prioritas sangat tinggi
yang meminta perhatian segera pengguna, seperti setelan
panggilan telepon masuk atau jam alarm yang dikonfigurasi oleh pengguna. Untuk aplikasi yang menargetkan
Android 14 (API level 34) atau yang lebih tinggi, aplikasi yang diizinkan untuk menggunakan
izin ini terbatas pada aplikasi yang hanya menyediakan panggilan dan alarm. Google
Play Store mencabut izin USE_FULL_SCREEN_INTENT
default untuk aplikasi
apa pun yang tidak sesuai dengan profil ini. Batas waktu untuk perubahan kebijakan ini adalah 31 Mei
2024.
Izin ini tetap diaktifkan untuk aplikasi yang diinstal di ponsel sebelum pengguna mengupdate ke Android 14. Pengguna dapat mengaktifkan dan menonaktifkan izin ini.
Anda dapat menggunakan API baru
NotificationManager.canUseFullScreenIntent
untuk memeriksa apakah aplikasi
Anda memiliki izin. Jika tidak, aplikasi Anda dapat menggunakan intent baru
ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT
untuk meluncurkan halaman
setelan tempat pengguna dapat memberikan izin.
Keamanan
Pembatasan ke intent yang implisit dan tertunda
Untuk aplikasi yang menargetkan Android 14 (API level 34) atau yang lebih tinggi, Android membatasi pengiriman intent implisit ke komponen aplikasi internal dengan cara berikut:
- Intent implisit hanya dikirim ke komponen yang diekspor. Aplikasi harus menggunakan intent eksplisit untuk mengirim ke komponen yang tidak diekspor, atau menandai komponen sebagai diekspor.
- Jika aplikasi membuat intent tertunda yang dapat berubah dengan intent yang tidak menentukan komponen atau paket, sistem akan menampilkan pengecualian.
Perubahan ini mencegah aplikasi berbahaya agar tidak mencegat intent implisit yang dimaksudkan untuk digunakan oleh komponen internal aplikasi.
Misalnya, berikut ini filter intent yang dapat dideklarasikan dalam file manifes aplikasi Anda:
<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>
Jika aplikasi Anda mencoba meluncurkan aktivitas ini menggunakan intent implisit, pengecualian ActivityNotFoundException
akan ditampilkan:
Kotlin
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(Intent("com.example.action.APP_ACTION"))
Java
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(new Intent("com.example.action.APP_ACTION"));
Untuk meluncurkan aktivitas yang tidak diekspor, aplikasi Anda harus menggunakan intent eksplisit:
Kotlin
// This makes the intent explicit. val explicitIntent = Intent("com.example.action.APP_ACTION") explicitIntent.apply { package = context.packageName } context.startActivity(explicitIntent)
Java
// This makes the intent explicit. Intent explicitIntent = new Intent("com.example.action.APP_ACTION") explicitIntent.setPackage(context.getPackageName()); context.startActivity(explicitIntent);
Penerima siaran yang terdaftar runtime harus menentukan perilaku ekspor
以 Android 14(API 级别 34)或更高版本为目标平台并使用上下文注册的接收器的应用和服务必须指定以下标志,以指明接收器是否应导出到设备上的所有其他应用:RECEIVER_EXPORTED
或 RECEIVER_NOT_EXPORTED
。此要求有助于利用 Android 13 中引入的这些接收器的功能,来保护应用免受安全漏洞的影响。
仅接收系统广播的接收器的例外情况
如果您的应用仅通过 Context#registerReceiver
方法(例如 Context#registerReceiver()
)针对系统广播注册接收器,那么它在注册接收器时不应指定标志。
Pemuatan kode dinamis yang lebih aman
Jika aplikasi Anda menargetkan Android 14 (level API 34) atau yang lebih tinggi dan menggunakan Pemuatan Kode Dinamis (DCL), semua file yang dimuat secara dinamis harus ditandai sebagai hanya baca. Jika tidak, sistem akan menampilkan pengecualian. Sebaiknya aplikasi menghindari pemuatan kode secara dinamis jika memungkinkan, karena hal itu akan sangat meningkatkan risiko aplikasi disusupi oleh injeksi kode atau modifikasi kode.
Jika Anda harus memuat kode secara dinamis, gunakan pendekatan berikut untuk menetapkan file yang dimuat secara dinamis (seperti file DEX, JAR, atau APK) sebagai file hanya baca, segera setelah file dibuka dan sebelum konten apa pun ditulis:
Kotlin
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, parentClassLoader)
Java
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);
Menangani file yang dimuat secara dinamis dan sudah ada
Agar pengecualian tidak ditampilkan untuk file yang dimuat secara dinamis dan sudah ada, sebaiknya hapus dan buat ulang file sebelum Anda mencoba lagi memuatnya secara dinamis di aplikasi Anda. Saat Anda membuat ulang file, ikuti panduan sebelumnya untuk menandai file sebagai hanya baca pada waktu penulisan. Atau, Anda dapat melabeli ulang file yang ada sebagai hanya baca, tetapi dalam kasus ini, kami sangat menyarankan Anda untuk memverifikasi integritas file terlebih dahulu (misalnya dengan memeriksa tanda tangan file terhadap nilai tepercaya) untuk membantu melindungi aplikasi Anda dari tindakan berbahaya.
Batasan tambahan dalam memulai aktivitas dari latar belakang
Untuk aplikasi yang menargetkan Android 14 (API level 34) atau yang lebih tinggi, sistem akan lebih membatasi kapan aplikasi diizinkan untuk memulai aktivitas dari latar belakang:
- Saat mengirim
PendingIntent
menggunakanPendingIntent#send()
atau metode serupa, aplikasi harus memilih ikut serta jika ingin memberikan hak istimewa peluncuran aktivitas latar belakangnya sendiri untuk memulai intent yang tertunda. Untuk ikut serta, aplikasi harus meneruskan paketActivityOptions
dengansetPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
. - Saat aplikasi yang terlihat mengikat layanan dari aplikasi lain yang ada di latar belakang
menggunakan metode
bindService()
, aplikasi yang terlihat kini harus memilih ikut serta jika ingin memberikan hak istimewa peluncuran aktivitas latar belakangnya ke layanan terikat. Untuk ikut serta, aplikasi harus menyertakan flagBIND_ALLOW_ACTIVITY_STARTS
saat memanggil metodebindService()
.
Perubahan ini memperluas rangkaian pembatasan yang ada untuk melindungi pengguna dengan mencegah aplikasi berbahaya agar tidak menyalahgunakan API untuk memulai aktivitas yang mengganggu dari latar belakang.
Zip path traversal
Untuk aplikasi yang menargetkan Android 14 (API level 34) atau yang lebih tinggi, Android mencegah Kerentanan Zip
Path Traversal dengan cara berikut:
ZipFile(String)
dan
ZipInputStream.getNextEntry()
menampilkan
ZipException
jika nama entri file zip berisi ".." atau dimulai
dengan "/".
Aplikasi dapat memilih untuk tidak mengikuti validasi ini dengan memanggil
dalvik.system.ZipPathValidator.clearCallback()
.
Izin pengguna diperlukan untuk setiap sesi pengambilan MediaProjection
Untuk aplikasi yang menargetkan Android 14 (API level 34) atau yang lebih tinggi, SecurityException
akan ditampilkan oleh MediaProjection#createVirtualDisplay
dalam salah satu skenario
berikut:
- Aplikasi Anda menyimpan dalam cache
Intent
yang ditampilkan dariMediaProjectionManager#createScreenCaptureIntent
, dan meneruskannya beberapa kali keMediaProjectionManager#getMediaProjection
. - Aplikasi Anda memanggil
MediaProjection#createVirtualDisplay
beberapa kali pada instanceMediaProjection
yang sama.
Aplikasi Anda harus meminta pengguna untuk memberikan izin sebelum setiap sesi pengambilan. Satu sesi pengambilan adalah satu pemanggilan di MediaProjection#createVirtualDisplay
, dan setiap instance MediaProjection
hanya boleh digunakan satu kali.
Menangani perubahan konfigurasi
Jika aplikasi Anda perlu memanggil MediaProjection#createVirtualDisplay
untuk menangani
perubahan konfigurasi (seperti perubahan orientasi layar atau ukuran layar),
Anda dapat mengikuti langkah-langkah berikut untuk mengupdate VirtualDisplay
untuk instance
MediaProjection
yang ada:
- Panggil
VirtualDisplay#resize
dengan lebar dan tinggi baru. - Berikan
Surface
baru dengan lebar dan tinggi baru keVirtualDisplay#setSurface
.
Mendaftarkan callback
Aplikasi Anda harus mendaftarkan callback untuk menangani kasus saat pengguna tidak memberikan
izin untuk melanjutkan sesi pengambilan. Untuk melakukannya, terapkan
Callback#onStop
dan minta aplikasi Anda merilis resource terkait (seperti
VirtualDisplay
dan Surface
).
Jika aplikasi Anda tidak mendaftarkan callback ini,
MediaProjection#createVirtualDisplay
akan menampilkan IllegalStateException
saat aplikasi Anda memanggilnya.
Pembatasan non-SDK yang diperbarui
Android 14 包含更新后的受限非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。在限制使用非 SDK 接口之前,我们会尽可能确保有可用的公开替代方案。
如果您的应用并非以 Android 14 为目标平台,其中一些变更可能不会立即对您产生影响。然而,虽然您目前仍可以使用一些非 SDK 接口(具体取决于应用的目标 API 级别),但只要您使用任何非 SDK 方法或字段,终归存在导致应用出问题的显著风险。
如果您不确定自己的应用是否使用了非 SDK 接口,则可以测试您的应用来进行确认。如果您的应用依赖于非 SDK 接口,您应该开始计划迁移到 SDK 替代方案。然而,我们知道某些应用具有使用非 SDK 接口的有效用例。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,应请求新的公共 API。
如需详细了解此 Android 版本中的变更,请参阅 Android 14 中有关限制非 SDK 接口的更新。如需全面了解有关非 SDK 接口的详细信息,请参阅对非 SDK 接口的限制。