2025-09-08升级问题记录: 升级SDK从Android11到Android12
将 Android 工程的 targetSdkVersion
从 30 (Android 11)升级到 31(Android 12)需要关注一些重要的行为变更和适配点。
主要适配要点:
适配类别 | 关键变更点 | 适配紧迫性 | 简要说明 |
---|---|---|---|
组件导出属性 | 声明了 Intent Filter 的组件必须显式设置 android:exported 属性 | 强制 | 避免组件被意外调用,提升安全性。 |
PendingIntent | 必须显式声明可变性标志 | 强制 | 指定 FLAG_MUTABLE 或 FLAG_IMMUTABLE 以明确意图。 |
前台服务 | 限制从后台启动前台服务 | 强制 | 除特定情况外,应用在后台时无法启动前台服务。 |
自定义通知 | 自定义通知视图使用系统标准模板 | 强制 | 自定义通知的内容区域不再覆盖整个通知区域,需检查布局适配性。 |
应用启动动画 | 引入新的 SplashScreen API | 推荐 | 提供一致的应用启动体验,建议替换自定义启动页。 |
隐私和安全 | 近似位置权限、麦克风和摄像头指示器、剪贴板访问提示等 | 强制/推荐 | 增强用户隐私保护,需适配新的权限模型和提示。 |
1. 组件导出属性 (android:exported)
问题:在 Android 12 中,所有包含了 intent-filter
的 Activity、Service、Broadcast、Receiver 都必须显式声明 android:exported
属性,明确指示该组件是否允许被其他应用调用。
适配方案:检查你的 AndroidManifest.xml
文件,为所有包含 <intent-filter>
的组件添加 android:exported
属性。
<activityandroid:name=".YourActivity"android:exported="true"> <!-- 明确设置 exported 值 --><intent-filter><action android:name="android.intent.action.VIEW" /></intent-filter>
</activity><serviceandroid:name=".YourService"android:exported="false"> <!-- 仅限内部使用 -->
</service><receiverandroid:name=".YourReceiver"android:exported="true"><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter>
</receiver>
注意:android:exported="true"
表示允许外部应用调用,false
则表示不允许。缺乏此声明在 Android 12 及以上版本会导致安装失败或运行时错误,如下:
2. PendingIntent 可变性
问题:Android 12 要求为创建的每个 PendingIntent
对象显式指定其可变性标志:FLAG_MUTABLE
或 FLAG_IMMUTABLE
。
适配方案:检查所有创建 PendingIntent
的代码。
// 创建一个可变的 PendingIntent(通常用于需要被其他应用修改的 Intent)
PendingIntent mutablePendingIntent = PendingIntent.getActivity(context,requestCode,intent,PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE // 添加 FLAG_MUTABLE
);// 创建一个不可变的 PendingIntent(安全性更高,推荐只要不需要修改 Intent 就使用此选项)
PendingIntent immutablePendingIntent = PendingIntent.getActivity(context,requestCode,intent,PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE // 添加 FLAG_IMMUTABLE
);
原则:如果不需要让其他应用修改你的 PendingIntent
所包装的 Intent
,优先使用 FLAG_IMMUTABLE
,这样更安全。
3. 前台服务启动限制
问题:Android 12 开始,应用在处于后台时,通常无法启动前台服务。这是为了节省电量和管理资源。否则会抛出 ForegroundServiceStartNotAllowedException
。
适配方案:
-
评估需求:确认是否必须在后台启动前台服务。许多后台任务可以用 WorkManager 来调度和执行。
-
使用替代方案:优先考虑使用
WorkManager
的加急作业(Expedited Work)来处理需要立即执行的后台任务。
// WorkManager 示例 (使用加急作业)
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(YourWorker.class).setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) // 设置加急.build();
WorkManager.getInstance(context).enqueue(request);
-
检查例外情况:如果你的场景确实符合后台启动前台服务的少数例外条件(例如,由于用户操作,如点击通知、小部件;或者特定的系统事件回调),请确保正确配置并准备好处理可能的异常。
4. 自定义通知样式
问题:Android 12 改变了完全自定义通知的视觉效果。系统会使用一个标准模板来装饰所有通知,自定义布局只能占据模板内指定区域,而不再是整个通知区域。
适配方案:
-
测试通知外观:务必在 Android 12 设备上测试所有自定义通知,确保布局正确显示。
-
使用标准样式:尽可能使用系统的标准通知样式和扩展布局(如
InboxStyle
,BigPictureStyle
),它们能更好地适应不同版本。 -
提供展开式布局:如果必须使用自定义视图,确保同时为展开状态提供布局(
setCustomBigContentView
)。
5. 应用启动画面 (Splash Screen)
问题:Android 12 引入了统一的应用启动画面 API(SplashScreen)。系统会为所有应用默认显示一个启动画面,该画面由应用的启动图标和主题的 windowBackground
组成。
适配方案:
-
接受默认效果:如果不介意默认效果,可以不做任何改动。
-
定制启动画面(推荐):若要自定义,请使用 Jetpack 的 SplashScreen 兼容库,它可以在 Android 12 之前和之后的版本上提供一致的体验。
-
添加依赖:
implementation "androidx.core:core-splashscreen:1.0.1"
-
定义主题,继承
Theme.SplashScreen
。 -
在
AndroidManifest.xml
中将该主题应用于启动 Activity。 -
在 Activity 中安装 Splash Screen。
6. 隐私和安全增强
Android 12 引入了多项隐私改进,部分会影响所有应用,部分仅针对 targetSdkVersion 31+的应用:
-
近似位置权限:用户可以仅授予应用大致位置(模糊位置)权限。如果你的应用需要精确定位,需要在运行时明确向用户解释为何需要,并妥善处理用户只授予大致位置的情况。
-
麦克风和摄像头指示器:当应用使用麦克风或摄像头时,状态栏会出现相应图标8。确保你的应用只在必要时访问这些传感器,并妥善处理用户可能因此产生的疑虑。
-
剪贴板访问提示:当应用读取来自其他应用的剪贴板内容时,系统会显示提示 toast8。避免不必要的剪贴板读取。