Android10适配

1,117 阅读4分钟

本篇博客仅记录自己为Android10适配做的一些改变,避免以后遗忘花时间百度查找;本文会不定期更新,不会一次性把所有Android10新特性全部适配记录,所以本文有遗漏或有误之处,欢迎大家留言交流。(也可参考Google官方地址)

隐私权限变更

首先,引用Google官方的介绍图

分区存储

Android10在应用访问外部存储设备做了限制:

  1. Android10之前访问外部存储设备,需要申请读写权限(READ_EXTERNAL_STORAGE和WRITE_EXTERANL_STORAGE);
  2. Android10及更高版本则禁止随意访问外部存储设备,只能访问外部存储设备为每个应用分配的"隔离存储沙盒",且无需读写权限即可访问(context.getExternalFilesDir("name"));读取沙盒中的外的媒体共享文件,比如照片、音乐、视频,需要申请新的媒体权限:READ_MEDIA_IMAGES, READ_MEDIA_VIDEO,READ_MEDIA_AUDIO;
  3. 如果想停用分区存储:方式一:可以把tagetsdk改到29一下;方式二:taggetsdk>=29,则再mainfest中添加android:requestLegacyExternalStorage="true";

增强了用户对位置权限的控制力

为了控制应用对位置信息的访问权限,Android10新增后台访问定位权限:

  1. Android10以及更高版本访问位置权限,除非当前应用的Activity可见,否则访问定位视为后台访问,需要申请后台定位访问权限才行();
  2. Android10版本以下访问定位权限,如果申请了ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION权限,系统会默认 申请ACCESS_BACKGROUND_LOCATION权限;

系统执行后台Activity

从Android10开始,系统增加针对后台启动Activity的限制,启动的Activity是因为用户互动直接引发,比如点击按钮跳转,才不会受到影响。

不可重置的硬件标识符

从Android10开始,应用必须具有READ_PRIVILEGED_PHONE_STATE权限才能访问设备的唯一标识符(包含IMEI和序列号),该权限只提供给系统应用,所以一般应用无法访问硬件标识符。以下是受影响的方法:

  1. Android10以及更高版本,使用上述方法会抛出SecurityException: getSerial: The user 10180 does not meet the requirements to access device identifiers;
  2. Android10以下版本,如果有READ_PHONE_STATE权限,可以获取到相应数据,否则会抛出SecurityException;

备注:Google官方推荐获取唯一标识方法

无线扫描权限

Android10以及更高版本,非系统应用无法停用或启动WLAN。

深色模式

Android10引入深色模式

简单适配深色模式

在res下创建名为values-v29的文件夹,创建styles.xml文件,写入代码:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:forceDarkAllowed">true</item>
</style>

这种方式最简单,但只是把界面改成深色,卡片控件的效果则无法看到,如下图:

通过修改主题的方式来适配深色模式

在values-v29/styles.xml或者values/styles.xml中修改代码

<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

这种方式比上面方式要好,但对硬编码的控件无法适配,比如写死背景颜色和文字颜色的状态栏则无法改变

适配硬编码方式的深色模式

需要在res下创建values-night文件夹和colors.xml文件,制定深色模式,如下图状态栏:

自适应适配深色模式

上述硬编码方式可以解决所有的适配问题,但这额外增加了工作量,最好还是能自适应:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="?android:attr/colorBackground"
        tools:context=".ui.activity.ReadWriteActivity">
    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="50sp"
            android:text="hello word"
            android:textColor="?android:attr/textColorPrimary"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
<androidx.constraintlayout.widget.ConstraintLayout/>

关于深色模式的一些辅助方法

/**
* 判断是否是深色主题
*/
fun isDarkTheme(context: Context):Boolean{
    val flag = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
    return flag == Configuration.UI_MODE_NIGHT_YES
}
/**
* 监听深色模式切换
*/
override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    val flag = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
    when(flag) {
        Configuration.UI_MODE_NIGHT_YES  -> Log.d("TAG", "深色模式")
        Configuration.UI_MODE_NIGHT_NO  -> Log.d("TAG", "浅色模式")
    }
}

备注:切换深色模式时,应用程序中所有处于started状态的Activity都会被重建(不在started状态的Activity则会恢复started状态时重新创建,在清单文件中配置android:configChanges="uiMode"可避免被重建)