适配Android11手机
此模块的修改内容针对所有项目在Android11
手机上存在的改动,与targetSdkVersion
无关。
数据访问审核 ⭐
“为了让应用及其依赖项访问用户私密数据的过程更加透明,Android 11 引入了数据访问审核功能。借助此流程得出的见解,您可以更好地识别和纠正可能出现的意外数据访问。
”
哪些范畴属于用户私密数据呢?其实就是危险权限的调用,所以这个功能就是提供了可以监听危险权限调用的监听。主要涉及到的方法是AppOpsManager.OnOpNotedCallback
。无论是应用本身,还是依赖库或者SDK中的代码,只要访问到私密数据(危险权限),都会回调给我们。
对于工程庞大或者使用较多SDK的工程比较适合用上这个功能,让自己应用的私有数据管理更加透明规范
,否则对于私有数据的使用和管理并不全面和方便。而且还可以对权限使用添加归因
,也就是一个tag,标志权限用到了什么地方。方便回调的时候知晓哪里使用了私有数据
。
🌰来:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_test1) //创建归因(attribute) attributionContext = createAttributionContext("shareLocation") //监听事件 val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() { private fun logPrivateDataAccess( opCode: String, attributionTag: String, trace: String) { Log.i(TAG, "Private data accessed. " + "Operation: $opCode\n " + "Attribution Tag:$attributionTag\nStack Trace:\n$trace") } override fun onNoted(syncNotedAppOp: SyncNotedAppOp) { syncNotedAppOp.attributionTag?.let { logPrivateDataAccess(syncNotedAppOp.op, it, Throwable().stackTrace.toString()) } } override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) { syncNotedAppOp.attributionTag?.let { logPrivateDataAccess(syncNotedAppOp.op, it, Throwable().stackTrace.toString()) } } override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) { asyncNotedAppOp.attributionTag?.let { logPrivateDataAccess(asyncNotedAppOp.op, it, asyncNotedAppOp.message) } } } //开启私密数据监听 val appOpsManager = getSystemService(AppOpsManager::class.java) as AppOpsManager appOpsManager.setOnOpNotedCallback(mainExecutor, appOpsCallback) btn1.setOnClickListener { getLocation() } } fun getLocation() { val locationManager = attributionContext.getSystemService( LocationManager::class.java) as LocationManager if (!checkPermission()) { return } val location: Location? = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) if (location != null) { showToast("${location.latitude}") } }
该例子主要展示了一个获取位置信息
的功能,如果调用到getLocation
方法,就会触发onNoted
回调,回调信息包括危险权限code以及归因。
其中OnOpNotedCallback 一共三个回调方法:
onNoted
正常情况下都会回调到该方法onAsyncNoted
如果数据访问并非发生在应用调用API期间,就会调用onAsyncNoted(),比如一些监听器的回调。onSelfNoted
在极少数情况下,如果应用将自身的UID传递到 noteOp(),需要调用 onSelfNoted()。
最后点击按钮,看下回调的结果日志:
Private data accessed. Operation: android:coarse_location Attribution Tag:shareLocation Stack Trace: [Ljava.lang.StackTraceElement;@14f5a16
可以看到权限代码:android:coarse_location
以及归因 shareLocation
单次授权
“在 Android 11 中,每当应用请求与位置信息、麦克风或摄像头相关的权限时,面向用户的权限对话框会包含仅限这一次选项。如果用户在对话框中选择此选项,系统会向应用授予临时的单次授权。
”
简单的说,就是在申请与位置信息、麦克风或摄像头
相关的权限时,系统会自动提供一个单次授权
的选项,只供这一次权限获取。然后用户下次打开app的时候,系统会再次提示用户授予权限。这个影响应该不大,只要我们每次使用的时候都去判断权限,没有就去申请即可。放一张新版本权限获取样式:
权限对话框的可见性
“Android 11 建议不要请求用户已选择拒绝的权限。在应用安装到设备上后,如果用户在使用过程中屡次针对某项特定的权限点按拒绝,此操作表示其希望“不再询问”。
”
这个都算不上改动,只是官方的一个良好建议
。建议在用户多次拒绝之后,不要再展示权限申请。
Scudo Hardened Allocator
“Android 11 在内部使用 Scudo Hardened Allocator 为堆分配提供服务。Scudo 能够检测并减轻某些类型的内存安全违规行为。如果您在原生代码崩溃报告中发现与 Scudo 相关的崩溃(例如 Scudo ERROR:),请参阅 Scudo 问题排查文档。
”
Scudo
是一种动态的用户模式内存分配器,旨在抵御与堆相关的漏洞,同时保持良好的性能。它是一个开源的项目。Android 11中,将采用这个新的heap分配器
,性能更好,更安全。
文件描述符排错程序
“Android 10 引入了 fdsan(文件描述符排错程序)。fdsan 检测错误处理文件描述符所有权的错误,例如 use-after-close 和 double-close。在 Android 11 中,fdsan 的默认模式发生了变化。现在,fdsan 会在检测到错误时中止,而以前的行为则是记录警告并继续。
”
问题来了,fdsan
是啥?先要了解fd是啥
文件描述符(FileDescriptor) 是Unix/Linux
系统文件操作的相关概念,它在形式上是一个非负整数。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。系统的进程也就是使用了这个fd
来标示打开的文件,有了它就能对文件做各种操作,获得文件的各种相关信息了。
所以fdsan
也就是检测文件处理中发生的一些错误。
应用使用情况统计信息
“为了更好地保护用户,Android 11 将每个用户的应用使用情况统计信息存储在凭据加密存储空间中。
”
这就涉及到了UsageStatsManager
,UsageStatsManager
是Android提供统计应用使用情况的服务。通过这个服务可以获取指定时间区间内应用使用统计数据、组件状态变化事件统计数据以及硬件配置信息统计数据。
比如queryAndAggregateUsageStats
方法,可以获取指定时间区间内使用统计数据,以应用包名为键值进行数据合并。
但是在Android 11 设备中,不好意思,不能随意使用这些信息了。只有当isUserUnlocked()
方法返回true的时候,才能正常访问这些数据。也就是以下两种情况:
- 用户在系统启动后首次解锁其设备
- 用户在设备上切换到自己的帐号
JobScheduler API 调用限制调试
JobScheduler
任务调度器,可以在设备空闲时做一些任务处理。Android11中如果你设置为debug模式
(debuggable 清单属性设置为 true),超出速率限制的JobScheduler API
调用将返回 RESULT_FAILURE
。这个有什么用呢?应该可以帮助我们发现一些性能问题,感兴趣的可以自己试试。
顺便提下,Jetpack组件WorkManager
也是用到了JobScheduler,不熟悉的同学可以去了解下,JobScheduler
是由SystemServer进程启动的一个系统服务,所以才可以有这么大的权限。
无障碍操作
“在以前的 Android 版本中,框架会向未正确处理基于点击的无障碍操作的微件分派触摸事件。通常,这些视图会直接处理触摸事件,而不是注册点击监听器。为了在正确定义无障碍操作的应用中创建更一致的行为,Android 11 绝不会分派触摸事件。相反,系统会完全依赖于基于点击的无障碍操作:ACTION_CLICK 和 ACTION_LONG_CLICK。此更改会影响屏幕阅读器的行为。
”
在Android
手机上有个预安装的屏幕阅读服务,叫做TalkBack
,为视力障碍人士或者视力状态不佳的老年人提供。那我们应用为了让这个阅读器能够读懂你的自定义view操作,必须给与自定义控件定义处理程序,包括点击,长按
等操作。原来版本可能对于OnTouchListener
也支持无障碍触摸事件,而在Android11
中,必须专门制定点击或者长按事件才行了。给个🌰:
class TriSwitch(context: Context) : Switch(context) { // 0, 1, or 2. var currentState: Int = 0 private set init { updateAccessibilityActions() } private fun updateAccessibilityActions() { ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK, action-label) { view, args -> moveToNextState() }) } private fun moveToNextState() { currentState = (currentState + 1) % 3 } }
一个自定义控件TriSwitch
,继承自Switch,由于和Switch
的点击效果不一样,所以必须通过替换 ViewCompat.replaceAccessibilityAction()
来重新定义相应的无障碍操作。
非SDK接口限制
“Android 11 包含更新后的受限制非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。在限制使用非 SDK 接口之前,我们会尽可能确保提供公开替代方案。
”
老样子,Android11也会限制一些接口,包括灰名单和白名单,具体看非SDK接口列表
总结
一路分析下来也可以看到,如果是重要的改动,特别是涉及到崩溃的改动还是放到了targetSdkVersion=30
的内容中,这也是每次Android发版的一个潜规则吧,为了最大程度不影响已上线的app所作出的举动。
但是,这并不意味我们就可以不改。因为应用可拖不起,用户可拖不起,毕竟升级才能给到用户最好的体验
。而且各大应用市场也都会建议或者强制应用升级targetSdkVersion
,以便适配最新的手机。
所以,行动吧。
谢谢你的阅读,如果你觉得写的还行,就点个赞支持下吧!感谢!
你的一个👍,就是我分享的动力❤️。