Android14 - 前台Service、图片选择器 、OpenJDK 17、其他适配

时间:2024-10-22 15:40:02

前台服务

1. 指定前台服务类型

     以 Android 14(API 级别 34)或更高版本为目标平台的应用,需要为应用中的每项前台服务指定服务类型,因为系统需要特定类型的前台服务满足特定用例。具体介绍如下:

    在Android 10 在 <service> 元素内引入了 android:foregroundServiceType 属性。
如果您的应用以 Android 14 为目标平台,则必须指定适当的前台服务类型,可组合使用多个类型;以下了可供选择的前台服务类型:
    •    camera
    •    connectedDevice
    •    dataSync
    •    health
    •    location
    •    mediaPlayback
    •    mediaProjection
    •    microphone
    •    phoneCall
    •    remoteMessaging
    •    shortService
    •    specialUse
    •    systemExempted

如果应用中的用例与这些类型均不相关,考虑使用 WorkManager 或Android 14中引入的新Api,即作业必须是用户发起的数据传输作业。

    在上述的类型中,Android 14 中新增 health, remoteMessaging, shortService, specialUse 和 systemExempted 等类型。 

    类型示例:
  1. <manifest ...>
  2.   <uses-permission android:name=".FOREGROUND_SERVICE" />
  3.   <uses-permission android:name=".FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
  4.     <application ...>
  5.       <service
  6.           android:name=".MyMediaPlaybackService"
  7.           android:foregroundServiceType="mediaPlayback"
  8.           android:exported="false">
  9.       </service>
  10.     </application>
  11. </manifest>

         备注: 如果以 Android 14 或更高版本为目标的应用未在清单中定义给定服务的类型,系统会在调用 startForeground() 时引发 MissingForegroundServiceTypeException。

2. 启动时包含前台服务类型

        启动前台服务,最好的方式,是使用 ServiceCompat 版本的 startForeground()(适用于 androidx-core 1.12 及更高版本),传入前台服务的类型值,可传入一个或多个,比如aa,或aa|bb, 或aa|bb|cc;  


       启动服务,(0, notification, FOREGROUND_SERVICE_TYPE_LOCATION)如果调用中未指定前台服务类型,则该类型默认为清单中定义的值。如果您没有在清单中指定服务类型,系统会抛出 MissingForegroundServiceTypeException。

        如果前台服务在您启动后需要新的权限,需要调用 startForeground() 并添加新的服务类型。例如,假设健身应用运行一项跑步跟踪器服务,该服务始终需要 location 信息,只想跟踪其位置,您的应用应调用 startForeground() 并仅传递 location 服务类型。如果用户想要开始播放音频,请再次调用 startForeground() 并传递 location|mediaPlayback。

3. 声明新权限来使用

        需要前台服务类型声明 Android 14 中引入的特定权限,需在清单文件中声明的权限中声明。所有这些权限都定义为一般权限,并默认授予。用户无法撤消这些权限。如果在调用 startForeground() 时未声明适当的前台服务类型权限,系统会抛出 SecurityException。

      例如,使用前台服务类型 FOREGROUND_SERVICE_TYPE_LOCATION 的应用,需要请求 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION 权限。应用在尝试调用 startForeground() 之前,必须先请求并获得所需的权限。如果您的应用未满足启动前台服务的所有运行时要求,调用 startForeground() 后,系统会抛出 SecurityException。

    举个启动相机前台服务的示例:

    

  1. class MyCameraService: Service() {
  2.   private fun startForeground() {
  3.     // Before starting the service as foreground check that the app has the
  4.     // appropriate runtime permissions. In this case, verify that the user has
  5.     // granted the CAMERA permission.
  6.     val cameraPermission =
  7.             (this, )
  8.     if (cameraPermission != PermissionChecker.PERMISSION_GRANTED) {
  9.         // Without camera permissions the service cannot run in the foreground
  10.         // Consider informing user or updating your app UI if visible.
  11.         stopSelf()
  12.         return
  13.     }
  14.     try {
  15.         val notification = (this, "CHANNEL_ID")
  16.             // Create the notification to display while the service is running
  17.             .build()
  18.         (
  19.             /* service = */ this,
  20.             /* id = */ 100, // Cannot be 0
  21.             /* notification = */ notification,
  22.             /* foregroundServiceType = */
  23.             if (.SDK_INT >= Build.VERSION_CODES.R) {
  24.                 ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
  25.             } else {
  26.                 0
  27.             },
  28.         )
  29.     } catch (e: Exception) {
  30.         if (.SDK_INT >= Build.VERSION_CODES.S
  31.                 && e is ForegroundServiceStartNotAllowedException) {
  32.             // App not in a valid state to start foreground service
  33.             // (. started from bg)
  34.         }
  35.         // ...
  36.     }
  37.   }
  38. }

4. 每种前台服务类型及权限

      举2个例子介绍,具体参考官网地址 /about/versions/14/changes/fgs-types-required?

      为了使用给定的前台服务类型,必须在清单文件中声明特定权限,且运行时检查权限是否授权;

相机

      1. 要在清单中的 android:foregroundServiceType 下声明的前台服务类型 - camera
      2. 在manifest中声明的权限 - FOREGROUND_SERVICE_CAMERA
      3. 传递给 startForeground() 的常量 - FOREGROUND_SERVICE_TYPE_CAMERA
      4. 运行时前提条件 - 请求并获得 CAMERA 运行时权限

说明:继续在后台访问摄像头,例如支持多任务的视频聊天应用。

连接的设备

1. 清单中声明的前台服务类型android:foregroundServiceTypeconnectedDevice
2. 在manifest声明的权限FOREGROUND_SERVICE_CONNECTED_DEVICE
3. 传递给 startForeground() 的常量 FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
运行时前提条件
必须至少满足以下其中一个条件:

     在清单中至少声明以下其中一项权限:

        CHANGE_NETWORK_STATE
        CHANGE_WIFI_STATE
        CHANGE_WIFI_MULTICAST_STATE
        NFC
        TRANSMIT_IR
    请求并获得以下至少一项运行时权限:

        BLUETOOTH_CONNECT
        BLUETOOTH_ADVERTISE
        BLUETOOTH_SCAN
        UWB_RANGING
调用 ()

使用场景:与需要蓝牙、NFC、IR、USB 或网络连接的外部设备进行互动。

注意 :如果您的应用执行投影或远程消息传递操作,请改用相应的媒体投影或远程消息传递类型。

     其他类型,参考/about/versions/14/changes/fgs-types-required?

图片选择器

        Android 14 引入了对所选照片的访问权限,使用户授权App访问其媒体库中的特定图片和视频,而不是授予对指定类型的所有媒体的访问权限。对以Android 14 及更高的版本的App才会启用此变更,系统提供图片选择器,App调用选择器即可,无需请求任何存储权限。

        如果你的App使用存储权限以实现和维护自己的图库选择器,需要使用到READ_MEDIA_VISUAL_USER_SELECTED 权限,不使用新权限,系统会在兼容模式下运行应用。

OpenJDK 17 更新

Android 14 的 Android 的核心 与最新 OpenJDK LTS 版本中的功能保持一致,包括适合应用和平台开发者的库更新及 Java 17 语言支持。

影响应用兼容性的点:

正则表达式的更改:

        严格地遵循 OpenJDK 的语义,不允许无效的组引用,否则抛出 类抛出 IllegalArgumentException。


UUID 处理:

        () 方法会执行更严格的检查;


ProGuard 问题:

        使用 ProGuard 缩减、混淆和优化应用时,添加 类会导致问题,因为 Kotlin 库会根据 ("") 返回类更改行为。若应用使用没有 类的旧版,新的优化可能会将 computeValue 方法从派生自 的类中移除。

其他

1. 动态代码加载

        使用动态代码(DCL) 功能时,必须将所有动态加载的文件标记为只读,否则,系统会抛出异常。在动态文件(例如 DEX、JAR 或 APK 文件)打开之前将其设为只读:

  1. val jar = File("DYNAMICALLY_LOADED_FILE.jar")
  2. val os = FileOutputStream(jar)
  3. {
  4.     // Set the file to read-only first to prevent race conditions
  5.     ()
  6.     // Then write the actual file content
  7. }
  8. val cl = PathClassLoader(jar, parentClassLoader)

2. 后台activity 启动处理Intent/Service的限制

        ​以Android 14及以上的目标平台的App,系统会进一步限制允许应用何时从后台启动 Activty:

        当应用使用 PendingIntent#send() 或类似方法发送 PendingIntent 时,如果它想授予自己后台的activity 启动处理 intent 的权限,需要选择启用。选择启用,通过 setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED) 传递 ActivityOptions的方式;

        当可见应用使用 bindService() 方法绑定另一个后台应用的服务时,这时想授予自己的后台 Activity 对绑定服务的启动权限,需要在调用 bindService() 方法时包含 BIND_ALLOW_ACTIVITY_STARTS 标志。

        以上改动目的是防止恶意应用滥用 API 在后台做启动干扰活动。

3. 压缩路径遍历

        Android 14 及更高版本的应用中,如果 zip 文件条目名称包含“..”或以“/”开头,使用 () 函数时,会抛出 ZipException。App可以通过调用 () 选择停用此验证

4. MediaProjection 拍摄会话都需要征得用户同意

        Android14及以上的目标版本App,在以下任一情况下使用,MediaProjection#createVirtualDisplay 会抛出 SecurityException,

        第一种情况:App缓存从 MediaProjectionManager#createScreenCaptureIntent 返回的 Intent,并将其多次传递给 MediaProjectionManager#getMediaProjection。
        第二种情况:App在同一 MediaProjection 实例上多次调用MediaProjection#createVirtualDisplay。
        所以,适配方式:App必须在每次捕获会话之前征求用户同意。单次拍摄会话是指对 MediaProjection#createVirtualDisplay 的单次调用,且每个 MediaProjection 实例只能使用一次。

总结: 

        结合前两张的描述,Android 14的适配基本写完,其实应该在去年底写完的,只是今年才想在****上发表一些文章。