Android 14 应用适配指南:https://dev.mi.com/distribute/doc/details?pId=1718
Android 14 功能和变更列表 ?|? Android 开发者 ?|? Android Developers
https://developer.android.com/about/versions/14/overview#timeline
1.2 小米手机升级Android 14
现在Xiaomi 13、Xiaomi 13 Pro、Xiaomi Pad 6 可通过链接,线刷基于Android? 14 Beta 1的MIUI 14开发者预览版。
https://web.vip.miui.com/page/info/mio/mio/detail?postId=40403123&fromPathname=mioSingleBoard&app_version=dev.230112
如您暂未拥有上述设备也没关系,我们为您提供了足量的云测设备,来支持广大开发者的适配工作,云测平台详见:https://testit.miui.com/android_14 (云测平台使用权限申请:https://m.beehive.miui.com/Ooq42P35TPAIP-sZJT1EqA)
开发者持有Pixel系列的机器可以直接ota升级,或者下载镜像升级,具体见链接:
https://developer.android.google.cn/about/versions/14/download
在Android Studio中,可按照如下方式安装Android 14 SDK:
点击OK,安装SDK。
如需访问 Android 14 API 并测试您的应用与 Android 14 的兼容性,请打开模块级build.gradle或build.gradle.kts文件,并使用 Android 14所对应的值对它们进行更新:如何设置这些值的格式取决于您所使用的 Android Gradle 插件 (AGP) 版本。
注意:如果您尚未准备好完全支持 Android 14,您仍然可以使用可调试的应用、Android 14 设备和兼容性框架来执行应用兼容性测试,而无需更改应用以使其与预览版 SDK 兼容或以此为目标平台。
Android 14 扩展了 Android 13(API level 33)中引入的App级语言功能:
我们在此展示一个动态修改系统设置中可选语言列表的例子:
我们名为multilingual settings的demo App中res/xml/locales_config.xml内容如下:
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en"/>
<locale android:name="en-GB"/>
<locale android:name="fr"/>
<locale android:name="ja"/>
<locale android:name="zh-Hans-MO"/>
<locale android:name="zh-Hant-MO"/>
</locale-config>复制
然后在manifest.xml的application下加入:
android:localeConfig="@xml/locales_config"
复制
打开设置 > 系统 > 语言和输入法 > 应用语言 > multilingual settings,可以看到我们在locals_config.xml中编写的可选语言列表:
若我们在App内通过按钮调用U上新增的setOverrideLocaleConfig方法:
MainActivity#overrideLocalConfig:
public void overrideLocalConfig(View view) {
this.getSystemService(LocaleManager.class).setOverrideLocaleConfig(new LocaleConfig(new LocaleList(Locale.ENGLISH, Locale.GERMAN, Locale.KOREAN, Locale.ITALIAN)));
}复制
按下按钮后,我们回到设置 > 系统 > 语言和输入法 > 应用语言 > multilingual settings,发现:
系统设置中的可选语言列表变成了我们在setOverrideLocaleConfig中的参数所代表的语言。
而在T上,我们没有overrideLocalConfig API,系统设置中的App的可选语言列表只能由res/xml/locales_config.xml指定,一旦App编译完成便无法更改。overrideLocalConfig给了我们动态更改的能力。
3.输入法 (IME) 的App语言可见性:IME 可以利用 getApplicationLocales() 方法检查当前App的语言并将 IME 语言与该语言相匹配。
30 亿人使用带有性别的语言:语法类别(例如名词、动词、形容词和介词)根据交谈或谈论的人和对象的性别而变化的语言。传统上,许多性别语言使用阳性语法性别作为默认或通用性别。
以错误的语法性别称呼用户,例如以男性语法性别称呼女性,会对他们的表现和态度产生负面影响。相比之下,具有正确反映用户语法性别的语言的 UI 可以提高用户参与度并提供更加个性化和自然的用户体验。
为了帮助开发者为性别语言构建以用户为中心的 UI,Android 14 引入了语法性别变形 API,从而无需重构应用即可添加对语法性别的支持。
本特性的调用较为简单,首先在Android Studio中,在res文件夹下新建命名如values-fr-feminine的文件夹,在里面新建strings.xml,并仅将涉及语法性别的文本包含进去。本例中,不涉及语法性别的法语文本应被放进res/values-fr/strings.xml中。官方文档可见为带有语法性别的语言添加翻译。
然后,我们即可为应用设定语法性别:
this.getSystemService(GrammaticalInflectionManager.class).setRequestedApplicationGrammaticalGender(Configuration.GRAMMATICAL_GENDER_FEMININE);复制
上面的代码将应用的语法性别设为阴性,即假定用户为女性。
要获取App的语法性别int值,可以:
int gender = this.getSystemService(GrammaticalInflectionManager.class).getApplicationGrammaticalGender();复制
Android 14上共有4种与语法性别相关的int值,分别为:
Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED, Configuration.GRAMMATICAL_GENDER_NEUTRAL, Configuration.GRAMMATICAL_GENDER_FEMININE, Configuration.GRAMMATICAL_GENDER_MASCULINE复制
2.3 地区偏好
区域偏好使用户能够个性化温度单位、一周的第一天和编号系统。居住在美国的欧洲人可能更喜欢以摄氏度而不是华氏度为单位的温度单位,并且希望应用将星期一视为一周的开始,而不是美国默认的星期日。
Android 14为用户提供了一个集中的位置来更改这些应用偏好。这些偏好也会在备份和恢复过程中持续存在。多个 API 和intent(例如 getTemperatureUnit 和 getFirstDayOfWeek)授予应用读取用户偏好的权限,因此应用可以调整其显示信息的方式。开发者还可以在 ACTION_LOCALE_CHANGED 上注册 BroadcastReceiver 以在区域偏好更改时收到广播。
要找到这些设置,请打开“Settings”并导航至System > Languages & input > Regional preferences。
2.4 分享表自定义行为和改善直接共享目标的排名
Android 14更新了系统的分项表,可支持自定义app行为,并且为用户提供更多有用的预览信息。
在Android 14,app可以在系统分享表中自定义行为。在分享表中,可借助ChooserAction.Builder来构建自定义ChooserAction,指定ChooserActions的列表作为使用Intent.createChooser创建的Intent的Intent.EXTRA_CHOOSER_CCUSTOM_ACTIONS。
以下是创建自定义行为的一般过程
以发送多张图片为例
//创建Intent
Intent shareIntent1 =new Intent();
shareIntent1.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent1.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
shareIntent1.setType("image/*");//各种类型的图像
Intent shareIntent = Intent.createChooser(shareIntent1,null);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//添加自定义行为的intent
PendingIntent customAction = PendingIntent.getBroadcast(
mContext,
1,
new Intent(CHOOSER_CUSTOM_ACTION_BROADCAST_ACTION),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent fakeCustomAction = PendingIntent.getBroadcast(
mContext,
1,
new Intent("some_action"),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent fakeCustomAction2 = PendingIntent.getActivity(
mContext,
1,
new Intent(CHOOSER_CUSTOM_ACTION_BROADCAST_ACTION),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
//创建action
ChooserAction[] actions = new ChooserAction[] {
createChooserAction("act1", fakeCustomAction2),
createChooserAction("act2", fakeCustomAction),
createChooserAction("act3", fakeCustomAction),
createChooserAction("act4", fakeCustomAction),
createChooserAction("act5", customAction),
};
PendingIntent modifyShare = PendingIntent.getBroadcast(
mContext,
1,
new Intent(CHOOSER_CUSTOM_ACTION_BROADCAST_ACTION),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
//创建修改分享媒体资源的action
ChooserAction modifyShareAction = new ChooserAction.Builder(
Icon.createWithResource(
mContext.getPackageName(),
R.drawable.img),
modifyShareLabel,
modifyShare
).build();
//将自定义行为存到intent里
shareIntent.putExtra(Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION, modifyShareAction);
mContext.startActivity(shareIntent);复制
Android 14中为了给用户提供更有用的结果,其使用更多来自app的标签来决定直接分享对象的排名。为了提供更有用的标签,当用户向联系人发送消息时,通过使用相应的快捷方式调用pushDynamicShortcut来报告快捷方式的使用情况。并通过调用ShortcutInfoCompat.Builder#addCapabilityBinding(“actions.intent.SEND_MESSAGE”)将相应的功能“actions.intent.SEND.MESSAGE”附加到该快捷方式。
以下方法可有助于提高直接共享目标的排名
Android 13在开发者选项中引入了预测性返回动画。当在开发者选项中开启受支持的应用使用时,向后滑动会显示一个动画,指示返回手势退出应用程序并返回主屏幕。
Android 14中对预测性返回进行了多项改进并添加多项新的功能:
可以通过设置android:enableOnBackInvokedCallback=true为每个Activity而不是整个应用设置预测性返回系统动画。
添加了新的系统动画,以配合Android 13的返回主屏幕动画。新的系统动画是跨活动和跨任务的,在迁移到预测性返回后退出系统动画后自动获得。
为Bottom sheets、Side sheets和Search添加了新的动画。
为创建自定义动画和过渡提供了设计指南。
添加了很多新的API支持自定义应用内部过渡动画:
在OnBackPressedCallback添加了handleOnBackStarted、handleOnBackProgressed和handleOnBackCancelled。
在OnBackAnimationCallback添加了onBackStarted、onBackProgressed和onBackCancelled。
在用户滑动返回时的响应,请使用overrideActivityTransition而不是overridePendingTransition。
在新发布的Android 14预览版中,预测性返回所有的功能仍然保留在开发者选项中。请参阅开发者者指南进行支持应用的预测性返回手势,同样借助开发者指南进行创建自定义应用内部过渡动画。
Android 14引入了几个新的PackageInstaller API,以改善应用商店的用户体验。
2.6.1 在下载前请求用户同意
一般情况下,安装或更新应用需要得到用户的同意。当安装器利用REQUEST_INSTALL_PACKAGES权限尝试去安装一个新的应用,在Android 14以前的版本中,应用可以在APKs写入到session,且已提交session的后面向用户申请权限。
从Android 14开始,借助requestUserPreapproval()方法可以让安装器在提交session前申请用户的同意。这一改善让应用商店征得用户安装同意后开始下载APKs。并且一旦用户同意安装后,应用商店就可在后台进行下载和安装,这期间不会干扰用户的正常使用,有利于改善用户体验。
安装者借助新的方法setRequestUpdateOwnership()向系统表明它要对未来它安装的应用的更新负责。此功能强制执行了更新所有权,意味着只有更新所有者才能安装应用程序的自动更新。强制认定更新所有者有助于确保用户仅从预期的应用商店接收更新。
任何其他的安装者,包括使用INSTALL_PACKAGES权限的,都必须明确收到用户的同意才能安装更新。如果用户决定从其他地方更新应用,拥有更新所有权的安装者将失去更新所有权。
应用商店应该避免去更新用户正在使用的应用,这是因为更新操作可能会杀死正在使用的应用,会导致中断用户正在做的事情。
从Android 14开始,IntallConstraints API提供给用户一个方式确保应用在一个合适的时间进行更新。例如,应用商店可通过调用commitSessionAfterInstallConstraintsAreMet()方法来确保提交更新时用户不再与应用交互。
通过拆分,可将一整个APK按功能拆分成几个分散的APK文件。拆分APKs可让应用商店优化应用不同组件的发布。例如,应用商店可能基于目标硬件的特性来进行优化。自API 22,PackageInstaller API开始支持拆分APK操作。
在Android 14中,安装者借助setDontKillApp()方法声明当安装新的被拆分APK的部分组成时,不应该杀死正在运行应用的进程。当用户正在使用应用时,应用商店可以借助这个功能来无缝安装应用新的部分内容。
2.7 监测用户截屏行为
为了创建标准化的屏幕截图检测体验,Android 14引入了一个隐私保护的屏幕截屏检测API。这个API允许应用程序在每一个活动的基础上进行注册回调。当Activity在可见的情况下进行截屏时,Activity会调用这些回调,并通知用户一些信息。
API使用流程
<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);
}复制
注意:截屏方式触发回调函数执行的条件:
Android的Path API是一种强大而灵活的机制,用于创建和渲染矢量图形,能够绘制或填充路径,并且通过构建线段或者二次曲线或者三次曲线路径,然后执行相应的运算来获得更复杂的形状。其中路径对象的内部信息对调用方是不透明的。
创建一个路径,可通过方法moveTo()、lineTo()和cubicTo()等方法添加路径片段。但是对整个路内部的每一段内容无法访问,因此你必须要在创建路径开始的时候保存相关信息。
从Android 14开始,你可以查询每段路径的内容。路径内容都保存在PathIterator对象里面,该对象可通过Path.getPathIterator方法获得。
Path path = new Path();
path.moveTo(1.0F, 1.0F);
path.lineTo(2.0F, 2.0F);
path.close();
PathIterator pathIterator = path.getPathIterator();复制
通过以上代码可获取PathIterator的对象,该对象包含了路径片段的信息。通过PathIterator的迭代器可访问PathIterator的对象的路径信息。
while (pathIterator.hasNext()) {
PathIterator.Segment segment = pathIterator.next();
Log.i(LOG_TAG, "segment: " + segment.getVerb() + ", " + segment.getPoints());
}复制
通过查询路径可用来对路径进行插值。即通过两条内部结构相同的路径,通过interpolate()插值的方法来生成一条新的路径。通过两条构建两条路径path和path2,借助插值方法interpolate()得到resultPath,resultPath是由path和path2线性插值得到,插值因子是0.8f。三条路径如下图所示。
Path path = new Path();
path.lineTo(300, 1200);
Path path2 = new Path();
path2.moveTo(300f,600f);
path2.lineTo(600, 1200);
Path resultPath = new Path();
if(path.isInterpolatable(path2)){
path.interpolate( path2, 0.8f, resultPath);
}复制
注意:两条路径能够进行插值,需要两条路径的内部结构相同。即路径中点的数量相同;对应路径片段构建的方法相同,比如都是用lineTo()方法构建;以及曲线权重相同(目前使用权重的方法是conicTo(float x1, float y1, float x2, float y2, float weight))。
3.1行为变更:所有应用
3.1.1 设置精确闹钟权限默认关闭
精确闹钟适用于需要在精确时间发生的用户意图的通知或操作。
SCHEDULE_EXACT_ALARM 是 Android 12 中引入的允许应用安排准确闹钟的权限,该权限不再预先授予大多数targetSDK>= Android 14的新安装App(默认设置为拒绝)。如果用户通过备份还原操作将应用数据传输到运行Android 14的设备上,权限仍然会被拒绝。如果现有的应用已经拥有此权限,它将在设备升级到 Android 14 时预先授予。
需要 SCHEDULE_EXACT_ALARM 权限才能通过以下 API 启动准确的警报,否则将抛出 SecurityException:
SCHEDULE_EXACT_ALARM 权限的现有最佳实践仍然适用,包括以下内容:
在U上,App即使在AndroidManifest.xml里申明了
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />复制
在安装后系统也不会自动授予android.permission.SCHEDULE_EXACT_ALARM权限。若App没有该权限,在调用AlarmManager#setExact,AlarmManager#setAlarmClock和AlarmManager#setExactAndAllowWhileIdle三个设置精确闹钟的方法时,系统会抛出 SecurityException 。
若App想要获取该权限,需要用户到应用信息中或Settings->Apps->Special app access->Alarms & reminders中手动打开:
3.1.1.1 受影响的应用
如果设备运行的是 Android 14 或更高版本,此更改将影响新安装的具有以下特征的应用:
日历或闹钟应用需要在应用不再运行时发送日历提醒、唤醒警报或警报。这些应用可以请求 USE_EXACT_ALARM 普通权限。 USE_EXACT_ALARM 权限将在安装时授予,拥有此权限的应用程序将能够像具有 SCHEDULE_EXACT_ALARM 权限的应用程序一样安排确切的警报。小米应用商店会加强对此权限的管控。
3.1.1.3 可能不需要精确闹钟的使用场景
由于 SCHEDULE_EXACT_ALARM 权限现在默认被拒绝,并且权限授予过程需要用户执行额外的步骤,因此强烈建议开发人员评估他们的用例并确定确切的警报是否仍然对他们的用例有意义。
以下列表显示了可能不需要确切警报的常见工作流程:
如果任务需要牢记实时约束,例如明天下午 2:00 或 30 分钟后出发,则 set() 方法很有用。否则,建议改用 postAtTime() 或 postDelayed() 方法。
WorkManager 提供了一种方法来安排对时间敏感的周期性工作。您可以提供重复间隔和 flexInterval(至少 15 分钟)来定义工作的细粒度运行时间。
使用不准确的警报。具体来说,调用 setAndAllowWhileIdle()。
使用不准确的警报。具体来说,调用 set()。
使用不准确的警报。具体来说,调用 setWindow()。请注意,允许的最小窗口长度为 10 分钟。
3.1.1.4 继续使用精确闹钟的迁移步骤
应用程序必须在安排确切的警报之前检查它们是否具有权限。如果应用程序没有权限,则它们必须通过调用意图向用户请求权限。
这与请求特殊权限的标准工作流程相同:
最佳实践:使用alarmManager.canScheduleExactAlarms()首先检查有无SCHEDULE_EXACT_ALARM权限,若无,则使用startActivity向用户申请权限:
boolean canScheduleExactAlarms = mAlarmManager.canScheduleExactAlarms();
if(!canScheduleExactAlarms) {
Intent permissionIntent = new Intent();
permissionIntent.setAction("android.settings.REQUEST_SCHEDULE_EXACT_ALARM");
startActivity(permissionIntent);
}
复制
一些用户会拒绝授予权限。在这种情况下,我们建议应用程序优雅地降低体验,并仍然通过识别其用例来努力提供最佳的回退用户体验。
3.1.1.6 例外
以下类型的应用始终允许调用 setExact() 或 setExactAndAllowWhileIdle() 方法:
SYSTEM_WELLBEING 的角色持有者将被预先授予 SCHEDULE_EXACT_ALARM。
要测试此更改,请从系统设置中的特殊应用访问页面(设置 > 应用 > 特殊应用访问 > 闹钟和提醒)禁用您的应用的闹钟和提醒权限,并观察您的应用的行为。
3.1.2 当应用处于cached状态时,动态注册的广播会入队
Android 14中,当应用处于缓存状态时,系统可以将上下文注册的广播放入到队列中。这类似于Android 12(API级别31)为异步绑定器事务引入的排队行为。在Manifest.xml文件中声明的广播不会排队,并且应用程序会从缓存状态中删除以发送广播。
当应用离开cached状态时,例如回到前台时,此时系统会分发排队的广播。并且某些广播的多个实例会被合成一个。依据其他条件,例如系统运行状况,应用会从缓存状态中删除,并将之前排队的广播进行分发。
3.1.3 三方App只能杀死自己的后台进程
从 Android 14 开始,当App调用 killBackgroundProcesses() 时,该 API 只能杀死自己的后台进程。
如果传入其他App的包名,该方法对该App的后台进程没有影响,Logcat中会出现如下信息:
Invalid packageName: com.example.anotherapp复制
App不应使用 killBackgroundProcesses() API 或以其他方式尝试影响其他App的进程生命周期,即使是在较旧的Android版本上。Android 旨在将缓存的App保留在后台,并在系统需要内存时自动终止它们。如果应用不必要地杀死其他应用,它会降低系统性能并增加电池消耗,因为稍后需要完全重启这些应用,这比恢复现有的缓存应用占用的资源要多得多。
从Android 14开始,targetSdkVersion低于23的应用无法安装。要求应用满足这些最低目标API级别的要求可以提高用户的安全性和隐私性。
恶意软件通常针对较旧级别的API,绕过较新Android版本中引入的安全和隐私保护。例如,一些恶意应用使用targetSdkVersion 22,以避免受到Android 6.0 Marshmallow(API级别23)于2015年引入的运行时权限模型的影响。Android 14这一变化使恶意软件很难避免安全和隐私方面的改进。较低API级别的应用将会安装失败,并且在日志中显示以下消息:
INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 23, but found 7复制
在升级到Android 14的设备上,任何targetSdkVersion低于23的应用都会保持安装状态。
如果需要测试针对旧API级别的应用程序,请使用以下adb命令:
adb install --bypass-low-target-sdk-block FILENAME.apk复制
媒体存储支持查询OWNER_PACKAGE_NAME列,该列表示存储媒体文件的应用包名。从Android 14开始要满足以下两个条件之一,才可以查询到该列的值。
如果想要某程序包名读其他应用可见,那么其他应用必须在Manifest.xml文件中添加<queries>,package的包名是想要申请对方app的包名。
例如,testmediastore想要查询testmediastore2的信息,那么Mainfest.xml文件中<queries>的包名就是testmediastore2的包名。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.mimi.testmediastore">
//.../
<queries>
<package android:name="com.mimi.testmediastore2"/>
</queries>
//../
</manifest>复制
如果应用向用户显示常驻通知,Android 14 允许用户关闭此类通知。
此更改适用于通过 Notification.Builder#setOngoing(true) 或 NotificationCompat.Builder#setOngoing(true) 设置 Notification.FLAG_ONGOING_EVENT 来阻止用户手动删除前台通知的应用。 FLAG_ONGOING_EVENT 的行为已更改为使此类通知实际上可由用户手动删除。
在以下情况下,此类通知仍然不可关闭:
3.1.7 授权访问部分照片或视频
当Android 14上的应用程序请求在Android 13(API级别33)引入的权限READ_MEDIA_IMAGES 或READ_MEDIA_VIDEO时,可以选择授权部分访问权限。
注意:targetSDK≥33,Android 14上的应用都会受到此更改的影响。
3.1.7.1 新的弹窗显示内容
Android 14之前:
访问媒体图片或视频要申请权限READ_MEDIA_IMAGES和READ_MEDIA_VIDEO。当应用申请以上两个权限任何一个的时候,弹出的权限框只有以下两个选项。即允许(允许是允许所有照片)和不允许。
Android 14:
当申请权限READ_MEDIA_IMAGES和READ_MEDIA_VIDEO访问媒体文件的时候,其弹框中有三个选项。Select photos and videos: 选择部分图片允许应用访问
Allow all: 允许访问所有图片
Don't allow: 拒绝所有图片的访问
如果用户选择"SELECT PHOTOS AND VIDEOS",那么稍后应用再次请求READ_MEDIA_IMAGES或READ_MEDID_VIDEO,系统将显示不同的对话框,让用户有机会授予完全访问权限、保留当前选择或授予其他照片和视频。一段时间后,系统会直接显示系统选择器。
为了帮助应用程序支持新的更改,系统引入了一个新的权限READ_MEDIA_VISUAL_USER_SELECTED。根据您的应用程序是否使用新权限,系统的行为会有所不同。
注意:如果您的应用程序已经使用了照片选择器,则无需采取任何措施来支持此更改。否则,请考虑使用照片选择器,而不是采用此更改。
如果没有声明READ_MEDIA_VISUAL_USER_SELECTED权限,则会发生以下现象:READ_MEDIA_IMAGES和READ_MEDI_AVIDEO权限在应用会话期间授予,并且提供临时权限授予和对用户选择的照片和视频的临时访问。 当用户主动杀死应用时,系统最终会拒绝这些权限。这种行为和其他一次性权限一样。
如果应用以后要访问其他照片和视频,则必须手动再次请求READ_MEDIA_IMAGES或READ_MEDIA_VIDEO权限。该系统遵循与初始权限请求相同的流程,提示用户选择照片和视频。
如果应用遵循最佳方案,则此更改不会影响应用。假设应用不保留URI访问、不存储系统权限状态或者在权限更改后不刷新显示的图像集,则情况表现如此。然而,根据应用的不同情况,可能表现不会理想。
注意:你的应用不需要任何媒体权限,就可使用照片选择器或任何系统意图,例如ACTION_GET_CONTENT或ACTION_OPEN_DOCUMENT。
如果声明了READ_MEDIA_VISUAL_USER_SELECTED权限,并且用户选择了系统弹框中的"SELECT PHOTOS AND VIDEOS",接下来会出现以下情况:READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO权限都会被拒绝。
授予应用READ_MEDIA_VISUAL_USER_SELECTED权限,用来临时访问用户已经选择的照片和视频。
如果想要申请其他的照片或视频,你必须要再次申请READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO权限。
注意:在应用中创建一个UI界面,用户必须在重新申请READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO权限前按下该界面。防止用户再次看到系统弹框而感到惊讶。
READ_MEDIA_IMAGES和READ_MEDI_AVIDEO是用户用来访问照片和视频媒体库的其他唯一权限。声明READ_MEDIA_VISUAL_USER_SELECTED会让权限控制器意识到您的应用支持手动重新请求选择更多照片和视频。
为了防止用户看到多个系统权限请求弹框,请在一次操作中申请 READ_MEDIA_VISUAL_USER_SELECTED、ACCESS_MEDIA_LOCATION 和访问媒体的权限(READ_MEDIA_IMAGES或READ_MEDIA_VIDEO或两个一起)。
如果应用所在设备从以前的安卓版本升级到Android 14版本,系统会保持对用户照片和视频的完全访问权限,并自动授予应用一些权限。最终授予的权限取决于设备升级到Android 14之前应用所拥有的权限。
满足以上三个条件,应用仍然有权限访问所有的照片和视频。系统会自动授予应用READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO权限。
满足以上三个条件,应用仍然有权限访问所有的照片和视频。系统会自动授予应用READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO权限。
以下是使用READ_MEDIA_VISUAL_USER_SELECTED权限的最佳方案。
当应用程序进行媒体处理,在后台压缩或上传媒体文件,此时READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO权限处于被拒绝状态。强烈建议添加对READ_MEDIA_VISUAL_USER_SELECTED的支持。或者应用应该通过使用InputStream或使用ContentResolver来进行查询是否可以访问特定的照片或视频。
不要通过SharedPreferences或DataStore永久存储权限状态。存储的权限状态可能和实际状态不同步。权限状态可以在权限重置、应用程序休眠、用户启动的应用程序设置更改或应用进入后台更改。相反,请使用ContextCompat.checkSelfPermission()检查存储权限。
基于Android 14引入的变化,应用可能只能访问部分照片库的内容。当应用查询使用ContentResolver查询缓存媒体的数据,但是缓存的数据可能不是最新的。
通过以下方法可以解决不要依赖存储缓存,要借助ContentResolver直接查询媒体库
当应用在前台时,将结果保存到存储中。
如果用户在系统权限对话框中选择"SELECT PHOTOS AND VIDEOS",则所选取的照片和视频的访问权限的时间是有限的。无论应用有没有权限,应用都应该能处理无法访问任何Uri的情况。
从Android 14开始系统支持200%的字体缩放,为低视力用户提供了符合Web Content Accessibility Guidelines (WCAG)的额外无障碍选项。
为了防止屏幕中大型文本元素缩放过大,系统应用非线性缩放曲线。这种缩放策略意味着大文本的缩放速率与小文本不同。非线性字体缩放有助于保持不同大小元素之间的比例层次,同时缓解高度线性文本缩放问题(例如文本被剪切或由于显示尺寸过大而变的难以阅读)。
如果使用scaled pixel(sp)单位来定义文本大小,那么这些额外的选项和缩放改善将会自动应用于应用的文本。然而,应该在启动最大字体大小(200%)的情况下执行UI测试,确保应用正确使用字体大小,并且可在不影响可用性的情况下适应更大的字体。
启动200%的字体大小,执行以下步骤:
请记住始终以sp为单位来指定文本大小。当应用程序使用sp单位时,Android可以应用用户喜欢的文本大小并适当缩放。
不要使用sp作为view之间的距离或高度单位,以sp单位对于非线性缩放,缩放大小将不成比例。例如4sp+20sp并不等于24sp。
3.1.8.3 换算scaled pixel(sp)单位
使用TypedValue.applyDimension()方法将sp单位转为pixels单位,并且使用TypedValue.deriveDimension()方法将pixels单位转为sp单位。这些方法自动应用适当的非线性缩放曲线。
避免使用Configuration.fontScale或DisplayMetrics.scaledDensity。因为现在字体缩放是非线性的,这些值将变的不再准确。
3.2.1 必须申明前台服务类型
为了帮助开发人员更有意识地定义面向用户的前台服务,Android 10 引入了 android:foregroundServiceType 属性。
如果App以 Android 14 为目标平台,则它必须指定适当的前台服务类型。与之前的 Android 版本一样,可以组合多种类型。可供选择的前台服务类型有:camera、connectedDevice、dataSync、health、location、mediaPlayback、mediaProjection、microphone、phoneCall、remoteMessaging、shortService、specialUse、systemExempted。
如果App中的使用场景与这些类型中的任何一种都不相关,我们强烈建议使用 WorkManager 或user-initiated data transfer jobs。
Android 14新增的前台服务类型为health、remoteMessaging、shortService、specialUse 和 systemExempted 类型。
以下代码片段提供了AndroidManifest.xml中前台服务类型声明的示例:
<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 的应用未在AndroidManifest.xml中定义给定服务的类型,则系统将在为该服务调用 startForeground() 时抛出MissingForegroundServiceTypeException。
3.2.1.1 声明使用前台服务类型的新权限
如果以 Android 14 为目标平台的应用使用前台服务,则它们必须根据前台服务类型声明 Android 14 引入的特定权限。所有权限都定义为normal权限,并默认授予。用户无法撤销这些权限。如果调用 startForeground() 时未声明适当的前台服务类型权限,系统将抛出 SecurityException。
启动前台服务的应用的最佳做法是使用 startForeground() 的重载方法,可以在其中传入前台服务类型的按位整数。可以选择传递一个或多个类型值。
通常,应该只声明特定使用场景所需的类型。这使得更容易满足系统对每种前台服务类型的期望。如果前台服务以多种类型启动,则前台服务必须遵守所有类型的平台实施要求。
但是,如果启动使用以下任何类型的前台服务,则每次为该服务调用 startForeground() 时都应始终包含这些类型:
FOREGROUND_SERVICE_TYPE_CAMERA、FOREGROUND_SERVICE_TYPE_LOCATION、FOREGROUND_SERVICE_TYPE_MICROPHONE。
如果调用中未指定前台服务类型,它将默认为AndroidManifest.xml中定义的值。
简单来说,新特性要求开发者在使用前台服务时,在manifest中声明前台服务类型和所需权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<application
...
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="mediaPlayback" />复制
也可以在启动前台服务时声明类型,但此处所声明的类型应是manifest中类型的子集,且最终前台服务类型跟随startForeground中的声明。
MyService#onStartCommand:
startForeground(222, notification, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);复制
从而启动一个前台服务。
3.2.1.3 系统运行时检查
系统检查是否正确使用了前台服务类型,并确认应用已请求正确的运行时权限或使用所需的 API。例如,系统期望使用前台服务类型 FOREGROUND_SERVICE_TYPE_LOCATION 类型的应用请求 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION。
这意味着应用在向用户请求权限和启动前台服务时必须遵循非常特定的操作顺序。在应用尝试调用 startForeground() 之前,必须请求并授予权限。在前台服务启动后请求适当权限的应用必须更改此操作顺序并在启动前台服务之前请求权限。
注意:如果应用不满足启动前台服务的所有运行时要求,系统会在为该服务调用 startForeground() 后抛出 SecurityException。这会阻止前台服务启动,可能导致正在运行的前台服务从前台进程状态中删除,并可能导致App崩溃。
为了使用给定的前台服务类型,必须在清单文件中声明特定的权限,必须满足特定的运行时要求,并且应用必须满足该类型的一组预期使用场景。以下部分解释了必须声明的权限、运行时要求以及每种类型的预期使用场景。
对正则表达式的更改:现在不允许无效的组引用(Invalid group references),以更紧密地遵循 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 类的旧版本runtime开发的,则这些优化可能会从继承自 java.lang.ClassValue 的类中删除 computeValue 方法。
3.2.3 对于隐式意图和挂起意图的限制
对于针对Android 14的应用程序,Android通过以下方式限制应用程序向内部组件发送隐式意图:
这些变化可防止恶意应用程序拦截应用程序内部组件使用的隐式意图。
例如,下面是应用在manifest文件中声明的意图过滤器:
<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>复制
如果应用尝试使用隐式意图去启动这个活动,则会抛出异常:
// Throws an exception when targeting Android 14.
context.startActivity(new Intent("com.example.action.APP_ACTION"));复制
为了启动没有导出的活动,应用必须使用显示意图:
// This makes the intent explicit.
Intent explicitIntent =
new Intent("com.example.action.APP_ACTION")
explicitIntent.setPackage(context.getPackageName());
context.startActivity(explicitIntent);复制
针对Android 14平台,在其上面使用上下文注册接收器的应用或服务需要声明一个标志,以表明接收器是否导出到其他应用或设备。标志为RECEIVER_EXPORTED或RECEIVER_NOT_EXPORTED。这一要求通过利用Android 13引入的有关接收器的功能,有助于保护应用程序免收安全漏洞的影响。
例如,上下文注册的广播接收器声明RECEIVER_EXPORTED或RECEIVER_NOT_EXPORTED:
// This broadcast receiver should be able to receive broadcasts from other apps.
// This option causes the same behavior as setting the broadcast receiver's
// "exported" attribute to true in your app's manifest.
context.registerReceiver(sharedBroadcastReceiver, intentFilter,
RECEIVER_EXPORTED);
// For app safety reasons, this private broadcast receiver should **NOT**
// be able to receive broadcasts from other apps.
context.registerReceiver(privateBroadcastReceiver, intentFilter,
RECEIVER_NOT_EXPORTED);复制
如果应用利用Context#registerReceiver方法注册了一个接收器,仅对于系统广播,那么该应用不需要声明以上标志。
如果App以 Android 14 为目标平台并使用动态代码加载 (DCL),则所有动态加载的文件都必须标记为只读。否则,系统会抛出异常。我们建议应用尽可能避免动态加载代码,因为这样做会大大增加应用因代码注入或代码篡改而受到危害的风险。
如果必须动态加载代码,请使用以下方法将动态加载的文件(例如 DEX、JAR 或 APK 文件)在文件打开后和写入任何内容之前立即设置为只读:
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);复制
处理已存在的动态加载文件
为防止现有动态加载文件抛出异常,我们建议在尝试在应用中再次动态加载文件之前删除并重新创建这些文件。重新创建文件时,请按照前面的指导在写入时将文件标记为只读。或者,可以将现有文件重新标记为只读,但在这种情况下,我们强烈建议首先验证文件的完整性(例如,通过根据可信值检查文件的签名),以帮助保护应用免受恶意操作。
3.2.6 防止zip文件路径穿透攻击
对于针对 Android 14 的应用,Android 通过以下方式防止 Zip 路径穿透漏洞:如果 zip 文件条目名称包含“..”或以“/”开头,则 ZipFile(String) 和 ZipInputStream.getNextEntry() 会抛出 ZipException。
应用可以通过调用 dalvik.system.ZipPathValidator.clearCallback() 选择关闭此验证。
3.2.7 后台启动Activity的附加限制
对于目标平台是Android 14的应用,系统进一步限制了应用在后台启动Activity的权限。目的是为了防止恶意程序滥用API从后台启动破坏性活动来保护用户。
3.2.7.1 系统允许后台应用启动Activity的情况
Android 14新的限制主要是限制这些情况。
后台启动Activity限制的例外情况主要为:
3.2.7.2.1 新增限制一
当后台应用使用PendingIntent#()send()方法,发送PendingIntent的时候,其相关的参数ActivityOptions应该设置setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)。
使用说明:
// A app创建pendingIntent
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0,
notificationIntent,
PendingIntent.FLAG_MUTABLE,
null);
//通过将pendingIntent发送到B app
//B app收到创建的pendingcontent,然后执行send()方法发送,同时构建ActivityOptions,
//设置相应的模式
PendingIntent pendingIntent = intent.getParcelableExtra("pending");
ActivityOptions options
= ActivityOptions
.makeBasic()
.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
.setPendingIntentCreatorBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
pendingIntent.send(context, 0, null, null, null, null, options.toBundle()); 复制
当一个可见应用程序使用bindService()方法绑定另一个后台应用程序的服务时,如果想要授予绑定的后台服务所在应用启动Activity,必须要在绑定服务的时候,即调用bindService()方法的时候包含BIND_ALLOW_ACTIVITY_STARTS。
使用说明:
targetSdk<Android 14,前台应用绑定后台服务启动Activity需要BIND_AUTO_CREATE
bindService(intent, coon, BIND_AUTO_CREATE);
targetSdk≥Android 14,前台应用绑定后台服务要启动Activity,需要在之前的基础上
添加BIND_ALLOW_ACTIVITY_STARTS标志。
bindService(intent, coon, BindServiceFlags.of(BIND_ALLOW_ACTIVITY_STARTS|BIND_AUTO_CREATE));复制
从 Android 9(API 级别 28)开始,Android 平台对应用能使用的非 SDK 接口实施了限制。只要应用引用非 SDK 接口或尝试使用反射或 JNI 来获取其句柄,这些限制就适用。这些限制旨在帮助提升用户体验和开发者体验,为用户降低应用发生崩溃的风险,同时为开发者降低紧急发布的风险。
如果应用不以 Android 14 为目标平台,其中一些更改可能不会立即产生影响。然而,虽然应用目前可以使用一些非 SDK 接口(取决于应用的 TargetSDK),但使用任何非 SDK 方法或字段总是会带来破坏应用的高风险。
如果不确定应用是否使用非 SDK 接口,可以使用 StrictMode API或veridex进行测试。如果应用依赖于非 SDK 接口,应该开始计划迁移到 SDK 替代方案。尽管如此,我们了解到某些应用具有使用非 SDK 接口的有效使用场景。如果找不到使用非 SDK 接口实现应用功能的替代方案,可以向Google请求一个新的公共 API。
在 Android 11(API level 30)中,任何应用程序都可以使用 Notification.Builder.setFullScreenIntent 在手机锁定时发送全屏intent。您可以通过在 AndroidManifest 中声明 USE_FULL_SCREEN_INTENT 权限在应用安装时自动授予此权限。
全屏intent通知专为需要用户立即关注的极高优先级通知而设计,例如来电或用户设置的闹钟。从 Android 14 开始,允许使用此权限的应用仅限于提供通话和闹钟的应用。
在用户更新到 Android 14 之前,此权限对安装在手机上的应用程序保持启用状态。用户可以打开和关闭此权限。
您可以使用新的 API NotificationManager.canUseFullScreenIntent 来检查App是否具有权限;如果没有,App可以使用新的意图 ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT 来启动设置页面,用户可以在其中授予权限。
3.2.10.1 权限方面的考虑
对于某些权限,系统运行时权限对话框现在包含一个可点击的部分,突出显示您的应用程序的数据共享操作。系统对话框的这一部分包含如下信息,例如您的应用可能决定与第三方共享数据的原因,以及将用户链接到他们可以控制您的应用数据访问的位置。
如果用户在App中分享了他们的位置,并且App随后通过以下方式之一扩大了其位置共享的使用范围,则用户会在 30 天内看到系统通知:
当用户点击此通知时,他们将被带到一个新的位置数据共享更新页面,该页面显示了进行相关更改的应用程序的详细列表,以及更改每个应用程序权限设置的简便方法。下图显示了此流程的示例。
新的位置数据共享更新页面可从设备的Settings > Privacy或者Settings > Security & Privacy页面永久访问,并显示最近增加的位置数据共享。
当某些已安装的应用程序更改其数据共享范围时出现的系统通知
4.迁移指南
在每个Android版本中,都会引入新功能和行为更改,其目的是使Android更有用、更安全且性能更高。在许多情况下,应用在新版本上可直接使用,完全符合预期,但是在某些情况下,需要更新应用以适配平台的变化。
源码发布到AOSP(Android开源项目),用户就可以开始使用新平台。因此让应用做好准备,按用户预期的方式运行,并理想地利用新功能和API发挥平台最大优势。
迁移有两个典型的阶段,可以同时进行:
您必须测试现有应用在Android 14上的运行表现,确保用户在最新Android版本获得良好的体验。有些平台的更改可能会影响到应用的表现。因此,有必要进行全面测试应用并进行必要的调整。
通常您可以调整应用并发布更新,而无需更改应用的targetSdkVersion。同样,根据应用的构建方式及其使用平台功能的不同,您可能也不需要使用新的API或更改应用的compileSdkVersion。
在开始测试强,请务必熟悉应用行为变更。即使不更改应用的targetSdkVersion,这些变更也会影响到您的应用。
执行兼容性测试
大多数情况下,测试Android 14的兼容性与普通应用程序的测试类似。这也是回顾核心应用程序质量指南和测试最佳实践的时机。
要进行测试,请在运行Android 14的设备安装您当前发布的应用,并在查找问题的同时完成所有流程和功能测试。为了集中精力测试,请查看Android 14中引入的所有应用程序的行为变化。这些变化可能会影响您的应用并可能导致应用崩溃。
同时还要确保检查和测试受限制的非SDK接口的使用。您应该将应用使用的任何受限接口替换为公共SDK或等效的NDK接口。留意突出显示这些访问权限的logcat警告,并使用StrictMode方法 detectNonSdkApiUsage()来捕获它们。
最后,请务必完整测试应用中的库和SDK,确保它们在Android 14上面如期运行,并遵循隐私权、性能、UX、用户体验、数据处理和权限方面的最佳实践。如果您遇到问题,请尝试更新到最新版本的SDK,或者联系SDK开发者寻求帮助。
当您完成测试并更新后,我们建议您立即发布兼容的应用。这样可以尽早让您的用户测试应用,并帮助用户顺利过渡到Android 14。
4.2 更新应用的目标平台并使用新的API进行构建
一旦发布应用兼容版本后,下一步通过更新targeSdkVersion并利用Android 14新API和功能来添加对Android 14的全面支持。准备就绪后,即可开始进行这些更新。
当您计划全面支持Android 14的时,请查看以Android 14为目标平台的应用的行为变更。这些针对性的行为变更可能会导致功能问题,然后需要解决这些问题。在某些情况下,这些变更需要进行大量的开发工作,因此我们建议您尽早了解并解决这些问题。为确定影响您的应用的具体的行为变更,请使用兼容性开关来测试已启用所选变更的应用。
以下是全面支持Android 14的步骤。
如需要测试完整的Android 14的支持,请使用最新预览版的Android Studio下载Android 14SDK,以及其他所需要的任何工具。接下来,更新应用的targetSdkVersion和compileSdkVersion,然后重新编译该应用。有关详细信息,请参阅SDK设置指南。
编译应用并将其安装在运行Android 14的设备上后,开始测试,确保应用在Android 14平台上正常运行。某些行为变换仅在应用以新平台为目标平台时适用,因此在开始之前需要查看这些行为变更。
与基本测试一样,完成所有流程和功能以查找问题。将测试重点放在以Android 14为目标平台的行为变更上。您还可以根据核心应用质量指南和测试最佳实践来检查应用。
确保检查和测试可能使用的受限非SDK接口的使用。留意高亮显示这些访问权限的logcat警告,并使用StrictMode方法 detectNonSdkApiUsage()以编程方式来捕获它们。
最后,请务必完整测试应用中的库和SDK,确保它们在Android 14上面如期运行,并遵循隐私权、性能、UX、用户体验、数据处理和权限方面的最佳实践。如果您遇到问题,请尝试更新到最新版本的SDK,或者联系SDK开发者寻求帮助。
Android 14包含兼容性开关,可让您更轻松地在应用中测试针对性的行为变更。对于可调试的应用程序,切换开关可让您:在不实际更改应用程序的targetSdkVersion的情况下测试针对性的变更。您可以切换开关强制启用特定的针对性行为变更,以评估对现有应用的影响。
仅针对特定变更进行测试。您可以使用切换开关停用除了要测试的变更之外的所有针对性变更,而不必一次性处理所有针对性变更。
通过adb管理切换开关。您可以使用adb命令在自动测试环境中启用和停用可切换的变更。
使用标准变更ID更快地进行调试。每个可切换的变更都具有唯一ID和名称,可用于在日志输出中快速调试出根本原因。
在您准备更改应用目标平台时,或者在积极开发以便支持Android 14时,切换开关将会十分有用。如需了解更多信息,请参阅兼容性框架变更(Android 14)。
?