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

分区存储
Android10在应用访问外部存储设备做了限制:
- Android10之前访问外部存储设备,需要申请读写权限(READ_EXTERNAL_STORAGE和WRITE_EXTERANL_STORAGE);
- Android10及更高版本则禁止随意访问外部存储设备,只能访问外部存储设备为每个应用分配的"隔离存储沙盒",且无需读写权限即可访问(context.getExternalFilesDir("name"));读取沙盒中的外的媒体共享文件,比如照片、音乐、视频,需要申请新的媒体权限:READ_MEDIA_IMAGES, READ_MEDIA_VIDEO,READ_MEDIA_AUDIO;
- 如果想停用分区存储:方式一:可以把tagetsdk改到29一下;方式二:taggetsdk>=29,则再mainfest中添加android:requestLegacyExternalStorage="true";
增强了用户对位置权限的控制力
为了控制应用对位置信息的访问权限,Android10新增后台访问定位权限:
- Android10以及更高版本访问位置权限,除非当前应用的Activity可见,否则访问定位视为后台访问,需要申请后台定位访问权限才行();
- Android10版本以下访问定位权限,如果申请了ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION权限,系统会默认 申请ACCESS_BACKGROUND_LOCATION权限;

系统执行后台Activity
从Android10开始,系统增加针对后台启动Activity的限制,启动的Activity是因为用户互动直接引发,比如点击按钮跳转,才不会受到影响。
不可重置的硬件标识符
从Android10开始,应用必须具有READ_PRIVILEGED_PHONE_STATE权限才能访问设备的唯一标识符(包含IMEI和序列号),该权限只提供给系统应用,所以一般应用无法访问硬件标识符。以下是受影响的方法:

- Android10以及更高版本,使用上述方法会抛出SecurityException: getSerial: The user 10180 does not meet the requirements to access device identifiers;
- 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"可避免被重建)