转眼之间就要适配Android 11,记得距离先前写 Android 10 适配手册才过去不久.

  行为变更:所有应用

  这些变化,影响所有的应用,不管你的应用的targetSdkVersion是多少,只要是运行在Android 11 上就会影响,应该更加的关注:

  数据访问初审

  为了使应用及其依赖项访问用户私密数据的过程愈发透明,Android 11 引入了数据访问初审功能。借助此步骤得出的想法,您可以更好地辨识跟纠正或许出现的意外数据访问。

  您的应用可以注册 AppOpsManager.OnOpNotedCallback 实例,该例子可在每天发生以下任一丑闻时执行相应操作:

  应用的代码访问私密数据。为了帮助您确定应用的那个逻辑部份读取了风波,您可以按归因标记初审数据访问。

  依赖库或 SDK 中的代码访问私密数据。

  数据访问初审是在发生数据恳求的句柄上读取的。这意味着,如果应用中的第三方 SDK 或库读取访问私密数据的 API,您的 OnOpNotedCallback 可以读取数据访问初审检测有关该读取的信息。通常,此反弹对象可以通过查看应用的当前状态(例如当前轮询的堆栈轨迹)以判定读取是来自您的应用还是来自 SDK。

  单次授权

  在 Android 11 中,每当应用恳求与位置信息、麦克风或摄像头相关的权限时,面向用户的权限对话框会包含仅限这一次选项。如果用户在对话框中选择此选项,系统会向应用追授临时的单次授权。

  在申请与位置信息、麦克风或摄像头相关的权限时,系统会手动提供一个单次授权的选项,只供这一次权限获取。然后用户上次打开app的时侯,系统会重新提示用户追授权限。这个影响应当不大,只要我们每天使用的时侯都去分辨权限,没有就去申请即可。放一张新版本权限获取款式:

  JobScheduler API 调用限制调试

  JobScheduler任务调度器,可以在设备闲暇时做一些任务处理。Android11中假如你设置为debug方式(debuggable 清单属性设置为 true),超出速度限制的JobScheduler API读取将返回 RESULT_FAILURE。这个有什么用呢?应该可以帮助我们发觉一些功耗问题,感兴趣的可以自己试试。

  顺便提下,Jetpack组件WorkManager只是用到了JobScheduler,不熟悉的朋友可以去了解下,JobScheduler是由SystemServer进程启动的一个系统服务,所以才可以有如此大的权限。

  应用使用状况统计信息

  为了更好地保护用户,Android 11 将每位用户的应用使用状况统计信息储存在凭据加密储存空间中。

  UsageStatsManager是Android提供统计应用使用状况的服务。通过这个服务可以获取指定时间区间内应用使用统计数据、组件状态变化风波统计数据以及软件配置信息统计数据。

  比如queryAndAggregateUsageStats方式,可以获取指定时间区间内使用统计数据,以应用包名为主键进行数据合并。

  但是在Android 11 设备中,系统跟任何应用都难以访问该数据,除非 isUserUnlocked() 返回 true,这发生在出现以下某些状况以后:

  无障碍操作

  在Android相机上有个预安装的屏幕阅读服务,叫做TalkBack,为视力障碍人士或则视力状态不佳的老年人提供。那我们应用为了使这个阅读器才能看懂你的自定义view操作,必须予以自定义控件定义处理程序,包括点击,长按等操作。原来版本或许对于OnTouchListener也支持无障碍触摸丑闻,而在Android 11中,必须专门拟定点击或则长按风波才行了

   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插口限制

  老样子,Android11也会限制一些插口,包括灰名单跟白名单,具体看

  Scudo Hardened Allocator

  Scudo 是一个动态的用户方式显存分配器(也称为堆分配器),旨在抵抗与堆相关的漏洞(如基于堆的缓冲区溢出、释放后再使用跟双重释放),同时保持功耗良好。它提供了标准 C 分配跟取消分配基元(如 malloc 和 free),以及 C++ 基元(如 new 和 delete)

  文件描述符排错程序 (fdsan)

  Android 10 引入了 fdsan(文件描述符排错程序)。fdsan 检测错误处理文件描述符所有权的错误,例如 use-after-close 和 double-close。在 Android 11 中typecho注册用户权限,fdsan 的默认方式发生了变化。现在,fdsan 会在检查至错误时终止,而原先的行为则是记录警告并继续。如果您在应用中发觉因为 fdsan 而造成的崩溃,请参阅 fdsan documentation。

  后面两个对于普通应用开发者虽然不需要如何适配,剩下的假如用到都须要适配

  以 Android 11 为目标平台的应用

  Android 11 更新主要还是集中在隐私控制这块:

  分区储存强制执行

  对外部储存目录的访问仅限于应用专属目录,以及应用已争创的特定类别的媒体。

  分区储存,在Android10就早已实行了,简单的说,就是应用对于文件的读写只好在沙盒环境,也就是属于自己应用的目录底下读写。其他媒体文件可以通过MediaStore进行访问。

  具体可以看这儿

  但是在android10的时侯,Google还是为开发者考虑,留了一手。在targetSdkVersion = 29应用中,设置android:requestLegacyExternalStorage="true",就可以不启动分区储存,让先前的文件加载正常使用。但是targetSdkVersion = 30中不行了,强制开启分区储存。

  同时降低了android:preserveLegacyExternalStorage="true",供覆盖升级的应用使用,它可以暂时关掉分区储存,好使开发者完成数据迁移的工作。但是只要卸载再度重装应用都会失效。

  下面是关于旧版本的储存权限的相关运行情况:

  targetSdkVersion = 28,运行后正常读写。targetSdkVersion = 29,不删掉应用,targetSdkVersion 由28更改至29,覆盖安装,运行后正常读写。targetSdkVersion = 29,删除应用,重新运行,读写报错,程序崩溃(open failed: EACCES (Permission denied))targetSdkVersion = 29,添加android:requestLegacyExternalStorage="true"(不启用分区储存),读写正常不报错targetSdkVersion = 30,不删掉应用,targetSdkVersion 由29更改至30,读写报错,程序崩溃(open failed: EACCES (Permission denied))targetSdkVersion = 30,不删掉应用,targetSdkVersion 由29更改至30,增加android:preserveLegacyExternalStorage="true",读写正常不报错targetSdkVersion = 30,删除应用,重新运行,读写报错,程序崩溃(open failed: EACCES (Permission denied))

  三种方式访问文件:

  1)应用专属目录

   //分区存储空间

    val file = File(context.filesDir, filename)
    //应用专属外部存储空间

  2)访问公共媒体目录文件

   val cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, "${MediaStore.MediaColumns.DATE_ADDED} desc")

    if (cursor != null) {
        while (cursor.moveToNext()) {
            val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
            val uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
            println("image uri is $uri")
        }
        cursor.close()
    }

  3) SAF(存储访问框架--Storage Access Framework)

   val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)

        intent.addCategory(Intent.CATEGORY_OPENABLE)
        intent.type = "image/*"
        startActivityForResult(intent, 100)
        @RequiresApi(Build.VERSION_CODES.KITKAT)
        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            super.onActivityResult(requestCode, resultCode, data)
            if (data == null || resultCode != Activity.RESULT_OK) return
            if (requestCode == 100) {
                val uri = data.data
                println("image uri is $uri")
            }

  媒体文件访问权限

  为了在保证用户隐私的同时可以更轻松地访问媒体,Android 11 增加了以下功能。

  执行批量操作

  这里的批量操作指的是Android 11 向 MediaStore API 中添加了多种方式,用于简化特定媒体文件修改步骤(例如在原位置编辑相片),分别是:

  createWriteRequest() 用户向应用追授对指定媒体文件组的写入访问权限的恳求。

  createFavoriteRequest()用户将设备上指定的媒体文件标记为“收藏”的恳求。对该文件具备调用访问权限的任何应用都可以看见用户已将该文件标记为“收藏”。

  createTrashRequest() 用户将指定的媒体文件装入设备垃圾箱的恳求。垃圾箱中的内容会在系统定义的时间段后被永久删掉。

  createDeleteRequest() 用户立刻永久删掉指定的媒体文件(而不是先将其放到垃圾箱)的恳求。

  具体的使用方式可以看Google提供的相关文档

  所有文件访问权限

  可通过执行以下操作,向用户恳求名为“所有文件访问权限”的特殊应用访问权限:

  在清单中申明 MANAGE_EXTERNAL_STORAGE 权限。

  使用 ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION intent 操作将用户引导到一个系统设置页面,在该页面上,用户可以为应用启用以下选项:授予所有文件的管理权限。

   //判断是否获取MANAGE_EXTERNAL_STORAGE权限:

  MANAGE_EXTERNAL_STORAGE 权限会追授以下权限:

  `注意:/sdcard/Android/media 目录是共享储存空间的一部分。

  对 MediaStore.Files 表的内容的访问权限。`

  获得此权限的应用一直未能访问属于其他应用的应用专用目录,因为这种目录在储存卷上显示为 Android/data/ 的子目录。

  当应用具备 MANAGE_EXTERNAL_STORAGE 权限时,它可以使用 MediaStore API 或文件路径访问很多额外的文件跟目录。但是,当您使用储存访问框架时,只有在您不具备 MANAGE_EXTERNAL_STORAGE 权限也能访问文件或目录的状况下才会访问文件或目录。

  自动重置权限

  如果应用以 Android 11 为目标平台甚至数月未使用,系统会通过手动重置用户已追授应用的运行时敏感权限来保护用户数据。此操作与用户在系统设置中查看权限并将应用的访问权限级别修改为抵制的做法疗效一样。如果应用已依照有关在运行时恳求权限的最佳做法,那么您何必对应用进行任何修改。这是因为,当用户与应用中的功能互动时,您应当会验证相关功能是否具备所需权限。

  请求用户停用手动重置功能

  将用户引导到系统设置中您应用的页面,请读取包含 Intent.ACTION_AUTO_REVOKE_PERMISSIONS intent 操作的 intent。在此屏幕中,用户可以通过执行以下操作来阻挡系统重置应用的权限:

  点按权限,系统会读取应用权限设置屏幕。

  确定是否已停用手动重置功能

  调用 isAutoRevokeWhitelisted()。如果此方式返回 true,则系统不会手动重置应用的权限。。

  电话号码相关权限

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

  具体改了哪些呢?其实就是两个API:

  TelephonyManager 类中不受支持的 getMsisdn() 方法。

  如果您的应用申明 READ_PHONE_STATE

  也就是当用到这两个API的时侯,原来的READ_PHONE_STATE权限不好使了,需要READ_PHONE_NUMBERS权限才行。

  系统告诫窗口变更

  从 Android 11 开始,已弃用自定义消息框视图。如果您的应用以 Android 11 为目标平台,包含自定义视图的消息框在从后台公布时会被屏蔽

  也就是自定义Toast被启用了。如果在后台使用了自定义Toast会有一个警告,不过这儿正好有一个第三方库,兼容Android 11 新API的

  (这个库和我没啥关系typecho注册用户权限,刚耐看至而已 ^_^)

  相机

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

  android.media.action.VIDEO_CAPTURE

  android.media.action.IMAGE_CAPTURE

  android.media.action.IMAGE_CAPTURE_SECURE

  如果有多个预装的系统手机应用可用,系统会显示一个对话框,供用户选择应用。如果您希望自己的应用使用特定的第三方相机应用来代表其捕捉图片或视频,可以通过为 intent 设置软件包名称或组件来让那些 intent 变得明晰。

  5G

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

  5G 检测

  从 Android 11 开始,您可以使用基于反弹的 API 调拿来检验设备是否连结至了 5G 网络。您可以检测连结的是 5G NR(独立)网络,还是 NSA(非独立)网络。

  调用 TelephonyManager.listen() 并传到 LISTEN_DISPLAY_INFO_CHANGED,以确定用户是否连结至了 5G 网络。替换 onDisplayInfoChanged() 方法,以确定应用连结至的网路类别:

  |返回类别| 网路|

  | :- | :- |

  |OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO| 初级专业版 LTE (5Ge)|

  |OVERRIDE_NETWORK_TYPE_NR_NSA| NR (5G) - 5G Sub-6 网络|

  |OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE| 5G+/5G UW - 5G mmWave 网络|

  检查按流量计费性

   NetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) ||

  如果值为 true,则您可以将网路视为不按流量收费。

  后台位置信息访问权限

  在配备 Android 11 的设备上,当应用中的某项功能恳求在后台访问位置信息时,用户听到的系统对话框不再包含适于启用后台位置信息访问权限的按键。如需启用后台位置信息访问权限,用户应当在设置页面上针对应用的位置权限设置一律容许选项。

  主要牵涉至两点:

  可能有点绕,操作几个举例说明:

  Android10设备,申请前台跟后台位置权限(任意targetSdkVersion):

  requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_BACKGROUND_LOCATION), 100)

  执行疗效:

  Android11设备,targetSdkVersion

最后修改:2021 年 02 月 12 日 10 : 44 AM
如果觉得我的文章对你有用,请随意赞赏