Android11最全适配实践指南

藏宝库编辑 2024-9-25 11:31:32 28 0 来自 中国

  • Android11 为目标版本的应用(targetSdkVersion>=30才有影响)
  • 全部应用在Android11装备上适配改动(无论targetSdkVersion是多少,只要在Android11装备上运行的应用都有影响)
为什么先说targetSdkVersion>=30的模块呢?因为一样寻常来说为了Google为了让我们更长时间适应新的内容以及保障线上应用的稳固,都会把改动大的,必要花时间适配的内容放到新的targetSdkVersion对应的应用上,假如你临时没有适配targetSdkVersion30的需求,也可以看看第二模块,看看是否有涉及你的应用相干内容。
适配targetSdkVersion30

此模块的修改内容只针对targetSdkVersion 30大概以上才见效。
分区存储欺压实行

对外部存储目次的访问仅限于应用专属目次,以及应用已创建的特定类型的媒体。
关于分区存储,在Android10就已经推行了,简朴的说,就是应用对于文件的读写只能在沙盒情况,也就是属于自己应用的目次内里读写。其他媒体文件可以通过MediaStore举行访问。
但是在android10的时间,Google照旧为开辟者考虑,留了一手。在targetSdkVersion = 29应用中,设置android:requestLegacyExternalStorage="true",就可以不启动分区存储,让从前的文件读取正常利用。但是targetSdkVersion = 30中不行了,欺压开启分区存储。
固然,作为人性化的android,照旧为开辟者留了一小手,假如是覆盖安装呢,可以增长android:preserveLegacyExternalStorage="true",临时关闭分区存储,好让开辟者完成数据迁徙的工作。为什么是临时呢?因为只要卸载重装,就会失效了。以下是关于分区存储会遇到的全部情况,给各人摆列出来了,先上代码:
    fun saveFile() {        if (checkPermission()) {            //getExternalStoragePublicDirectory被弃用,分区存储开启后就不允许访问了            val filePath = Environment.getExternalStoragePublicDirectory("").toString() + "/test3.txt"            val fw = FileWriter(filePath)            fw.write("hello world")            fw.close()            showToast("文件写入乐成")        }    }分情况运行:

  • 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))
那到底应该怎么改呢?三种方法访问文件:

  • 应用专属目次
//分区存储空间val file = File(context.filesDir, filename)//应用专属外部存储空间val appSpecificExternalDir = File(context.getExternalFilesDir(), filename)

  • 访问公共媒体目次文件
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()}

  • 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提供了两个intent入口:

  • 调用ACTION_MANAGE_STORAGE intent 利用查抄可用空间。
  • 调用ACTION_CLEAR_APP_CACHE intent 利用扫除全部缓存。
说来说去,反正应用数据私有化是局势所趋,照旧早点适配分区存储,别等以后手机只有沙盒机制的时间,就来不及了。
媒体文件访问权限

为了在包管用户隐私的同时可以更轻松地访问媒体,Android 11 增长了以下功能。实行批量利用和利用直接文件路径和原生库访问文件。

  • 实行批量利用
    这里的批量利用指的是Android 11 向 MediaStore API 中添加了多种方法,用于简化特定媒体文件更改流程(例如在原位置编辑照片),分别是:


  • createWriteRequest() 用户向应用授予对指定媒体文件组的写入访问权限的哀求。
  • createFavoriteRequest()用户将装备上指定的媒体文件标志为“收藏”的哀求。对该文件具有读取访问权限的任何应用都可以看到用户已将该文件标志为“收藏”。
  • createTrashRequest() 用户将指定的媒体文件放入装备垃圾箱的哀求。垃圾箱中的内容会在体系界说的时间段后被永世删除。
  • createDeleteRequest() 用户立即永世删除指定的媒体文件(而不是先将其放入垃圾箱)的哀求。
直接看个例子:
val urisToModify = listOf(uri,uri,...)val editPendingIntent = MediaStore.createWriteRequest(contentResolver,        urisToModify)// Launch a system prompt requesting user permission for the operation.startIntentSenderForResult(editPendingIntent.intentSender, EDIT_REQUEST_CODE,    null, 0, 0, 0)override fun onActivityResult(requestCode: Int, resultCode: Int,                 data: Intent?) {    when (requestCode) {        EDIT_REQUEST_CODE ->            if (resultCode == Activity.RESULT_OK) {                /* Edit request granted; proceed. */            } else {                /* Edit request not granted; explain to the user. */            }    }}传入uri的聚集,获取用户的同意后,就可以举行利用了。

  • 直接文件路径和原生库访问文件
没错!Android11又规复了利用直接文件路径访问访问媒体文件!哈哈,这样就方便多了。也就是除了 MediaStore API之外尚有两种方式可以访问媒体文件:

  • File API。
  • 原生库,例如 fopen()。
Android10咋办呢??要不就用MediaStore,要不就直接把分区存储关了吧(requestLegacyExternalStorage=true)
全部文件访问权限

固然说了这么多,但是尚有些应用就要访问全部文件,好比杀毒软件,文件管理器。放心,有办法!MANAGE_EXTERNAL_STORAGE 这不来了吗。
这个权限就是用来获取全部文件的管理权限。
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />val intent = Intent()intent.action= Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSIONstartActivity(intent)//判定是否获取MANAGE_EXTERNAL_STORAGE权限:val isHasStoragePermission= Environment.isExternalStorageManager()来张截图过过瘾:
1.jpg 电话号码相干权限

Android 11 更改了您的应用在读取电话号码时利用的与电话相干的权限。
具体改了什么呢?实在就是两个API:

  • TelecomManager 类中的 getLine1Number() 方法
  • TelecomManager 类中的 getMsisdn() 方法
也就是当用到这两个API的时间,原来的READ_PHONE_STATE权限不管用了,必要READ_PHONE_NUMBERS权限才行。
下面具体说说,targetSdkVersion修改到30,然后运行一个获取电话号码的步伐:
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 100)btn2.setOnClickListener {    val tm = this.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager    val phoneNumber = tm.line1Number    showToast(phoneNumber)}瓦解了:
java.lang.SecurityException: getLine1NumberForDisplay: Neither user 10151 nor current process has android.permission.READ_PHONE_STATE, android.permission.READ_SMS, or android.permission.READ_PHONE_NUMBERS预想之中哈,Andmanifest.xml中注册好权限,而且添加动态权限申请:
<uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />ActivityCompat.requestPermissions(this,arrayOf(Manifest.permission.READ_PHONE_STATE,Manifest.permission.READ_PHONE_NUMBERS), 100)搞定,假如你只必要获取手机号码这一个功能,也可以只申请READ_PHONE_NUMBERS这一个权限:
<uses-permission android:name="android.permission.READ_PHONE_STATE"  android:maxSdkVersion="29" /><uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />自界说消息框视图被屏蔽

从 Android 11 开始,已弃用自界说消息框视图。假如您的应用以 Android 11 为目标平台,包罗自界说视图的消息框在从背景发布时会被屏蔽
大概有人会奇怪了,什么是自界说消息框视图啊?我说英文你就知道了,英文是custom toast views,也就是自界说toast。简朴写个代码:
Toast toast = new Toast(context);toast.setDuration(show_length);toast.setView(view);toast.show();糟了糟了,自界说toast被弃用了?我们项目就是用的这个啊!不消担心,只是不允许自界说toast从背景表现了。
好比我写一个3秒后再表现toast,然后应用一打开就进入背景,看看会发生什么:
Handler().postDelayed({      IToast.show("你好,我是自界说toast") }, 3000)W/NotificationService: Blocking custom toast from package com.example.studynote due to package not in the foreground啥也没表现,只是发出来一个告诫。
以是不消太过担心,假如实在必要背景表现,就用平凡的toast吧!
现在必要 APK 署名方案 v2

对于以 Android 11(API 级别 30)为目标平台,且现在仅利用 APK 署名方案 v1 署名的应用,现在还必须利用 APK 署名方案 v2 或更高版本举行署名。用户无法在搭载 Android 11 的装备上安装或更新仅通过 APK 署名方案 v1 署名的应用。
这个先容已经很显着了吧,假如你的targetSdkVersion修改到30,那么你就必须要加上v2署名才行。否则无法安装和更新。
媒体intent利用必要体系默认相机

从 Android 11 开始,只有预装的体系相机应用可以相应以下 intent 利用:

  • android.media.action.VIDEO_CAPTURE
  • android.media.action.IMAGE_CAPTURE
  • android.media.action.IMAGE_CAPTURE_SECURE
    也就是说,假如我调用intent唤起照相机,利用VIDEO_CAPTURE的action,只有体系的相机可以大概相应,而第三方的相机应用不会相应了。
    val intent=Intent()    intent.action=android.provider.MediaStore.ACTION_IMAGE_CAPTURE    startActivity(intent)    //无法唤起第三方相机了,只能唤起体系相机这点对平凡的相机应用照旧有点打击的,官方给的发起是假如要利用特定的第三方相机应用来代表其捕获图片或视频,可以通过为intent设置软件包名称或组件来使这些intent变得明白。
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)}假如是5g网络,就免不了要去判定是不是按流量计费的,否则5G的流量可不是开顽笑的。
检测流量计费方法也很简朴,监听网络,在回调中判定:
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)      }})判定该值,假如为 true,则将毗连视为不按流量计费。
背景位置信息访问权限

在搭载 Android 11 的装备上,当应用中的某项功能哀求在背景访问位置信息时,用户看到的体系对话框不再包罗用于启用背景位置信息访问权限的按钮。如需启用背景位置信息访问权限,用户必须在设置页面上针对应用的位置权限设置划一允许选项。
什么意思呢?重要涉及到两点:

  • 从Android10体系的装备开始,就必要哀求背景位置权限(ACCESS_BACKGROUND_LOCATION),并选择Allow all the time (始终允许)才华得到背景位置权限。Android11装备上再次增强对背景权限的管理,重要体现在体系对话框上,对话框不再提示始终允许字样,而是提供了位置权限的设置入口,必要在设置页面选择始终允许才华得到背景位置权限。
  • 在搭载Android11体系的装备上,targetVersion小于30的时间,可从前台背景位置权限一起申请,而且对话框提供了笔墨说明,表现必要随时获取用户位置信息,进入设置选择始终允许即可。但是targetVersion为30的时间,你必须单独申请背景位置权限,而且要在获取前台权限之后,序次不能乱。而且无任何提示,必要开辟者自己设计提示样式。
大概有点绕,利用几个例子说明:

  • Android10装备,申请前台和背景位置权限(恣意targetSdkVersion):
requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_BACKGROUND_LOCATION), 100)实行效果:


  • Android11装备,targetSdkVersion<=29(Android 10),申请前台和背景位置权限:
requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_BACKGROUND_LOCATION), 100)实行效果:


  • Android11装备,targetSdkVersion=30(Android 11),申请前台和背景位置权限:
requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_BACKGROUND_LOCATION), 100)实行无反应

  • Android11装备,targetSdkVersion=30(Android 11),先申请前台位置权限,后申请背景位置权限:
requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 100)实行效果:

requestPermissions(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), 100)实行效果(直接跳转到设置页面,无任何说明):

以是,该怎么适配呢?

  • targetSdkVersion<30情况下,假如你之前就有判定过前台和背景位置权限,那就无需担心,没有什么必要适配。
  • targetSdkVersion>30情况下,必要分开申请前背景位置权限,而且对背景位置权限申请做好说明和引导,固然也是为了更好的服务用户。
权限申请的demo代码:
val permissionAccessCoarseLocationApproved = ActivityCompat    .checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) ==    PackageManager.PERMISSION_GRANTEDif (permissionAccessCoarseLocationApproved) {   val backgroundLocationPermissionApproved = ActivityCompat       .checkSelfPermission(this, permission.ACCESS_BACKGROUND_LOCATION) ==       PackageManager.PERMISSION_GRANTED   if (backgroundLocationPermissionApproved) {        //前背景位置权限都有   } else {        //申请背景权限        if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.R){            ActivityCompat.requestPermissions(this,                    arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),                    200)        }else{            AlertDialog.Builder(this).setMessage("必要提供背景位置权限,请在设置页面选择始终允许")                    .setPositiveButton("确定", DialogInterface.OnClickListener { dialog, which ->                        ActivityCompat.requestPermissions(this,                                arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),                                200)                    }).create().show()        }   }} else {    if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.R){        //申请前台和背景位置权限        ActivityCompat.requestPermissions(this,                arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_BACKGROUND_LOCATION),                100)    }else{        //申请前台位置权限        ActivityCompat.requestPermissions(this,                arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),                100)    }}软件包可见性

Android 11 更改了应用查询用户已在装备上安装的其他应用以及与之交互的方式。利用新的  元素,应用可以界说一组自身可访问的其他应用。通过告知体系应向您的应用表现哪些其他应用,此元素有助于鼓励最小权限原则。别的,此元素还可资助 Google Play 等应用市肆评估应用为用户提供的隐私权和安全性。
也就是说,Android11中,假如你想去获取其他应用的信息,好比包名,名称等等,不能直接获取了,必须在清单文件中添加<queries>元素,告知体系你要获取哪些应用信息大概哪一类应用。
好比我这段查询应用信息的代码:
val pm = this.packageManagerval listAppcations: List<ApplicationInfo> = pm        .getInstalledApplications(PackageManager.GET_META_DATA)for (app in listAppcations) {    Log.e("lz",app.packageName)}在Android11版本,只能查询到自己应用和体系应用的信息,查不到其他应用的信息了。怎么呢?添加<queries>元素,两种方式:

  • 元素中到场具体包名
<manifest package="com.example.game">    <queries>        <package android:name="com.example.store" />        <package android:name="com.example.services" />    </queries>    ...</manifest>

  • 元素中到场固定过滤的intent
<manifest package="com.example.game">    <queries>        <intent>            <action android:name="android.intent.action.SEND" />            <data android:mimeType="image/jpeg" />        </intent>    </queries></manifest>大概照旧有人会迷惑,那我的应用是浏览器大概装备管理器咋办呢?我就要获取全部包名啊?
放心,Android11还引入了 QUERY_ALL_PACKAGES 权限,清单文件中到场即可。但是Google Play可不肯定能滥用哦,它为必要QUERY_ALL_PACKAGES 权限的应用会提供相干指南,但是还没出来,具体要看背面的消息了。
至于国内市场。。。(渴望能有个应用市场一统天下好好管理这紊乱的市场吧!)
文档访问限定

为让开辟者有时间举行测试,以下与存储访问框架 (SAF) 相干的变更只有在应用以 Android 11 为目标平台时才会见效。
上文存储的时间说过可以通过SAF(存储访问框架--Storage Access Framework)来访问公共目次,但是Android11再次升级,部分目次和文件不能访问了,具体如下:
无法再利用 ACTION_OPEN_DOCUMENT_TREE intent 利用哀求访问以下目次:

  • 内部存储卷的根目次。
  • 装备制造商以为可靠的各个 SD 卡卷的根目次,无论该卡是模仿卡照旧可移除的卡。可靠的卷是指应用在大多数情况下可以乐成访问的卷。
  • Download 目次。
无法再利用 ACTION_OPEN_DOCUMENT_TREEACTION_OPEN_DOCUMENT intent 利用哀求用户从以下目次中选择单独的文件:

  • Android/data/ 目次及其全部子目次。
  • Android/obb/ 目次及其全部子目次。
限定对 APN 数据库的读取访问

以 Android 11 为目标平台的应用现在必须具备 Manifest.permission.WRITE_APN_SETTINGS 特权,才华读取或访问电话提供步伐 APN 数据库。假如在不具备此权限的情况下实验访问 APN 数据库,会天生安全非常。
标题来了,APN是啥?

  • 指一种网络接入技能,是通过手机上网时必须设置的一个参数,APN设置参数包罗名字,运营商编号,APN接入点等等。
就是说假如没有Manifest.permission.WRITE_APN_SETTINGS权限就不能读取APN数据库了,但是!这个权限很早之前就被限定只有体系步伐才华申请这个权限了,现在这个特权没明白到是什么意思,岂非体系步伐都不能随便申请了?
在元数据文件中声明“无停滞”按钮利用情况

从 Android 11 开始,您的无停滞服务无法在运行时声明与体系的“无停滞”按钮的关联。假如您将 AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON 附加到 AccessibilityServiceInfo 对象的 flags 属性,框架就不会将“无停滞”按钮回调事故转达给您的服务。
做过无停滞辅助功能的应该都知道AccessibilityServiceInfo要设置flag为FLAG_REQUEST_ACCESSIBILITY_BUTTON,getAccessibilityButtonController方法获取辅助功能按钮控制器,而且可用于查询辅助功能按钮的状态并注册监听器以举行交互和辅助功能按钮的状态更改。
但是,Android 11开始,这样写不能获取辅助按钮回调事故了,得换成别的一种写法。在元数据文件(通常为 res/raw/accessibilityservice.xml)中利用 flagRequestAccessibilityButton 标志声明您的无停滞服务与“无停滞”按钮的关联。
Firebase JobDispatcher 和 GCMNetworkManager

假如您的应用以 API 级别 30 或更高级别为目标平台,在搭载 Android 6.0(API 级别 23)或更高版本的装备上会停用 Firebase JobDispatcher 和 GcmNetworkManager API 调用。
这两个api国内都用不了,重要用于背景使命。官方给出的更换意见是WorkManager,这个国内是可以用的,属于jetpack组件,重要用于调理和实行可延期的背景工作。
装备到装备文件传输

假如您的应用以 Android 11 为目标平台,您将无法再利用 allowBackup 属性停用应用文件的装备到装备迁徙。体系会主动启用此功能。不外,即使您的应用以 Android 11 为目标平台,您也可以通过将 allowBackup 属性设置为 false 来停用应用文件的云端备份和规复。
android:allowBackup属性

  • 代表是否允许应用到场备份和规复底子架构。假如将此属性设为 false,则永世不会为该应用实行备份或规复,即使是采取全体系备份方法也不破例(这种备份方法通常会通过 adb 生存全部应用数据)。此属性的默认值为 true。
以是这里是不能停用文件的装备到装备迁徙,但是可以停用云端备份和规复
主动重置权限

假如应用以 Android 11 为目标平台而且数月未利用,体系会通过主动重置用户已授予应用的运行时敏感权限来掩护用户数据。此利用与用户在体系设置中检察权限并将应用的访问权限级别更改为拒绝的做法效果一样。假如应用已遵照有关在运行时哀求权限的最佳做法,那么您不必对应用举行任何更改。这是因为,当用户与应用中的功能互动时,您应该会验证相干功能是否具有所需权限。
官方说明说的很清晰了,而且只要应用遵照有关在运行时哀求权限的最佳做法,也就是每次必要调用权限的时间都会去判定,那么就不会有什么标题。
假如必要关闭这个功能怎么办呢?只有引导用户去设置页面关闭了,可以调用包罗Settings.ACTION_APPLICATION_DETAILS_SETTINGS action的 Intent将用户定向到体系设置中应用的页面。
怎么查抄应用是否停用主动重置功能呢?调用 PackageManager的isAutoRevokeWhitelisted()方法。假云云方法返回 true,代表体系不会主动重置应用的权限。
前台服务类型

从 Android 9 开始,应用仅限于在前台访问摄像头和麦克风。为了进一步掩护用户,Android 11 更改了前台服务访问摄像头和麦克风相干数据的方式。假如您的应用以 Android 11 为目标平台而且在某项前台服务中访问这些类型的数据,您必要在该前台服务的声明的 foregroundServiceType 属性中添加新的 camera 和 microphone 类型。
在Android10的时间,对于前台定位服务就必须加上android:foregroundServiceType="location",现在Android11上又增长了两个权限限定,一个是摄像头一个是麦克风。
以是总结下来就是,应用某项前台服务必要访问位置信息、摄像头和麦克风,那么就要在清单文件中这样添加:
<manifest>    <service ...        android:foregroundServiceType="location|camera|microphone" /></manifest>有的朋侪大概测试发现,不加foregroundServiceType的前提下,让Activity启动了一个前台服务,并在服务里去获取定位,竟然可以获取到定位信息,岂非官方说错了?
实在这是因为你并没有让前台服务单独运行,你可以试着在Activity启动Service后,进入Home界面,然后过几秒再哀求位置,就哀求不到了。但是不会瓦解,因为这个被体系设置的权限种别为MODE_IGNORED,也就是静默失败模式。
以是为了保险起见,只要前台服务涉及到了这三个功能,就在清单文件加上android:foregroundServiceType
适配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, "rivate data accessed. " +                    "Operation: $opCode\n " +                    "Attribution TagattributionTag\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 接口之前,我们会尽大概确保提供公开更换方案。
总结

一起分析下来也可以看到,假如是告急的改动,特别是涉及到瓦解的改动照旧放到了targetSdkVersion=30的内容中,这也是每次Android发版的一个潜规则吧,为了最大水平不影响已上线的app所作出的活动。
附件

官网改动先容
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 18:28, Processed in 0.190483 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表