Android各版本适配(6-14)

前言

从Android 4.4一直开发到现在,每次版本更新都有不同的内容需要适配。这里总结一下常见的适配方案,以便于查找。

Android 6

1、运行时权限

android6.0 之前,我们把app需要用到的权限全部罗列在Manifest清单文件中。安装app时android系统会询问用户是否授予这些权限,拒绝后则无法安装app。如果授予,则安装app,之后无法修改授予状态。

android6.0 将权限分为普通权限(不涉及用户隐私和安全)和危险权限(设计用户隐私和安全)。普通权限和andorid6.0之前一样,在Manifest清单文件中申请即可。危险权限需要在使用时动态申请,由用户决定是否授予。

危险权限分组:

在这里插入图片描述

2、HttpClient的移除

自Android6.0起,HttpClient系列代码从SDK中剔除,推荐使用HttpURLConnection。

Android 7

1、应用间共享文件

对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI 。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。

要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的最简单方式是使用 FileProvider 类。

(1)在AndroidManifest.xml清单文件中注册provider

<manifest>
  ...
  <application>
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.demo.fileprovider" <!--替换为自己的包名-->
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/paths" />
    </provider>
    ...
  </application>
</manifest>

(2)res/xml中定义对外暴露的文件夹路径:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="external_storage_root"
        path="." />
    <files-path
        name="files-path"
        path="." />
    <cache-path
        name="cache-path"
        path="." />
    <external-files-path
        name="external_file_path"
        path="." />
    <external-cache-path
        name="external_cache_path"
        path="." />
    <root-path
        name="root-path"
        path="" />
</paths>
  • name:一个引用字符串。

  • path:文件夹“相对路径”,完整路径取决于当前的标签类型。

  • 代表Environment.getExternalStorageDirectory()

  • 代表context.getFilesDir()

  • 代表context.getCacheDir()

  • <external-files-path>代表context.getExternalFilesDirs()

  • <external-cache-path>代表getExternalCacheDirs()

  • 代表设备的根目录new File(“/”);

7.0之前跳转到系统相机拍照的代码,需要提前设定好保存图片的uri,跳转到相机应用

String cachePath = getApplicationContext().getExternalCacheDir().getPath();
File picFile = new File(cachePath, "test.jpg");
Uri picUri = Uri.fromFile(picFile);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, picUri);
startActivityForResult(intent, 100);

7.0之后:

String cachePath = getApplicationContext().getExternalCacheDir().getPath();
File picFile = new File(cachePath, "test.jpg");
Uri uriForFile = null;
 if (Build.VERSION.SDK_INT >= 24) {
   uriForFile = FileProvider.getUriForFile(MainActivity.this"com.demo.fileprovider", picFile);
  } else {
   uriForFile = Uri.fromFile(picFile);
  }
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uriForFile);
startActivityForResult(intent, 100);

Uri.fromFile(file) 换成了 FileProvider.getUriForFile,这里getUriForFile方法的第二个参数,就是第一步的provider的authorities。注意:替换为自己的包名

2、广播

Android 7.0 移除了三项隐式广播。

面向 Android 7.0 开发的应用不会收到 CONNECTIVITY_ACTION 广播,即使它们已有清单条目来请求接受这些事件的通知。

在主线程中通过Context.registerReceiver()动态注册了CONNECTIVITY_ACTION广播,该应用程序仍然可以接收到该广播。

应用无法发送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 广播。此项优化会影响所有应用,而不仅仅是面向 Android 7.0 的应用。

Android 8

1、通知渠道id

Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。targeSdk升级到26之后,所有的通知的实现都需要提供通知渠道,如果不提供通知渠道的话,所有通知在8.0系统上面都不能正常展示。

		//通知渠道id
        val channelId = "channelId"
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //通知渠道名称
            val channelName = "channelName"
            //通知渠道重要程度
            val importance = NotificationManager.IMPORTANCE_HIGH
            //构建通知渠道
            val channel = NotificationChannel(channelId, channelName, importance)
            //设置通知渠道描述
            channel.description = ""
            //向系统注册通知渠道,注册后则不能修改重要性以及其他通知行为,但可以删除
            notificationManager.createNotificationChannel(channel)
        }
        
        val notification = NotificationCompat.Builder(this"channelId")
                .setContentTitle("标题")
                .setContentText("消息内容")
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setAutoCancel(true)//点击自动消失
                .build()
        //通知id,每个通知都应该不同否则会覆盖
        val notifyId = 1
        notificationManager.notify(notifyId, notification)

2、后台执行限制

  • 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。
  • 新的 Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。

3、允许安装未知来源应用

8.0 的应用需要在 AndroidManifest.xml 中声明 REQUEST_INSTALL_PACKAGES 权限,否则将无法进行应用内升级。

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

4、隐式广播的限制

我们自己定义的广播隐式调用不能接收,显式Intent可以接收。或者动态注册,然后隐式调用可以接收。

如果要接收系统广播只能动态注册。

5、图标适配

在图标适配中有两个部分,前景图和背景图,所以设计的时候需要提供前景图和背景图两个图,这样在不同的厂商无论图标的背景怎么换都是对图标的背景图进行切割,而前景图是不会变的。

6、权限

之前对于隐私权限只要申请一个就会将其在的权限组全部通过,android 8.0以后申请单个只给单个;

Android 9

1、限制非 Activity 环境中启动 Activity

在 Android 9 中,不能从非 Activity 环境中启动 Activity,除非您传递 Intent 标志 FLAG_ACTIVITY_NEW_TASK。 如果您尝试在不传递此标志的情况下启动 Activity,则该 Activity 不会启动,系统会在日志中输出一则消息。

2、前台服务

针对 Android 9 或更高版本并使用前台服务的应用必须请求 FOREGROUND_SERVICE 权限。 这是普通权限,因此,系统会自动为请求权限的应用授予此权限。

3、默认情况下启用网络传输层安全协议 (TLS)

如果应用以 Android 9 或更高版本为目标平台,则默认情况下 isCleartextTrafficPermitted() 函数返回 false。 如果您的应用需要为特定域名启用明文,您必须在应用的网络安全性配置中针对这些域名将 cleartextTrafficPermitted 显式设置为 true。

在 res 目录下新建xml文件夹,添加network_security_config.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </base-config>
</network-security-config>

AndroidManifest.xml中的application添加:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config">
            ...
    </application>
</manifest>

4、对非 SDK 接口的限制

现已禁止访问特定的非 SDK 接口,无论是直接访问,还是通过 JNI 或反射进行间接访问。尝试访问受限制的接口时,会生成 NoSuchFieldException 和 NoSuchMethodException 之类的错误。

5、Webview多进程访问限制

为改善 Android 9 中的应用稳定性和数据完整性,应用无法再让多个进程共用同一 WebView 数据目录。 此类数据目录一般存储 Cookie、HTTP 缓存以及其他与网络浏览有关的持久性和临时性存储。

如果开发者需要在多进程中使用 WebView,则必须先调用 WebView.setDataDirectorySuffix() 方法为每个进程设置用于存储 WebView 数据的目录。若多进程 WebView 之间需要共享数据,开发者需自己通过 IPC 的方式实现。

此外,若开发者只想在一个进程中使用 WebView,并且希望严格执行这个规则,可以通过在其他进程中调用 WebView.disableWebView() 方法,这样其他进程创建 WebView 实例就会抛出异常。

6、硬件序列号

在 Android 9 中,Build.SERIAL 始终设置为 “UNKNOWN” 以保护用户的隐私。

如果您的应用需要访问设备的硬件序列号,您应改为请求 READ_PHONE_STATE 权限,然后调用 getSerial()。

Android10

1、分区存储:

分区存储将外部存储分成两部分:

(1)App-specific directory (沙盒目录)

APP只能在Context.getExternalFilesDir()目录下通过File的方式创建文件,APP卸载的时候,这个目录下的文件会被删除;无法通过File的方式在其他路径创建文件。

(2)Public Directory 公共目录

公共目录包括:多媒体公共目录(Photos, Images, Videos, Audio)和下载文件目录(Downloads)。

APP可以通过MediaStore 或者 SAF(System Access Framework)的方式访问其中的文件。APP卸载后,文件不会被删除。

Android Q以上移除了WRITE_EXTERNAL_STORAGE权限,应用不需要这个权限就可以向沙盒内存储文件,也可以通过媒体数据库的方式保存媒体数据至特定位置。

App卸载后,对应的沙盒目录也会被删除,如果APP想要在卸载时保留沙盒目录下的数据,要在 AndroidManifest.xml 中声明 android:hasFragileUserData=“true”,这样在 APP卸载时就会有弹出框提示用户是否保留应用数据。

2、后台运行时访问设备位置信息需要权限

Android 10 引入了 ACCESS_BACKGROUND_LOCATION 权限(危险权限)。

该权限允许应用程序在后台访问位置。如果请求此权限,则还必须请求ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION权限。只请求此权限无效果。

Android 10中必须具有 ACCESS_FINE_LOCATION 权限才能使用的类和方法:

电话

  • TelephonyManager
    • getCellLocation()
    • getAllCellInfo()
    • requestNetworkScan()
    • requestCellInfoUpdate()
    • getAvailableNetworks()
    • getServiceState()
  • TelephonyScanManager
    • requestNetworkScan()
  • TelephonyScanManager.NetworkScanCallback
    • onResults()
  • PhoneStateListener
    • onCellLocationChanged()
    • onCellInfoChanged()
    • onServiceStateChanged()

WLAN

  • WifiManager
    • startScan()
    • getScanResults()
    • getConnectionInfo()
    • getConfiguredNetworks()
  • WifiAwareManager
  • WifiP2pManager
  • WifiRttManager

蓝牙

  • BluetoothAdapter
    • startDiscovery()
  • startLeScan()
    • startScan()
  • BluetoothAdapter.LeScanCallback
  • BluetoothLeScanner

3、后台启动 Activity 的限制

应用处于后台时,无法启动Activity。

4、深色主题

(1)手动适配—资源替换

res 下新建 values-night目录,创建对应的colors.xml文件。

(2)自动适配—Force Dark

应用必须选择启用 Force Dark,方法是在其主题背景中设置 android:forceDarkAllowed=“true”。此属性会在所有系统及 AndroidX 提供的浅色主题背景(例如 Theme.Material.Light)上设置。

Force Dark需要注意几点:

  • 如果使用的是 DayNight 或 Dark Theme 主题,则设置forceDarkAllowed 不生效。
  • 如果有需要排除适配的部分,可以在对应的View上设置forceDarkAllowed为false。

5、标识符和数据

对不可重置的设备标识符实施了限制

受影响的方法包括:

  • Build
    • getSerial()
  • TelephonyManager
    • getImei()
    • getDeviceId()
    • getMeid()
    • getSimSerialNumber()
    • getSubscriberId()

从 Android 10 开始,应用必须具有 READ_PRIVILEGED_PHONE_STATE 特许权限才能正常使用以上这些方法。

如果你的应用没有该权限,却仍然使用了以上的方法,则返回的结果会因目标 SDK 版本而异:

  • 如果应用以 Android 10 或更高版本为目标平台,则会发生 SecurityException。
  • 如果应用以 Android 9(API 级别 28)或更低版本为目标平台,则相应方法会返回 null 或占位符数据(如果应用具有 READ_PHONE_STATE 权限)。否则,会发生 SecurityException。

这项改动表示第三方应用无法获取Device ID这类唯一标识。

6、限制了对剪贴板数据的访问权限

除非您的应用是默认输入法 (IME) 或是目前处于焦点的应用,否则它无法访问 Android 10 或更高版本平台上的剪贴板数据。

7、对启用和停用 WLAN 实施了限制

以 Android 10 或更高版本为目标平台的应用无法启用或停用 WLAN。WifiManager.setWifiEnabled() 方法始终返回 false。

Android11

1、分区存储强制执行

Android 11在分区存储基础上限制了应用访问其他应用的文件。

分区存储将存储空间分为两部分:

  • 公共目录:Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones等

    • 公共目录的文件在App卸载后,不会删除
    • 可以通过SAF、MediaStore接口访问
    • 拥有权限,也能通过路径直接访问
  • 应用专属目录

    • 应用专属目录只能自己直接访问
    • App卸载,数据会清除。

将应用更新为以 Android 11 为目标平台后,您将无法使用requestLegacyExternalStorage,而且也没有其他标记可以提供停用分区存储。

所有文件访问权限 MANAGE_EXTERNAL_STORAGE,用来获取所有文件的管理权限。

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

val intent = Intent()
intent.action= Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
startActivity(intent)

//判断是否获取MANAGE_EXTERNAL_STORAGE权限:
val isHasStoragePermission= Environment.isExternalStorageManager()

2、电话号码相关权限

Android 11 更改了应用在读取电话号码时使用的与电话相关的权限。

如果应用以 Android 11 或更高版本为目标平台,并且需要访问以下列表中显示的电话号码 API,则必须请求 READ_PHONE_NUMBERS 权限,而不是 READ_PHONE_STATE 权限。

  • TelephonyManager 类和 TelecomManager 类中的 getLine1Number() 方法。
  • TelephonyManager 类中不受支持的 getMsisdn() 方法。

如果应用声明 READ_PHONE_STATE 以调用前面列表中的方法以外的方法,可以继续在所有 Android 版本中请求 READ_PHONE_STATE。不过,如果仅对前面列表中的方法使用 READ_PHONE_STATE 权限,请按以下方式更新您的清单文件:

更改 READ_PHONE_STATE 的声明,以使应用仅在 Android 10(API 级别 29)及更低版本中使用该权限。

添加 READ_PHONE_NUMBERS 权限。

<manifest ...>
    <!-- Grants the READ_PHONE_STATE permission only on devices that run
         Android 10 (API level 29) and lower. -->
    <uses-permission android:name="READ_PHONE_STATE"
                     android:maxSdkVersion="29" />
    <uses-permission android:name="READ_PHONE_NUMBERS" />
</manifest>

3、自定义消息框视图被屏蔽

出于安全方面的考虑,同时也为了保持良好的用户体验,如果包含自定义视图的消息框是以 Android 11 或更高版本为目标平台的应用从后台发送的,系统会屏蔽这些消息框。请注意,仍允许使用文本消息框;此类消息框是使用 Toast.makeText() 创建的,并不调用 setView()。

如果您的应用仍尝试从后台发布包含自定义视图的消息框,系统不会向用户显示相应的消息,而是会在 logcat 中记录以下消息:

W/NotificationService: Blocking custom toast from package \
  <package> due to package not in the foreground

消息框回调

如果您希望在消息框(文本消息框或自定义消息框)出现或消失时收到通知,请使用 Android 11 中添加的 addCallback() 方法。

文本消息框 API 变更

以 Android 11 或更高版本为目标平台的应用会发现文本消息框受到以下负面影响:

  • getView() 方法返回 null。

以下方法的返回值并不反映实际值,因此您不应在应用中依赖于它们:

  • getHorizontalMargin()
  • getVerticalMargin()
  • getGravity()
  • getXOffset()
  • getYOffset()

以下方法是空操作,因此您的应用不应使用它们:

  • setMargin()
  • setGravity()

4、媒体intent操作需要系统默认相机

从 Android 11 开始,只有预装的系统相机应用可以响应以下 intent 操作:

  • android.media.action.VIDEO_CAPTURE
  • android.media.action.IMAGE_CAPTURE
  • android.media.action.IMAGE_CAPTURE_SECURE

也就是说,如果我调用intent唤起照相机,使用VIDEO_CAPTURE的action,只有系统的相机能够响应,而第三方的相机应用不会响应了。

如果要使用特定的第三方相机应用来代表其捕获图片或视频,可以通过为intent设置软件包名称或组件来使这些intent变得明确。

5、5G

Android 11 添加了在您的应用中支持 5G 的功能。

新的Android11也是支持了5G相关的一些功能,包括:

  • 检测是否连接到了5G网络
  • 检查按流量计费性

检测5G网络,通过TelephonyManager的监听方法:

private fun getNetworkType(){
 val tManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
 tManager.listen(object : PhoneStateListener() {

 @RequiresApi(Build.VERSION_CODES.R)
override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
 if (ActivityCompat.checkSelfPermission(this@Android11Test2Activity, android.Manifest.permission.READ_PHONE_STATE) != android.content.pm.PackageManager.PERMISSION_GRANTED) {
  return
 }
 super.onDisplayInfoChanged(telephonyDisplayInfo)

 when(telephonyDisplayInfo.networkType) {
 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> showToast("高级专业版 LTE (5Ge)")
 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> showToast("NR (5G) - 5G Sub-6 网络")
 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> showToast("5G+/5G UW - 5G mmWave 网络")
 else -> showToast("other")
 }
}

 }, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
}

检测流量计费方法,监听网络,在回调中判断:

val manager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
 manager.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
  override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
   super.onCapabilitiesChanged(network, networkCapabilities)

 //true 代表连接不按流量计费
 val isNotFlowPay=networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) ||
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
   }
})

6、后台位置信息访问权限

在搭载 Android 11 的设备上,当应用中的某项功能请求在后台访问位置信息时,用户看到的系统对话框不再包含用于启用后台位置信息访问权限的按钮。如需启用后台位置信息访问权限,用户必须在设置页面上针对应用的位置权限设置一律允许选项。

7、软件包可见性

Android 11 更改了应用查询用户已在设备上安装的其他应用以及与之交互的方式。使用 <queries> 元素,应用可以定义一组自身可访问的其他软件包。通过告知系统应向应用显示哪些其他软件包,此元素有助于鼓励最小权限原则。此外,此元素还可帮助 Google Play 等应用商店评估应用为用户提供的隐私权和安全性。

如果应用以 Android 11 或更高版本为目标平台,您可能需要在应用的清单文件中添加 <queries> 元素。在 <queries> 元素中,您可以按软件包名称、intent 签名或提供程序授权指定软件包。

8、前台服务类型

从 Android 9 开始,应用仅限于在前台访问摄像头和麦克风。为了进一步保护用户,Android 11 更改了前台服务访问摄像头和麦克风相关数据的方式。如果您的应用以 Android 11 为目标平台并且在某项前台服务中访问这些类型的数据,您需要在该前台服务的声明的 foregroundServiceType 属性中添加新的 camera 和 microphone 类型。

应用某项前台服务需要访问位置信息、摄像头和麦克风,那么就要在清单文件中这样添加:

<manifest ...>
    <service ...
        android:foregroundServiceType="location|camera|microphone" />
</manifest>

Android 12

1、SplashScreen

从 Android 12 开始,在搭载 Android 12 或更高版本的设备上运行时,所有应用都将拥有启动动画。这包括启动时的进入应用动作、显示应用图标的启动画面,以及向应用本身的过渡。

在这里插入图片描述

(1)应用图标应该是矢量可绘制对象(AVD XML),它可以是静态或动画形式。虽然动画的时长可以不受限制,但我们建议不超过1,000 毫秒。默认情况下,使用启动器图标。

(2)可以选择添加图标背景;在图标与窗口背景之间需要更高的对比度时图标背景很有用。如果您使用一个自适应图标,当该图标与窗口背景之间的对比度足够高时,就会显示其背景。

(3)与自适应图标一样,前景的三分之一被遮盖。

(4)窗口背景由不透明的单色组成。如果窗口背景已设置且为纯色,则未设置相应的属性时默认使用该背景。

2、更安全的组件导出

以Android 12为目标平台的App,如果其包含的四大组件中使用到了Intent过滤器(intent-filter),则必须显式声明 android:exported 属性,否则App将无法在Android 12及更高系统版本的设备上安装。

3、定位权限:大概位置

如果您的应用请求 ACCESS_COARSE_LOCATION,但未请求 ACCESS_FINE_LOCATION,则此变更不会影响您的应用。

如果您的应用请求 ACCESS_FINE_LOCATION 运行时权限,您还应请求ACCESS_COARSE_LOCATION 权限,以便处理用户授予应用大致位置访问权限的情形。

4、PendingIntent可变性

如果您的应用程序以Android 12为目标平台,您必须为应用创建的每个 PendingIntent 对象指定可变性。这项额外的要求可提高应用的安全性。因为三方app可以通过劫持PendingIntent,然后改写里面的action、category、data等,造成重定向攻击。

适配的具体就是在创建 PendingIntent时,使用 PendingIntent.FLAG_MUTABLE 或 PendingIntent.FLAG_IMMUTABLE 标志。否则运行时会报 IllegalArgumentException。

5、蓝牙权限

如果您的应用程序面向Android 12或更高版本,使用蓝牙功能时请在应用程序的清单文件中声明以下权限:

  • BLUETOOTH_SCAN:允许蓝牙设备扫描。
  • BLUETOOTH_CONNECT:允许蓝牙设备连接。
  • BLUETOOTH_ADVERTISE:允许当前蓝牙设备可以被其他蓝牙设备发现。

对于以前的与蓝牙相关的权限声明,设置android:maxSdkVersion到30。此应用兼容性步骤可帮助系统仅授予您的应用在运行Android 12 的设备上安装时所需的蓝牙权限。

<manifest ...>
    <!-- Request legacy Bluetooth permissions on older devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
                     android:maxSdkVersion="30" />

    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

    <!-- 只有当您的应用程序使用蓝牙扫描结果来获取物理位置时才需要 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
</manifest>

如果您的应用程序使用蓝牙扫描结果来获取物理位置,请声明ACCESS_FINE_LOCATION。以前版本中(6.0 ~ 11)是必须申请定位权限,才可以进行蓝牙扫描。

在Android 12上,如果你的应用程序不使用蓝牙扫描结果来获取物理位置。可以添加android:usesPermissionFlags 属性到您的 BLUETOOTH_SCAN 权限声明,并将该属性的值设置为 neverForLocation。

<manifest ...>
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
                     android:usesPermissionFlags="neverForLocation" />

    <!--Android 12后,可以删除定位权限 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
</manifest>

Android13

1、细化的媒体权限

如果应用以 Android 13 或更高版本为目标平台,并且需要访问其他应用已经创建的媒体文件,必须请求以下一项或多项细化的媒体权限,而不是READ_EXTERNAL_STORAGE 权限:

媒体类型请求权限
图片和照片READ_MEDIA_IAMGES
视频READ_MEDIA_VIDEO
音频文件READ_MEDIA_AUDIO

如果用户之前向您的应用授予了 READ_EXTERNAL_STORAGE 权限,系统会自动向您的应用授予细化的媒体权限。否则,当应用请求上表中显示的任何权限时,系统会显示面向用户的对话框。

<manifest ...>
    <!-- Required only if your app targets Android 13. -->
    <!-- Declare one or more the following permissions only if your app needs
    to access data that's protected by them. -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

    <!-- Required to maintain app compatibility. -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
                     android:maxSdkVersion="32" />
    <application ...>
        ...
    </application>
</manifest>

2、通知运行时权限

在AndroidManifest.xml中对发送通知权限进行声明:

<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <application ...>
        ...
    </application>
</manifest>

POST_NOTIFICATIONS权限只有在应用程序的targetSdk指定成33或更高时才会有用。

要确认用户是否已启用通知,请调用 areNotificationsEnabled()。

3、通知权限会影响前台服务的显示

如果用户拒绝授予通知权限,就不会在抽屉式通知栏中看到与前台服务相关的通知。 不过,无论是否授予通知权限,用户都会在前台服务 (FGS) 任务管理器中看到与前台服务相关的通知。

4、WebView

从Android 13开始,以Android13(API 33+)为目标平台的应用,WebView存在以下方法与API调整:

  • WebSettings.setAppCacheEnabled() 方法废弃。
  • WebSettings.setForceDark() 方法废弃。

4、静态广播注册

从Android 13开始,以Android13(API 33+)为目标平台的应用,注册静态广播时,需设置对其他应用的可见性:

  • 若对其他应用可见,广播注册时设置:Context.RECEIVER_EXPORTED
  • 若仅应用内使用,广播注册时设置:Context.RECEIVER_NOT_EXPORTED
private void registerTestReceiver() {
    IntentFilter filter = new IntentFilter();
    filter.addAction("com.xiaxl.test.action");
    // api >= 33
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        // 跨应用间使用
        MainActivity.this.registerReceiver(mTestReceiver, filter, Context.RECEIVER_EXPORTED);
        // 应用内使用
        //MainActivity.this.registerReceiver(mTestReceiver, filter, Context.RECEIVER_EXPORTED);
    }
    // api <= 32
    else {
        MainActivity.this.registerReceiver(mTestReceiver, filter);
    }
}

目前该增强措施并非默认生效,开发者需启用 DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED兼容性框架,并在动态注册广播时指定是否接受其他应用的广播。

如果启用了 DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED 兼容性框架更改,则必须为每个广播接收器指定 RECEIVER_EXPORTED 或 RECEIVER_NOT_EXPORTED。否则,当您尝试注册广播接收器时,系统会抛出 SecurityException。

5、Wi-Fi 权限

在以前的 Android 版本中,用户需要向您的应用授予 在以前的 Android 版本中,用户需要向您的应用授予 ACCESS_FINE_LOCATION 权限,应用才能完成一些常见的 Wi-Fi 用例。

由于用户很难将位置信息权限与 Wi-Fi 功能相关联,因此 Android 13(API 级别 33)在 NEARBY_DEVICES 权限组中引入了运行时权限,适用于管理设备与附近 Wi-Fi 接入点连接情况的应用。此权限 (NEARBY_WIFI_DEVICES) 可满足以下 Wi-Fi 用例:

  • 查找或连接到附近的设备,如打印机或媒体投射设备。通过该工作流,您的应用可以完成以下类型的任务:
    • 通过带外方式(例如通过 BLE)接收 AP 信息。
    • 使用仅限本地使用的热点,通过 Wi-Fi 感知和连接功能发现并连接到设备。
    • 通过 Wi-Fi 直连发现和连接到设备。
  • 发起与已知 SSID(例如汽车或智能家居设备)的连接。
  • 开启仅限本地使用的热点。
  • 连接到附近的 Wi-Fi 感知设备。

6、在后台使用身体传感器需要新的权限

Android 13 中引入了“在使用时”访问身体传感器(例如心率、体温和血氧饱和度)的概念。

如果您的应用以 Android 13 为目标平台,并且在后台运行时需要访问身体传感器信息,那么除了现有的 BODY_SENSORS 权限外,您还必须声明新的 BODY_SENSORS_BACKGROUND 权限。

Android 14

1、默认拒绝设定精确的闹钟

精确警报适用于需要在精确时间发生的用户意图的通知或操作。Android 12 中引入的应用安排精确闹钟的权限不再预先授予大多数以 Android 13 及更高版本为目标平台的新安装应用(默认情况下将设置为拒绝)。如果用户通过备份和还原操作将应用数据传输到搭载 Android 14 的设备,则该权限仍会被拒绝。如果现有应用已拥有此权限,则在设备升级到 Android 14 时,系统会预先授予该权限。

需要 SCHEDULE_EXACT_ALARM 权限才能通过以下 API 启动确切警报,否则将引发 SecurityException:

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

提示:如果确切警报是使用 OnAlarmListener 对象设置的,例如在 setExact API 中,则不需要SCHEDULE_EXACT_ALARM权限。

将应用设置为侦听前台广播AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED, 当用户授予权限时,系统会发送

适配流程:
1)调用 alarmManager.canScheduleExactAlarms() 检查是否有该权限;
2)假如没有权限,则通过 Intent,设置 ActionACTION_REQUEST_SCHEDULE_EXACT_ALARM 并加上包名调起设置页面,让用户赋予权限,回来后在 onResume 回调中判别是否权限是否已请求。

2、日历和闹钟应该声明 USE_EXACT_ALARM 权限

日历或闹钟应用需要在应用不再运行时发送日历提醒、唤醒闹钟或警报。这些应用可以请求USE_EXACT_ALARM正常权限。USE_EXACT_ALARM权限将在安装时授予,拥有此权限的应用程序将能够像具有SCHEDULE_EXACT_ALARM权限的应用程序一样安排确切的警报。

可能不需要确切警报的用例:

  • 如果任务需要牢记实时约束,例如在明天下午 2:00 或 30 分钟后关闭,set() 方法很有用。否则,建议改用 handler 的 postAtTime() 或 postDelayed() 方法。

  • 计划的后台工作,例如更新应用和上传日志 WorkManager 提供了一种安排对时间敏感的定期工作的方法。您可以提供重复间隔和 flexInterval(至少 15 分钟)来定义工作的粒度运行时间。

  • 当系统处于空闲状态时,需要警报在大约时间响起 使用不准确的警报。具体来说,调用 setAndAllowWhileIdle()。

  • 用户指定的操作,应在特定时间后发生 使用不准确的警报。具体来说,调用 set()。

  • 可在时间窗口内发生的用户指定的操作 使用不准确的警报。具体来说,调用 setWindow()。请注意,允许的最小窗口长度为 10 分钟。

3、对照片和视频进行独立授权

Android 14 引入了所选照片访问权限,可让用户授权应用访问其媒体库中的特定图片和视频,而不是授予对指定类型的所有媒体内容的访问权限。即独自设置 READ_MEDIA_IMAGESREAD_MEDIA_VIDEO,注意这两个权限仅在Android13及以上才能运行。

新增了一个 READ_MEDIA_VISUAL_USER_SELECTED 权限,归于 Dangerous 等级。用于在用户点击自定义的相片挑选器需求请求拜访相片和视频的权限时运用,这样就不必去请求 READ_MEDIA_IMAGESREAD_MEDIA_VIDEO 这两个权限了。

4、限制前台服务

targetSdkVersion 34 的情况下,必须为应用内的每个前台服务(foreground-services) 指定至少一种前台服务类型。

前台服务类型是在 Android 10 引入的,通过 android:foregroundServiceType 可以指定 <service> 的服务类型,可供选择的前台服务类型有:

如果应用中的用例与这些类型均不相关,强烈建议您迁移逻辑以使用 WorkManager用户发起的数据传输作业

以下代码段提供了一个清单中的前台服务类型声明示例:

<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

5、当应用进入缓存时,动态注册的广播将加入队列

在 Android 14 中,当应用处于缓存状态时,系统可能会将上下文注册的广播放入队列中。这与 Android 12(API 级别 31)为异步 binder 事务引入的队列行为类似。在清单中声明的广播不会加入队列,并且应用会从缓存状态中移除以进行广播传递。

当应用离开缓存状态(例如返回前台)时,系统会传递所有已加入队列的广播。某些广播的多个实例可以合并为一个广播。根据其他因素(例如系统运行状况),系统可能会将应用从缓存状态中移除,并且系统会传送之前加入队列的所有广播。

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

从 Android 14 开始,当您的应用调用 killBackgroundProcesses() 时,该 API 只能终止您自己应用的后台进程。

如果您传入另一个应用的软件包名称,此方法对该应用的后台进程没有影响,并且 Logcat 中会显示以下消息:

Invalid packageName: com.example.anotherapp

7、最低可安装的目标 API 级别

从 Android 14 开始,targetSdkVersion 低于 23 (Android 6.0)的应用无法安装。要求应用满足这些最低目标 API 级别要求有助于提高用户的安全性和隐私性。

8、在 BluetoothAdapter 中强制执行 BLUETOOTH_CONNECT 权限

对于以 Android 14(API 级别 34)为目标平台的应用,Android 14 会在调用 BluetoothAdapter getProfileConnectionState() 方法时强制执行 BLUETOOTH_CONNECT 权限。

此方法已需要 BLUETOOTH_CONNECT 权限,但未被强制执行。请确保应用在应用的 AndroidManifest.xml 文件中声明 BLUETOOTH_CONNECT(如以下代码段所示),并在调用 getProfileConnectionState 之前检查用户是否已授予权限

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

9、对隐式 intent 和待处理 intent 的限制

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

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

这些变更可防止恶意应用拦截意在供应用内部组件使用的隐式 intent。

比如在清单文件中声明:

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

如果应用尝试使用隐式 intent 启动此 activity,则系统会抛出异常:

// Throws an exception when targeting Android 14.  
context.startActivity(Intent("com.example.action.APP_ACTION"))

如需启动非导出的 activity,应用应改用显式 intent:

// This makes the intent explicit.
val explicitIntent = Intent("com.example.action.APP_ACTION")
 explicitIntent.apply {
   package = context.packageName  
}

context.startActivity(explicitIntent)

10、注册的广播接收器必须指定导出行为

以 Android 14 为目标平台并使用上下文注册的接收器的应用和服务必须指定以下标志,以指明接收器是否应导出到设备上的所有其他应用:RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED

val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
val listenToBroadcastsFromOtherApps = false
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
   ContextCompat.RECEIVER_EXPORTED
} else {
   ContextCompat.RECEIVER_NOT_EXPORTED
}
ContextCompat.registerReceiver(context, br, filter, receiverFlags)

例外:
如果您的应用仅通过 Context#registerReceiver 方法(例如 Context#registerReceiver()针对 系统广播 注册接收器,那么它在注册接收器时不应指定标志。

11、更安全的动态代码加载

如果您的应用以 Android 14 为目标平台并使用动态代码加载 (DCL) 功能,则必须将所有动态加载的文件标记为只读。否则,系统会抛出异常。我们建议应用尽可能避免动态加载代码,因为这样做会大大增加应用因代码注入或代码篡改而遭到入侵的风险。

如果必须动态加载代码,请使用以下方法,在动态文件(例如 DEX、JAR 或 APK 文件)打开并写入任何内容之前立即将其设为只读:

val jar = File("DYNAMICALLY_LOADED_FILE.jar")  
val os = FileOutputStream(jar)  
os.use {    
// Set the file to read-only first to prevent race conditions
   jar.setReadOnly()    
// Then write the actual file content  
}  
val cl = PathClassLoader(jar, parentClassLoader)

参考文档

以上只列举了部分关键改动,更多内容详见参考文档

  • 43
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android是一个开源的移动操作系统,由Google开发。它的各个版本都有不同的特性和适配要求。下面是Android各个版本适配情况: 1. Android 1.0:这是Android的首个正式版本,发布于2008年。它主要适配了当时的早期智能手机。 2. Android 1.5 Cupcake:这个版本引入了许多新特性,包括虚拟键盘、文本选择和复制功能等。它的适配要求相对较低,可以在较旧的设备上运行。 3. Android 1.6 Donut:这个版本增加了搜索框和快捷方式等功能。它的适配要求与Cupcake相似。 4. Android 2.0/2.1 Eclair:这个版本引入了许多新特性,包括支持多点触控、HTML5视频播放和Live壁纸等。它的适配要求相对较高,需要较新的设备支持。 5. Android 2.2 Froyo:这个版本引入了许多新特性,包括支持移动热点和Adobe Flash等。它的适配要求与Eclair相似。 6. Android 2.3 Gingerbread:这个版本引入了许多新特性,包括支持NFC和下载管理器等。它的适配要求相对较高,需要较新的设备支持。 7. Android 4.0 Ice Cream Sandwich:这个版本引入了许多新特性,包括全新的用户界面和面部解锁等。它的适配要求相对较高,需要较新的设备支持。 8. Android 4.1/4.2/4.3 Jelly Bean:这个版本引入了许多新特性,包括Google Now和通知增强等。它的适配要求与Ice Cream Sandwich相似。 9. Android 4.4 KitKat:这个版本引入了许多新特性,包括透明状态栏和打印支持等。它的适配要求相对较高,需要较新的设备支持。 10. Android 5.0/5.1 Lollipop:这个版本引入了许多新特性,包括Material Design和多用户支持等。它的适配要求相对较高,需要较新的设备支持。 11. Android 6.0 Marshmallow:这个版本引入了许多新特性,包括指纹识别和运行时权限等。它的适配要求相对较高,需要较新的设备支持。 12. Android 7.0/7.1 Nougat:这个版本引入了许多新特性,包括分屏模式和通知增强等。它的适配要求相对较高,需要较新的设备支持。 13. Android 8.0/8.1 Oreo:这个版本引入了许多新特性,包括自适应图标和通知渠道等。它的适配要求相对较高,需要较新的设备支持。 14. Android 9 Pie:这个版本引入了许多新特性,包括手势导航和应用程序切片等。它的适配要求相对较高,需要较新的设备支持。 15. Android 10:这个版本引入了许多新特性,包括暗黑模式和系统级录屏等。它的适配要求相对较高,需要较新的设备支持。 16. Android 11:这个版本引入了许多新特性,包括聊天气泡和无线Android Auto等。它的适配要求相对较高,需要较新的设备支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值