Android 14(API 34)带来了多项行为变更和新特性。
一、核心适配前提
- 升级开发环境:
- Gradle Plugin ≥ 8.0
- Compile SDK ≥ 34
- Target SDK ≥ 34(建议,若暂不升级需兼容行为变更)
- 依赖库升级:确保 AppCompat、Material、Jetpack 等库至最新版本,避免兼容性问题。
二、关键行为变更适配
1. 权限适配
(1)前台服务类型(Foreground Service Type)强制校验
Android 14 要求所有前台服务必须声明具体的 foregroundServiceType,且仅能使用声明的类型,否则会抛出 SecurityException。
适配步骤:
- 步骤1:Manifest 声明前台服务类型
xml
<service
android:name=".MyForegroundService"
android:foregroundServiceType="location|mediaPlayback"> <!-- 根据业务选择类型 -->
<intent-filter>
<action android:name="android.intent.action.FOREGROUND_SERVICE" />
</intent-filter>
</service>
- 步骤2:Kotlin 代码中指定类型启动前台服务
kotlin
class MyForegroundService : Service() {
private val NOTIFICATION_ID = 1001
private val CHANNEL_ID = "foreground_service_channel"
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
createNotificationChannel()
// 构建前台服务通知
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("前台服务标题")
.setContentText("前台服务内容")
.setSmallIcon(R.mipmap.ic_launcher)
.build()
// Android 14+ 必须指定前台服务类型
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForeground(
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION // 与Manifest声明一致
)
} else {
startForeground(NOTIFICATION_ID, notification)
}
return START_STICKY
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"前台服务通道",
NotificationManager.IMPORTANCE_DEFAULT
)
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel)
}
}
override fun onBind(intent: Intent?): IBinder? = null
}
(2)新增权限:POST_NOTIFICATIONS 强制校验
Android 13 引入的通知权限,Android 14 强化了校验,未授权时无法发送通知(包括前台服务通知)。
适配代码:
kotlin
// 检查并请求通知权限
private fun requestNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val notificationManager = getSystemService(NotificationManager::class.java)
if (!notificationManager.areNotificationsEnabled()) {
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
}
startActivity(intent)
// 或通过权限请求弹窗(Android 13+)
/*
ActivityResultContracts.RequestPermission().launch(Manifest.permission.POST_NOTIFICATIONS) { granted ->
if (granted) {
// 权限已授予,启动前台服务
} else {
// 引导用户手动开启
}
}
*/
}
}
}
2. 应用内安装权限适配
Android 14 要求安装 APK 时必须申请 REQUEST_INSTALL_PACKAGES 权限,且新增了 PackageInstaller 的安全校验。
适配代码:
kotlin
// 1. 检查安装权限
private fun checkInstallPermission(context: Context): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.packageManager.canRequestPackageInstalls()
} else {
true // 低版本无需校验
}
}
// 2. 请求安装权限
private fun requestInstallPermission(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !checkInstallPermission(activity)) {
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
data = Uri.parse("package:${activity.packageName}")
}
activity.startActivityForResult(intent, REQUEST_INSTALL_PERMISSION)
}
}
// 3. 使用PackageInstaller安装APK(Android 14推荐方式)
private fun installApkWithPackageInstaller(context: Context, apkPath: String) {
val packageInstaller = context.packageManager.packageInstaller
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId = packageInstaller.createSession(params)
val session = packageInstaller.openSession(sessionId)
// 写入APK文件
val inputStream = FileInputStream(apkPath)
val outputStream = session.openWrite("apk_install", 0, -1)
inputStream.copyTo(outputStream)
session.fsync(outputStream)
inputStream.close()
outputStream.close()
// 提交安装
val intent = Intent(context, InstallResultReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE // Android 12+ 必须加FLAG_IMMUTABLE
)
session.commit(pendingIntent.intentSender)
session.close()
}
// 接收安装结果的广播接收器
class InstallResultReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val status = intent?.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE)
val message = intent?.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
when (status) {
PackageInstaller.STATUS_SUCCESS -> {
// 安装成功
}
else -> {
// 安装失败
}
}
}
}
3. 后台弹出窗口权限适配
Android 14 限制了后台应用弹出窗口,需申请 SYSTEM_ALERT_WINDOW 权限,且仅前台应用可弹出窗口。
适配代码:
kotlin
// 检查悬浮窗权限
private fun checkOverlayPermission(context: Context): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Settings.canDrawOverlays(context)
} else {
true
}
}
// 请求悬浮窗权限
private fun requestOverlayPermission(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !checkOverlayPermission(activity)) {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
data = Uri.parse("package:${activity.packageName}")
}
activity.startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION)
}
}
4. PendingIntent 兼容性适配
Android 14 要求 PendingIntent 必须指定 FLAG_IMMUTABLE 或 FLAG_MUTABLE,否则会抛出异常。
适配代码:
kotlin
// 正确创建PendingIntent(Android 12+ 适配)
private fun createPendingIntent(context: Context): PendingIntent {
val intent = Intent(context, MyReceiver::class.java)
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
return PendingIntent.getBroadcast(context, 0, intent, flags)
}
三、Android 14 新特性接入
1. 实时文本(Live Text)支持
Android 14 新增实时文本 API,可提取图片中的文本,支持中文等多语言。
适配代码:
kotlin
// 提取图片中的文本
private suspend fun extractTextFromImage(context: Context, bitmap: Bitmap): String {
return withContext(Dispatchers.IO) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
val liveTextManager = context.getSystemService(LiveTextManager::class.java)
val result = liveTextManager.processImage(bitmap)
result.text ?: ""
} else {
"" // 低版本降级处理
}
}
}
2. 锁屏小组件(Lock Screen Widgets)
Android 14 支持锁屏添加小组件,需适配小组件的尺寸和交互。
适配代码:
kotlin
// 定义锁屏小组件(继承AppWidgetProvider)
class LockScreenWidget : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
appWidgetIds.forEach { widgetId ->
val views = RemoteViews(context.packageName, R.layout.widget_lock_screen)
// 设置小组件内容
views.setTextViewText(R.id.widget_text, "锁屏小组件示例")
// 适配锁屏尺寸(Android 14新增尺寸)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
views.setInt(R.id.widget_root, "setMinimumWidth", 300)
views.setInt(R.id.widget_root, "setMinimumHeight", 150)
}
appWidgetManager.updateAppWidget(widgetId, views)
}
}
}
// Manifest声明锁屏小组件
<receiver
android:name=".LockScreenWidget"
android:label="锁屏小组件">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_lock_screen_info" />
</receiver>
3. 拍照/录像 API 增强
Android 14 新增 CameraManager.AvailabilityCallback 回调,优化相机设备状态监听。
适配代码:
kotlin
// 监听相机可用性
private fun monitorCameraAvailability(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
val cameraManager = context.getSystemService(CameraManager::class.java)
cameraManager.registerAvailabilityCallback(object : CameraManager.AvailabilityCallback() {
override fun onCameraAvailable(cameraId: String) {
// 相机可用
}
override fun onCameraUnavailable(cameraId: String) {
// 相机不可用
}
}, Handler(Looper.getMainLooper()))
}
}
四、适配注意事项
- 分区适配 :使用
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE做版本判断,避免低版本崩溃。 - 权限申请时机:权限申请需在用户交互时触发(如点击按钮),否则系统会拒绝。
- FLAG_IMMUTABLE :所有
PendingIntent必须指定FLAG_IMMUTABLE(非交互场景)或FLAG_MUTABLE(交互场景)。 - 测试覆盖 :
- 测试 Target SDK 34 下的行为;
- 测试未授予权限时的降级逻辑;
- 测试后台启动、弹窗、安装等场景的权限校验。
- 隐私合规:新增权限需在隐私政策中说明用途,符合应用商店审核要求。
五、参考资源
六、总结
Android 14 的适配核心围绕权限强化 、安全校验 和新 API 接入展开,重点关注前台服务、安装权限、PendingIntent 等关键变更。通过版本判断做分区适配,确保低版本兼容,同时接入实时文本、锁屏小组件等新特性,提升应用体验。
所有代码均基于 Kotlin 编写,适配了 Android 14 的核心变更,可直接集成到项目中,建议根据业务场景调整细节。