Android14 新特性及变更

一、Android 14 基本信息

Android 14 对应的 API 版本是 34 ,内部代号为 UpsideDownCake

一直以来,谷歌都有用甜点名称来命名 Android 版本的传统,遵循的规律是首字母按照字母表的顺序进行迭代。但是在 Android 10 推出时,这一传统有了变化,将 Android 版本重新命名为单纯的数字。但是内部仍然是有一个代号来代表每一个版本的,历史版本以及代号如下表所示

Android version API level 代号
Android 1.5 3 Cupcake
Android 1.6 4 Dout
Android 2.0 /2.0.0/2.1 5/6/7 Eclair
Android 2.2 8 Froyo
Android 2.3/2.3.3 9/10 Gingerbread
Android 3.0/3.1/3.2 11/12/13 Honeycomb
Android 4.0/4.0.3 14/15 IceCreamSandwich
Android 4.1/4.2/4.3 16/17/18 Jelly Bean
Android 4.4/4.4W 19/20 KitKat/KitKat Wear
Android 5.0/5.1 21/22 Lollipop
Android 6.0 23 Marshmallow
Android 7.0/7.1 24/25 Nougat
Android 8.0/8.1 26/27 Oreo
Android 9 28 Pie
Android 10 29 Q (Quince Tart)
Android 11 30 R (Red Velvet Cake)
Android 12/12L 31/32 S (Snow Cone/Snow Cone v2)
Android 13 33 T (Tiramisu)
Android 14 34 U (Upside Down Cake)

1.2 迁移到Android14

适配高版本Android的前提条件 设备已经刷写了Android14版本的系统映像,主要工作是对应用做兼容性测试

  • 设备

对应版本的手机或者模拟器

  • android studio

Android 14 SDK 与低版本的Android studio 有一些不兼容的改动,为了避免这一类问题的导致的报错,官方建议我们把版本替换到Android Studio Flamingo | 2022.2.1 或更高版本
image

  • SDK 获取

在AS 中下载对应的版本,分别在 SDK platform 以及 SDK Tools 中下载34 版本的

二、针对所有应用的变更

2.1 字体缩放

这个属于 无障碍功能 里面的一个子功能。
从 Android 14 开始,系统支持字体放大到 200%,主要是为了更好的为 弱视用户提供使用环境。

image

  • 缩放规则

为防止屏幕上的大文本元素放大过大,系统会采用非线性放大曲线。这种放大策略意味着大号文本的放大比例不会与较小的文本相同。非线性字体缩放有助于保持不同大小元素之间的比例层次结构,同时缓解高级别线性文本缩放的问题(例如文本被截断或文本因超大显示大小而难以阅读)

  • 启用方式

1、设置 > 无障碍 > 显示大小和文字
2、在界面操作 放大或缩小文字

  • 影响范围

主要是对个app (三方&系统)的适配上有影响,app需要调节到最大比例后检查界面显示情况

  • 适配方案

1、以 sp 为单位文本大小
2、使用 TypedValue.applyDimension() 从 sp 单位转换为像素,并使用 TypedValue.deriveDimension() 将像素转换为 sp,这些方法会自动应用适当的非线性缩放曲线。
3、对 lineHeight 使用 sp 单位,以便行高随文本一起缩放。否则,如果您的文本为 sp,而 lineHeight 以 dp 或 px 为单位,则文本无法缩放且看起来狭窄。以 sp 为单位同时定义 textSizelineHeight,TextView 会自动更正 lineHeight

2.2 sdk 版本 >= 33 的变更

2.2.1 默认拒绝设定精确的闹钟

Android 14 开始 ,targetSdkVersion>=33 的新安装用户 SCHEDULE_EXACT_ALARM 权限默认拒绝(此权限是在Android 12 中引入的),在使用以下几个方法

  • setExact()
  • setExactAndAllowWhileIdle()
  • setAlarmClock()

时,会抛出 SecurityException 异常

继续使用精确闹钟需要做以下几个操作:
针对 SCHEDULE_EXACT_ALARM 权限 ,也有对应的处理办法:
1、在调用上述几个方法之前,先调用 AlarmManager.canScheduleExactAlarms() 来检查权限
2、无权限。则调用 包含 ACTION_REQUEST_SCHEDULE_EXACT_ALARM 以及应用软件包名称的 intent,要求用户授予权限,代码示例:

AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// permission is granted
if (alarmManager.canScheduleExactAlarms()) {
   alarmManager.setExact(...);
} else {
   // 申请权限
   startActivity(new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM));
}

3、调用 AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED 来让应用设置为监听并正确响应前台广播 ,系统在授予权限时会发送该广播

2.2.2 授予对照片和视频的部分访问权限

在升级 Android 14 之后,用户可以限制目标 SDK 版本高于或等于 33 的应用程序仅能访问用户选定的照片。
具体操作是,当应用的照片权限被设置为“始终询问”时,当应用请求权限后,系统权限管理器会询问用户是否让App访问所有照片,还是选择特定的照片,选择后者会弹出“照片选择器”让用户选择照片,应用只能访问用户选定的照片.
新增的权限为 READ_MEDIA_VISUAL_USER_SELECTED ,如需要访问所有媒体文件需要同步申请 READ_MEDIA_IMAGESREAD_MEDIA_VIDEO .
image

2.3 上下文注册的广播会在应用缓存期间加入队列

在 Android 14 中,当应用处于缓存状态时,系统可以将上下文注册的广播放入队列中。在清单中声明的广播不会加入队列,并且应用会从缓存状态中移除以进行广播传递。
当应用离开缓存状态(例如返回前台)时,系统会传递所有已加入队列的广播。某些广播的多个实例可以合并为一个广播。根据其他因素(例如系统运行状况),系统可能会从缓存状态中移除应用,并且传输之前加入队列的所有广播。

  • 影响

此项变更让广播接收变得不那么靠谱,也可能导致app 资源未及时销毁导致内存泄漏

  • 什么是缓存进程

缓存进程是当前不需要的进程,因此当其他位置需要诸如内存等资源时,系统会根据需要随意终止该进程。在正常运行的系统中,这些是资源管理中涉及的唯一进程。
运行良好的系统始终会有多个缓存进程可用,以便在应用之间高效切换,并可根据需要定期终止缓存的应用。只有在非常关键的情况下,系统才会达到所有缓存进程都会终止的程度,并且必须开始终止服务进程。
由于系统可能随时终止缓存的进程,因此应用在处于缓存状态时应停止所有工作。如果应用必须执行用户关键任务,则应使用上述 API 以活跃进程状态运行工作。
缓存的进程通常会保留一个或多个用户当前不可见的 Activity 实例(其 onStop() 方法已被调用并返回)。如果它们在系统终止此类进程时正确实现其 Activity 生命周期,不会影响用户在返回该应用时的体验。当关联的 activity 在新进程中重新创建时,它可以恢复之前保存的状态。请注意,如果进程被系统终止,则不能保证会调用 onDestroy()
缓存的进程会保存在列表中。此列表的确切排序政策是平台的实现细节。通常,它会尝试保留更实用的进程,例如托管用户的主屏幕应用或用户上次看到的 activity 的进程,然后运行其他类型的进程。您也可以应用终止进程的其他政策,例如对允许的进程数量设置硬性限制或限制进程可以持续缓存的时长。

2.4 应用只能终止自己的后台进程

从 Android 14 开始,当您的应用调用 killBackgroundProcesses() 时,该 API 只能终止您自己应用的后台进程。
如果您传入另一个应用的软件包名称,此方法对该应用的后台进程没有影响,并且 Logcat 中会显示以下消息

Invalid packageName: com.example.anotherapp

2.5 最低可安装的目标 API 级别

基于安全性 方面的优化 ,从Android 14 开始 targetSdkVersion < 23 的应用无法安装。
Android 6.0 Marshmallow(API 级别 23)引入了运行时权限,需要动态申请权限,让用户选择是否同意,部分流氓软件会把 targetSdkVersion 设置为22,绕过此约束。
低于 23 的应用安装时会提示:

INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 23, but found 7

调试低版本的app 可以使用命令行来安装

adb install --bypass-low-target-sdk-block xxxxxx.apk

2.6 用户可关闭不可关闭通知

向用户显示不可关闭的前台通知,从Android 14 已允许用户关闭此类通知
这项变更适用于通过 Notification.Builder#setOngoing(true) NotificationCompat.Builder#setOngoing(true) 设置 Notification.FLAG_ONGOING_EVENT 来阻止用户关闭前台通知的应用。FLAG_ONGOING_EVENT 的行为已发生变化,使用户实际上能够关闭此类通知。

在以下情况下,此类通知仍不可关闭:

  • 当手机处于锁定状态时
  • 如果用户选择全部清除通知操作(有助于防止意外关闭)

此外,这一新行为不适用于以下用例中的不可关闭通知:

  • 使用 MediaStyle 创建的通知(notification.setStyle(ew NotificationCompat.MediaStyle()))
  • 安全和隐私用例的政策限制使用
  • 企业设备政策控制器 (DPC) 和支持软件包

三、targetSdk >= 34 的变更

3.1 启动前台服务必须设置前台服务类型

如果有使用 startForeground() 在清单文件中必须要指定服务的前台类型,Android 10 里面 <service> 引入了android:foregroundServiceType 属性,用以帮助开发者更有目的地定义面向用户的前台服务。

如果您应用以 Android 14 为目标平台,则必须指定适当的前台服务类型。与以前的 Android 版本一样,可组合使用多个类型。下面列出了可供选择的前台服务类型:

  • camera
  • connectedDevice
  • dataSync
  • health
  • location
  • mediaPlayback
  • mediaProjection
  • microphone
  • phoneCall
  • remoteMessaging
  • shortService
  • specialUse
  • systemExempted

Android 14 中新增了 health, remoteMessaging, shortService, specialUsesystemExempted 类型。
以下是清单中的前台服务类型声明示例:

<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 为目标平台的应用未在清单中定义给定服务的类型,系统会在调用 startForeground() 时引发 MissingForegroundServiceTypeException

3.2 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 的类中移除。

3.3 JobScheduler 增强回调和网络行为

自引入以来,JobScheduler 会预期您的应用在几秒内从 onStartJobonStopJob 返回。在 Android 14 之前,如果作业运行时间太长,则会静默停止并失败。如果您的应用以 Android 14 或更高版本为目标平台且在主线程上超出了授权的时间,应用会触发 ANR,并显示错误消息“对 onStartJob 没有响应”或“对 onStopJob 没有响应”。请考虑迁移到 WorkManager,该版本可支持异步处理,或将所有繁重工作迁移到后台线程。

JobScheduler 使用时 ,如果使用 setRequiredNetworkTypesetRequiredNetwork 约束条件,则需要声明 ACCESS_NETWORK_STATE 权限。如果未声明 ACCESS_NETWORK_STATE 权限,并且以 Android 14 或更高版本为目标平台,则会导致 SecurityException

3.4 对隐式 intent 和待处理 intent 的限制

对于以 Android 14 或更高版本为目标平台的应用,Android 会通过以下方式限制应用向内部应用组件发送隐式 intent:

  • 隐式 intent 只能传送到导出的组件。应用必须使用显式 intent 传送到未导出的组件,或将该组件标记为已导出。
  • 如果应用通过未指定组件或软件包的 intent 创建可变待处理 intent,系统会抛出异常。

这些变更可防止恶意应用拦截意在供应用内部组件使用的隐式 intent。
AndroidManifest.xml 声明如下:

<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>  

使用隐式跳转:会抛出异常

context.startActivity(new Intent("com.example.action.APP_ACTION"));  

需要指定包名:

Intent explicitIntent = new Intent("com.example.action.APP_ACTION")  
explicitIntent.setPackage(context.getPackageName());  
context.startActivity(explicitIntent);  

3.5 在运行时注册的广播接收器必须指定导出行为

以 Android 14 或更高版本使用 Context#registerReceiver() 动态注册广播接收器 ,需要设置导出标记: RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED
此要求有助于保护应用免受安全漏洞的影响。

注册系统广播时,不用指定标志。

3.6 安全的动态代码加载

应用以 Android 14(API 级别 34)或更高版本为目标平台,并使用动态代码加载 (DCL) 功能,则必须将所有动态加载的文件标记为只读,
如果必须动态加载代码,请使用以下方法,在动态文件(例如 DEX、JAR 或 APK 文件)打开并写入任何内容之前立即将其设为只读:

private void testDcl(File jar){  
      try (FileOutputStream os = new FileOutputStream(jar)) {  
          // Set the file to read-only first to prevent race conditions  
          boolean b = jar.setReadOnly();  
          // Then write the actual file content  
      } catch (IOException e) { ... }  
      PathClassLoader cl = new PathClassLoader(jar, parentClassLoader);  
  }

插件化/热更新等涉及到 这些操作的会受到影响,新下载的文件属性修改成只读文件,如果是已存在的文件最好先删除然后重新下载并且设置只读权限

3.7 针对从后台启动 activity 的其他限制

对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,系统会进一步限制允许应用何时从后台启动 activity:

  • 当应用使用 PendingIntent#send() 或类似方法发送 PendingIntent 时,如果它想要授予自己的后台 activity 启动待处理 intent 的启动特权,则必须选择启用。则该应用现在必须选择加入一个 ActivityOptions,具体为带有 setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)

  • 当可见应用使用 bindService() 方法绑定另一个后台应用的服务时,如果可见应用想要授予自己的后台 activity 对绑定服务的启动特权,则应用应在调用 bindService() 方法时包含 BIND_ALLOW_ACTIVITY_STARTS 标志

3.8 压缩路径遍历

对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,在遍历 ZIP 文件时,如果 Zip 文件名称包含“..”或以“/”开头,在使用 ZipFile(String)ZipInputStream.getNextEntry() 时会抛出 ZipException

应用可以通过调用 dalvik.system.ZipPathValidator.clearCallback() 选择停用此验证。

3.9 每个 MediaProjection 拍摄会话均需要征得用户同意

对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,在以下任一情况下,MediaProjection#createVirtualDisplay 会抛出 SecurityException

  • 应用会缓存从 MediaProjectionManager#createScreenCaptureIntent 返回的 Intent,并将其多次传递给 MediaProjectionManager#getMediaProjection。
  • 在同一 MediaProjection 实例上多次调用 MediaProjection#createVirtualDisplay。

应用必须在每次捕获会话之前征求用户同意。单次拍摄会话是指对 MediaProjection#createVirtualDisplay 的单次调用,且每个 MediaProjection 实例只能使用一次。

如果应用需要调用 MediaProjection#createVirtualDisplay 来处理配置更改(例如屏幕方向或屏幕尺寸更改),可以按照以下步骤更新现有 MediaProjection 实例的 VirtualDisplay:

  • 使用新的宽度和高度调用 VirtualDisplay#resize
  • VirtualDisplay#setSurface 提供具有新宽度和高度的新 Surface。

3.10 安全的全屏 intent 通知

对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,只有提供通话和闹钟的应用才能使用 USE_FULL_SCREEN_INTENT 权限支持全屏 intent 通知。

四、新增/改善 功能

4.1 截屏监听

Android 14 引入了保护护隐私的屏幕截图检测 API,可以在Activity 里面注册回调,在Activity 可见时,截屏会有对应的回调消息返回。操作步骤如下:

  • 声明权限
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />  
  • 在 Activity 中注册回调
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();  
    registerScreenCaptureCallback(executor, screenCaptureCallback);  
}  
  
@Override  
protected void onStop() {  
    super.onStop();  
    unregisterScreenCaptureCallback(screenCaptureCallback);  
}

禁止当前ACtivity截屏可以添加下面的flag

activity.getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);    

4.2 路径可查询和插值

Android 的 Path API 是一种强大且灵活的机制,可用于创建和渲染矢量图形,能够描边或填充路径,根据线段、二次曲线或立方曲线构建路径,执行布尔运算以获取更复杂的形状,或同时执行所有这些操作。但是无法找出 Path 对象中实际包含的内容;该对象的内部信息在创建后对于调用方是不透明的。

如需创建 Path,可以调用 moveTo()lineTo() cubicTo() 等方法来添加路径片段。但无法询问该路径有哪些段,因此必须在创建时保留该信息。

从 Android 14 开始,可以查询路径以了解其内部内容。首先使用 Path.getPathIterator API 获取 PathIterator

Path path = new Path();  
path.moveTo(1.0F, 1.0F);  
path.lineTo(2.0F, 2.0F);  
path.close();  
PathIterator pathIterator = path.getPathIterator(); 

接下来,调用 PathIterator 逐个遍历片段,并检索每个片段的所有必要数据。以下示例使用了 PathIterator.Segment 对象,它会打包数据:

while (pathIterator.hasNext()) {  
    PathIterator.Segment segment = pathIterator.next();  
    Log.i(LOG_TAG, "segment: " + segment.getVerb() + ", " + segment.getPoints());  
} 

PathIterator 还有一个非分配版 next(),您可以在其中传入缓冲区来保存点数据。
查询 Path 数据的一个重要用例是插值。例如,在两个不同的路径之间添加动画(或变形)。为了进一步简化该用例,Android 14 在 Path 中添加了 interpolate() 方法。假设两个路径具有相同的内部结构,interpolate() 方法会使用该插值结果创建一个新的 Path。以下示例返回了形状介于 path 和 otherPath 之间的一半(线性插值为 0 .5)的路径:

Path interpolatedResult = new Path();  
if (path.isInterpolatable(otherPath)) {  
    path.interpolate(otherPath, 0.5F, interpolatedResult);  
} 

4.3 各应用语言偏好设定

Android 14 扩展了 Android 13(API 级别 33)中引入的按应用设定语言功能,并包含以下额外功能:

  • 自动生成应用的 localeConfig:

从 Android Studio Giraffe Canary 7 和 AGP 8.1.0-alpha07 开始,您可以将应用配置为自动支持各应用语言偏好设定。Android Gradle 插件会根据您的项目资源生成 LocaleConfig 文件,并在最终清单文件中添加对该文件的引用,这样您就不再需要手动创建或更新该文件。AGP 使用应用模块的 res 文件夹中的资源以及任何库模块依赖项来确定要在 LocaleConfig 文件中添加的语言区域。

  • 动态更新应用的 localeConfig:

使用 LocaleManager 方法中的 setOverrideLocaleConfig()getOverrideLocaleConfig() 可以在设备的系统设置中动态更新应用的受支持语言列表。有了这种灵活性,您可以按区域自定义支持的语言列表、运行 A/B 实验,或者如果您的应用通过服务器端推送进行本地化,则可以提供更新后的语言区域列表。

  • 输入法 (IME) 的应用语言可见性:

IME 可以利用 getApplicationLocales() 方法查看当前应用的语言,并将 IME 语言与该语言进行匹配。

4.4 新增唤醒屏幕权限

Android 14 引入了 android.permision.TURN_SCREEN_ON 权限,目标平台为Android 14 及以上的应用使用唤醒屏幕时,需要在AndroidManifest文件中配置这个权限。
另外唤醒屏幕权限有一个对应的 appops 权限 AppOpsManager.OP_TURN_SCREEN_ON,这个权限在Android T上是默认开启,在Android U上对目标平台为Android 14及以上的应用默认关闭。

不申请权限调用下面API 无法唤醒屏幕

PowerManager powerManager = (PowerManager) Context.getSystemService(  
Context.POWER_SERVICE);  
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(  
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "testName");  
wakeLock.acquire(3000)  
posted @ 2024-03-28 18:45  阿丟啊  阅读(2365)  评论(0编辑  收藏  举报