Android 11适配方案变更及适配攻略
总算开始了Android 11的适配作业。记载一下,供需求的人参看。
1. 预备工作
老规矩,首要将咱们项目中的 targetSdkVersion
改为 30。或许运用兼容性调试东西,后边我会提到。
2. 存储机制更新
Scoped Storage(分区存储)
详细适配办法和上一年的Android 10 适配攻略中的没有太大差异。
不过需求留神的是,运用targetSdkVersion >= 30
,强制实施分区存储机制。之前在AndroidManifest.xml
中增加 android:requestLegacyExternalStorage="true"
的适配办法已不起效果。
还有一个改变:Android 11 容许运用除 MediaStore
API 之外的 API 经过文件途径直接拜访同享存储空间中的媒体文件。其间包括:
-
File
API。 - 原生库,例如
fopen()
。
假定你之前没有适配Android 10,这一点对你来说是个好音讯。Android 10在AndroidManifest.xml
中增加 android:requestLegacyExternalStorage="true"
来适配,Android 11上直接运用File
API拜访媒体文件。不得不说,等等党的成功?
不过,运用原始文件途径直接拜访同享存储空间中的媒体文件会重定向到 MediaStore
API,这次重定向会构成功用影响(随机读写慢一倍左右)。而且直接运用原始文件途径,并不会比运用 MediaStore
API 有更多优势,因而官方强烈主张直接运用 MediaStore
API。
MANAGE_EXTERNAL_STORAGE
当然还有一种简略粗暴的适配办法,获取外部存储办理权限。假定你的运用是手机管家、文件办理器这类需求拜访大量文件的app,能够央求MANAGE_EXTERNAL_STORAGE
权限,将用户引导至体系设置页面翻开。代码如下:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
public static void checkStorageManagerPermission(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
!Environment.isExternalStorageManager()) {
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
Android 11 改变及适配攻略
需求留神的是即便你有了MANAGE_EXTERNAL_STORAGE
权限,也无法拜访Android/data/
目录下的文件。
关于MANAGE_EXTERNAL_STORAGE
权限,国内运用应该没有什么影响。可是在Google Play上需求说明为什么已有的SAF
或MediaStore
不满足你的运用需求,审核经过才容许上架运用。所以一般状况下,我个人不推荐你为了适配简略,直接央求运用MANAGE_EXTERNAL_STORAGE
权限。
其他细节改动见文档:Android 11 中的存储机制更新。
相关api改动及运用推荐郭霖大神的这篇:Android 11新特性,Scoped Storage又有了新花样。
存储拜访结构 (SAF)改动
Android 11对SAF增加以下束缚:
- 运用
ACTION_OPEN_DOCUMENT_TREE
或ACTION_OPEN_DOCUMENT
,无法阅读到Android/data/
和Android/obb/
目录及其悉数子目录。 - 运用
ACTION_OPEN_DOCUMENT_TREE
无法授权拜访存储根目录、Download文件夹。
REQUEST_INSTALL_PACKAGES
在8.0的适配中,咱们设备apk包之前需求央求“设备不知道来历运用”的权限。一般来说初次是跳转到授权页面让用户手动翻开,然后回来app进行设备。
在Android 11中当用户打开“设备不知道来历运用”的权限,app就会被杀死。该行为与强制分区存储有关,由于持有 REQUEST_INSTALL_PACKAGES
权限的运用能够拜访其他运用的Android/obb
目录。
好在用户公布权限之后,尽管app会被杀死,可是设备页面仍然会弹出。
现在关于这一改动我没有发现能够适配处理的办法,详细介绍见:Android 11特性调整:设备外部来历运用需求重启APP
3.权限改变
单次权限授权
Android 11 改变及适配攻略从 Android 11 开始,每当运用央求与方位信息、麦克风或摄像头相关的权限时,面向用户的权限对话框会包括仅限这一次选项。假定用户在对话框中挑选此选项,体系会向运用公布暂时的单次授权。
单次权限授权的运用能够在一段时间内拜访相关数据,详细时间取决于运用的行为和用户的操作:
- 当运用的 Activity 可见时,运用能够拜访相关数据。
- 假定用户将运用转为后台作业,运用能够在短时间内继续拜访相关数据。
- 假定您在 Activity 可见时主张了一项前台服务,而且用户随后将您的运用转到后台,那么您的运用能够继续拜访相关数据,直到该前台服务中止。
- 假定用户撤消单次授权(例如在体系设置中撤消),不管您是否主张了前台服务,运用都无法拜访相关数据。与任何权限相同,假定用户撤消了运用的单次授权,运用进程就会中止。
当用户下次翻开运用而且运用中的某项功用央求拜访方位信息、麦克风或摄像头时,体系会再次提示用户公布权限。
假定你之前就是运用权限时才央求相关权限,那么这一改动关于你的运用没有影响。
央求方位权限
这部分在Android 10的适配有过调整,其时规则如下:
央求
ACCESS_FINE_LOCATION
或ACCESS_COARSE_LOCATION
权限标明在前台时具有拜访设备方位信息的权限。在央求弹框中,挑选“一向容许”标明前后台都能够获取方位信息,挑选“仅在运用运用进程中容许”只标明具有前台的权限。
在Android 11中,央求弹框中取消了“一向容许”这一选项。也就是说默许不会公布你后台拜访设备方位信息的权限。假定检验央求ACCESS_BACKGROUND_LOCATION
权限的一同央求任何其他权限,体系会抛出失常,不会向运用公布其间的任一权限。
官方给出的适配主张及原因如下:
主张运用对方位权限实施递加央求,先央求前台方位信息拜访权限,再央求后台方位信息拜访权限。实施递加央求能够为用户供给更大的控制权和透明度,由于他们能够更好地了解运用中的哪些功用需求后台方位信息拜访权限。
总结一下得出两点:
- 先央求前台方位信息拜访权限,再央求后台方位信息拜访权限。
- 单独央求后台方位信息拜访权限,不要与其他权限一同央求。
这儿还需求留神不同方针途径运用在Android 11上的表现:
- Android 10 为方针途径的运用
容许一同拜访前后台的方位信息权限,但相同不会有“一向容许”这一选项。
- 没有前后台的方位信息权限时:
- 有前台的方位信息权限时:
- Android 11 为方针途径的运用
- 没有前后台的方位信息权限时,只能先央求前台的方位信息权限:
- 有前台的方位信息权限,央求后台的方位信息时体系会跳转到下面的设置页面。
挑选“一向容许”标明具有前后台方位信息拜访权限,假定用户回绝两次运用定位拜访央求(直接回来等),后边央求相同权限都会被直接提示央求失利。(这儿就需求咱们给用户以引导了)
这儿解释一下“回绝两次”,这是Android 11 上增加的权限对话框的可见性
,从前咱们点击了“不再问询”标明回绝授权。现在还包括相似上面这种转到体系设置,然后点回来按钮,也算是回绝授权。当然,用户按回来按钮封闭权限对话框,此操作不算。
总结一下,与Android 10的差异就是将后台权限的央求分离了出来,增加了用户“回绝”的条件,避免了运用重复央求用户已回绝的权限。
软件包可见性
软件包可见性是Android 11上提高体系隐私安全性的一个新特性。它的效果是束缚app随意获取其他app的信息和设备状况。避免病毒软件、间谍软件利用,引发网络垂钓、用户设备信息走漏等安全事情。
获取主动可见运用的列表,能够实施指令adb shell dumpsys package queries
,找到 forceQueryable
部分。下面是在vivo iqoo手机的实施效果。
Queries:
system apps queryable: false
forceQueryable:
[com.android.BBKCrontab,com.vivo.fingerprint,com.vivo.epm,com.vivo.abe,com.vivo.fingerprintengineer,com.vivo.contentcatcher,com.vivo.floatingball,com.vivo.agent,com.vivo.nightpearl,android,com.wapi.wapicertmanage,com.vivo.vms,co
m.android.providers.settings,com.vivo.upslide,com.vivo.assistant,com.vivo.vivokaraoke,com.vivo.fingerprintui,com.android.wallpaperbackup,com.bbk.facewake,com.vivo.faceunlock,com.vivo.doubleinstance,com.vivo.audiofx,com.iqoo.powersav
ing,com.bbk.SuperPowerSave,com.vivo.vibrator4d,com.vivo.smartunlock,com.vivo.globalanimation,com.vivo.appfilter,com.vivo.voicewakeup,com.vivo.minscreen,com.android.bbklog,com.mobile.cos.iroaming,com.vivo.networkstate,com.vivo.daemon
Service,com.vivo.smartshot,com.vivo.vtouch,com.android.networkstack.tethering.inprocess,com.android.localtransport,com.vivo.pem,com.vivo.wifiengineermode,com.android.server.telecom,com.vivo.gamecube,com.vivo.aiengine,com.vivo.multin
lp,com.vivo.smartmultiwindow,com.vivo.permissionmanager,com.qti.diagservices,com.vivo.bsptest,com.qti.snapdragon.qdcm_ff,com.vivo.dr,com.vivo.sps,com.android.dynsystem,com.vivo.setupwizard,com.vivo.gamewatch,com.android.keychain,com
.vivo.faceui,com.android.networkstack.inprocess,com.android.location.fused,com.android.inputdevices,com.android.settings,com.iqoo.engineermode,com.vivo.fuelsummary]
[com.qualcomm.uimremoteserver,com.vivo.devicereg,com.qti.qualcomm.deviceinfo,com.volte.config,com.android.mms.service,com.android.ons,com.qualcomm.qcrilmsgtunnel,com.vivo.sim.contacts,com.qualcomm.qti.uimGbaApp,com.qualcomm.qti.
modemtestmode,com.android.stk,com.android.vendors.bridge.softsim,com.qualcomm.uimremoteclient,com.qti.qualcomm.datastatusnotification,com.qualcomm.qti.uim,com.android.phone,com.qualcomm.qti.dynamicddsservice,com.qualcomm.qti.telepho
nyservice,com.android.cellbroadcastservice,com.android.providers.telephony,com.qti.dpmserviceapp,com.android.incallui]
[com.android.vivo.tws.vivotws,com.android.bluetooth]
com.android.nfc
com.android.se
com.android.networkstack.permissionconfig
com.android.shell
com.android.providers.media.module
com.android.wifi.resources.overlay.common
com.android.theme.icon_pack.filled.themepicker
com.android.theme.icon_pack.circular.themepicker
com.android.server.telecom.overlay.common
......
能够看到都是体系运用包名,所以咱们的三方运用默许是不行见的。此项改动影响比较多的是共享支付一类需求与其他运用交互的功用。下面举一个简略的比如:
private static boolean hasActivity(Context context, Intent intent) {
PackageManager packageManager = context.getPackageManager();
return packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
}
public void test() {
Intent intent = new Intent();
intent.setClassName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareImgUI");
Log.d("hasActivity:", hasActivity(this, intent) + "");
}
hasActivity
办法中经过queryIntentActivities
来判别此页面是否存在。可是在targetSdkVersion >= 30
中,这些三方默许都是不行见的。所以都会回来false。相似办法getInstalledPackages
、getPackageInfo
也遭到相应的束缚。
解决办法很简略,在AndroidManifest.xml
中增加queries
元素,里面增加需求可见的运用包名。
<manifest package="com.example.app">
<queries>
<package android:name="com.tencent.mm" /> <- 指定微信包名
</queries>
...
</manifest>
我在适配中用到的还有下面的包名,咱们能够按需增加:
<queries>
<!-- 微博 -->
<package android:name="com.sina.weibo" />
<!-- QQ -->
<package android:name="com.tencent.mobileqq" />
<!-- 支付宝 -->
<package android:name="com.eg.android.AlipayGphone" />
<!-- AlipayHK -->
<package android:name="hk.alipay.wallet" />
</queries>
除了直接增加包名的办法外,咱们能够按intent和provider来增加:
<manifest package="com.example.app">
<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
<provider android:authorities="com.example.settings.files" />
</queries>
...
</manifest>
详细的规则拜见:办理软件包可见性
当然,还有一种简略粗暴的办法,能够直接央求权限QUERY_ALL_PACKAGES
。假定你的运用需求上架Google Play
,那么或许要留神相关方针。为了尊重用户隐私,主张咱们的运用按正常作业所需的最小软件包可见性来适配。
有一点需求说明一下,咱们日常运用的startActivity
办法不受体系软件包可见性行为的影响,即便hasActivity
为false,相同能够跳转。假定咱们在做跳转前,进行相似hasActivity
的判别,那么会受影响。
最终需求留神的是,运用queries
元素需求Android Gradle
插件版别是 4.1及以上,由于旧版其他插件并不兼容此元素,出现合并 manifest
的过错。
前台服务类型
Android 10中,在前台服务拜访方位信息,需求在对应的service
中增加 location
服务类型。
相同的,Android 11中,在前台服务拜访摄像头或麦克风,需求在对应的service
中增加camera
或microphone
服务类型。
<manifest>
...
<service
android:name="MyService"
android:foregroundServiceType="microphone|camera" />
</manifest>
这一束缚的改动,使得程序无法在后台主张服务拜访摄像头和麦克风。如需运用,只能是前台翻开前台服务。除非有如下状况:
- 服务由体系组件主张。
- 服务是经过运用小部件主张。
- 服务是经过与告知交互主张的。
- 服务是
PendingIntent
主张的,它是从另一个可见的运用程序发送过来的。 - 服务由一个运用程序主张,该运用是一个DPC,且在设备悉数者形式下作业。
- 服务由一个供给
VoiceInteractionService
的运用主张。 - 服务由一个具有
START_ACTIVITIES_FROM_BACKGROUND
权限的运用主张。
权限主动重置
假定运用以 Android 11 或更高版别为方针途径而且数月未运用,体系会经过主动重置用户已公布运用的作业时活络权限来保护用户数据。如下图所示:
image.gif留神上图中有一个主张主动重置的开关。假定咱们的运用有特别需求,能够引导用户封闭它。示例代码如下:
public void checkAutoRevokePermission(Context context) {
// 判别是否翻开
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
!context.getPackageManager().isAutoRevokeWhitelisted()) {
// 跳转设置页
Intent intent = new Intent(Intent.ACTION_AUTO_REVOKE_PERMISSIONS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.fromParts("package", context.getPackageName(), null));
context.startActivity(intent);
}
}
SYSTEM_ALERT_WINDOW权限
这部分我在适配中没有用到,直接照搬文档:
在 Android 11 中,体系会依据央求主意向某些类型的运用公布 SYSTEM_ALERT_WINDOW
权限:
-
体系会主意向具有
ROLE_CALL_SCREENING
且央求SYSTEM_ALERT_WINDOW
的悉数运用公布该权限。假定运用失掉ROLE_CALL_SCREENING
,就会失掉该权限。 -
体系会主意向经过
MediaProjection
截取屏幕且央求SYSTEM_ALERT_WINDOW
的悉数运用公布该权限,除非用户已清楚回绝向运用公布该权限。当运用中止截取屏幕时,就会失掉该权限。此用例首要用于游戏直播运用。
这些运用无需发送 ACTION_MANAGE_OVERLAY_PERMISSION
以获取 SYSTEM_ALERT_WINDOW
权限,它们只需直接央求 SYSTEM_ALERT_WINDOW
即可。
MANAGE_OVERLAY_PERMISSION
intent 一向会将用户转至体系权限屏幕
从 Android 11 开始,ACTION_MANAGE_OVERLAY_PERMISSION
intent 一向会将用户转至顶级设置屏幕,用户可在其间公布或撤消运用的 SYSTEM_ALERT_WINDOW
权限。intent 中的任何 package:
数据都会被疏忽。
在更低版其他 Android 中,ACTION_MANAGE_OVERLAY_PERMISSION
intent 能够指定一个软件包,它会将用户转至运用专用屏幕以办理权限。从 Android 11 开始将不再支撑此功用,而是有必要由用户先挑选要公布或撤消哪些运用的权限。此改动能够让权限的公布更有目的性,然后达到保护用户的目的。
读取手机号
假定你是经过TelecomManager
的getLine1Number
办法,或TelephonyManager
的getMsisdn
方法获取电话号码。那么在Android 11中需求增加READ_PHONE_NUMBERS
权限。运用其他办法不受限。
<manifest>
<!-- 假定运用仅在 Android 10及更低版别中运用该权限,能够增加 maxSdkVersion="29" -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"
android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
</manifest>
4.其他行为改动
自定义view的Toast
Android 11
为方针途径的运用,从后台发送自定义view的Toast音讯体系会进行屏蔽。前台运用不受影响。Toast
相应的setView
和 getView
也现已扔掉不主张运用。
假定要在后台运用,推荐运用默许的toast或Snackbar
替代。
APK签名
Android 11
为政策途径的运用,仅经过v1 签名的运用无法在Android 11
的设备上设备或更新。有必要运用v2或更高版别进行签名。
一同Android 11
增加了对 APK 签名方案 v4 的支撑。
AsyncTask
AsyncTask
在Android 11现已不主张运用,主张迁移至kotlin的协程。
此外Handler
未指定Looper
的结构办法也已不主张运用。
主张清楚指定Looper
:
private Handler handler = new Handler(Looper.myLooper());
// 或
private Handler handler = new Handler(Looper.getMainLooper());
5.新增东西
兼容性调试东西
以往咱们做适配的时分,需求先将咱们项目中的 targetSdkVersion
修改为对应版别。这就导致你适配进程中有或许遭到其他改动的影响,而这个新增的兼容性调试东西能够让你在不晋级targetSdkVersion
的状况下,针对每项改动逐一翻开适配。
运用办法:
- 开发者选项中找到运用兼容性改动选项。
- 点击进入找到你需求调试的运用
- 在改动列表中,找到想要翻开或封闭的改动,然后点击相应的开关。
上面榜首行
DEFAULT_SCOPED_STORAGE
就是启用分区储存,这些常量详细的意义见:Android 11 改动列表。
关于兼容性调试东西详细的运用办法见:兼容性结构东西,这儿限于篇幅就不打开说了。
无线调试
Android 11的开发者选项中增加了一个无线调试的功用。相似于联接蓝牙耳机功用,可以无需USB联接线进行日常开发调试作业。(差异于从前的Android WIFI ADB,这个是真无线,哈哈)
image.gif运用办法:
- 开发者选项中找到无线调试并翻开。
- 初次配对需点击“运用配对码配对设备”
- 作业
adb pair ipaddr:port
后输入配对码进行联接。
留神事项:
- 坚持电脑和手机在一个网络。
- Platform Tools 版别需大于30.0。可运用
adb --version
检查。
不过我自己领会下来,感觉联接不是很安稳,不知是AS的问题仍是手机问题。一同锁屏后也会断开联接,领会不是很好。。。等候后续的优化吧。
本篇内容有点多。总结一下,Android 11在权限上的改动比较多,但假定你一向恪守央求权限相关的最佳做法,那么根本上不需求额定的适配作业。
最终侧重一下,关于单次授权,权限对话框的可见性,SYSTEM_ALERT_WINDOW 权限,设备apk这些改动只要在Android 11上就会收效,不管你是否适配Android 11。关于其他改动和API(相机、5G、瀑布屏、键盘等),由于我暂时没有遇到,也就没有列出,有需求的能够点击文末的官方文档链接检查。
截止发这篇博客时,我手机上只发现哔哩哔哩现已适配了Android 11。大多数停留在28、29,更有甚者还在26(Android 8.0 国内上架的最低适配标准)。