根据官方应用了解Android 9.0的版本变更内容如下图:
变更目录.png当应用在 Android 9.0 平台上运行时,这些行为变更将影响所有应用,无论这些应用以哪个 API 级别为目标。 所有开发者都应查看这些变更,并修改其应用以正确支持这些变更(如果适用),详细内容参阅https://developer.android.com/about/versions/pie/android-9.0-changes-all?hl=zh-CN。
以 API 级别 28+ 为目标的应用需要注意的行为变更:
变更目录.png Android 9(API 级别 28)向 Android 系统引入了多项变更。 以下行为变更仅影响以 API 28 或更高级别为目标的应用。 将 targetSdkVersion 设为 API 28 或更高级别的应用必须进行修改,以便正确支持这些行为,详细地址https://developer.android.com/about/versions/pie/android-9.0-changes-28?hl=zh-CN。
下面列举出几个经常用到的适配点:
一、限制了HTTP网络请求
Android 9.0中限制了HTTP(明文传输)网络请求,若仍继续使用HTTP请求,日志中会有以下异常:(不会导致应用奔溃,只是无法正常发出请求)
java.net.UnknownServiceException: CLEARTEXT communication to xxx not permitted by network security policy
适配方法如下:
1.在资源目录中新建一个xml文件作为网络安全配置文件,例如xml/network_security_config.xml,然后在文件中填写以下内容:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
在AndroidManifest.xml里面进行配置:
<application...
android:networkSecurityConfig="@xml/network_security_config">
...
</application>
2.Android 6.0中引入了是否允许网络使用明文传输的配置:
<application android:usesCleartextTraffic=["true"|"false"]>
原来默认为true,但是Android 9.0中默认值改成了false,因此将配置手动设置为true即可解决明文传输被限制的问题。
二、弃用Apache HTTP Client
由于官方在 Android 9.0 中移除了所有 Apache HTTP Client 相关的类,因此我们的应用或是一些第三方库如果使用了这些类,就会抛出找不到类的异常:
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/conn/scheme/SchemeRegistry;
若需要继续使用 Apache HTTP Client ,可通过以下方法进行适配:
1.在AndroidManifest.xml中添加一下内容:
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
2.将Apache HTTP Client相关的类引入直接调用。
三、限制非SDK接口的调用
一直以来,官方提供的接口分为了 SDK 接口和非 SDK 接口。SDK 接口即官方支持的接口,开发者可以直接调用不会有任何限制。一般而言,SDK 接口都记录在官方的接口索引中,没有记录的就视为非 SDK 接口,例如一些使用了 @hide 标注的方法。以往开发者对于非 SDK 接口的调用通常是利用反射或者JNI间接调用的方式进行,但这样的调用方式如果处理不当会比较容易出现一些未知的错误。为了提升用户体验和降低应用发生崩溃的风险,Android 9.0 对应用能使用的非 SDK 接口实施了限制,具体的限制手段请见下表:
图片.png
此外,为了开发者能够顺利过渡到 Android 9.0,官方对非 SDK 接口进行了分类,共分为三类: light-greylist(浅灰名单)、dark-greylist(深灰名单)以及blacklist(黑名单)。
- light-greylist(浅灰名单):对于此名单中的非 SDK 接口,官方暂未找到可替代的 SDK 接口,因此开发者仍可继续访问(如果 targetSdkVersion 大于等于28时会出现警告)。
- dark-greylist(深灰名单):targetSdkVersion 小于28时仍可继续使用此名单中的接口,但会出现警告提示;大于等于28时,这些接口将会限制访问。
- blacklist(黑名单):无论 targetSdkVersion 为多少,只要应用运行在 Android 9.0 平台上,访问此名单中的接口都会受限。
如何测试应用是否使用非 SDK 接口,可以通过以下方式进行测试(详情请至官方文档):
1.使用 Android 9.0 或更高版本的设备调试应用
2.使用 StrictMode API 进行测试
3.使用 veridex 工具对应用进行扫描
建议使用第三种方式,该工具的扫描结果会列出应用对于三个限制名单中的接口的调用细节。
四、前台服务权限
在 Android 9.0 中,应用在使用前台服务之前必须先申请 FOREGROUND_SERVICE 权限,否则就会抛出 SecurityException 异常。此外,由于 FOREGROUND_SERVICE 权限只是普通权限,因此开发者只需在 AndroidManifest.xml 中注册此权限即可,系统会自动对此权限进行授权:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
五、强制执行 FLAG_ACTIVITY_NEW_TASK
要求在 Android 7.0(API 级别 24)之前,若开发者需要通过非 Activity context 启动 Activity,就必须设置 Intent 标志 FLAG_ACTIVITY_NEW_TASK,否则会启动失败并抛出以下异常:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
但这个要求在更新 Android 7.0 以后由于系统问题被临时取消了,开发者即使不设置标志也可以正常启动 Activity。而在 Android 9.0 中官方修复了这个问题,这个要求重新开始强制执行,因此开发者在适配 Android 9.0 时需要注意这个问题。
六、不允许共享 WebView 数据目录
Android 9.0 中为了改善应用稳定性和数据完整性,应用无法再让多个进程共用同一 WebView 数据目录。此类数据目录一般存储 Cookie、HTTP 缓存以及其他与网络浏览有关的持久性和临时性存储。如果开发者需要在多进程中使用 WebView,则必须先调用 WebView.setDataDirectorySuffix() 方法为每个进程设置用于存储 WebView 数据的目录。若多进程 WebView 之间需要共享数据,开发者需自己通过 IPC 的方式实现。此外,若开发者只想在一个进程中使用 WebView,并且希望严格执行这个规则,可以通过在其他进程中调用 WebView.disableWebView() 方法,这样其他进程创建 WebView 实例就会抛出异常。
七、其他 API 方面的修改
1.Region.Op相关
Android 9.0 中如果在使用绘图裁剪功能时设置了除 Region.Op.INTERSECT 或 Region.Op.DIFFERENCE 以外的类型,就会抛出以下异常:
java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
具体原因是官方废弃了那几个具有 Region.Op 参数的裁剪方法,如 :
clipRect(@NonNull RectF rect, @NonNull Region.Op op) :
/**
* Modify the current clip with the specified rectangle.
*
* @param rect The rect to intersect with the current clip
* @param op How the clip is modified
* @return true if the resulting clip is non-empty
*
* @deprecated Region.Op values other than {@link Region.Op#INTERSECT} and
* {@link Region.Op#DIFFERENCE} have the ability to expand the clip. The canvas clipping APIs
* are intended to only expand the clip as a result of a restore operation. This enables a view
* parent to clip a canvas to clearly define the maximal drawing area of its children. The
* recommended alternative calls are {@link #clipRect(RectF)} and {@link #clipOutRect(RectF)};
*
* As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
* {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
*/
@Deprecatedpublic boolean clipRect(@NonNull RectF rect, @NonNull Region.Op op) {
checkValidClipOp(op);
return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,op.nativeInt);
}
private static void checkValidClipOp(@NonNull Region.Op op) {
if (sCompatiblityVersion >= Build.VERSION_CODES.P&& op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {
throw new IllegalArgumentException("Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");
}
}
对于这个问题,可以通过以下方法进行适配:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {canvas.clipPath(path);} else {canvas.clipPath(path, Region.Op.XOR);// REPLACE、UNION 等类型}
2.Build.SERIAL 被弃用
Android 9.0 之前,开发者可以使用 Build.SERIAL 获取设备的序列号。现在这个方法被弃用了,Build.SERIAL 将始终设置为 "UNKNOWN" 以保护用户的隐私。
适配的方法:先请求 READ_PHONE_STATE 权限,然后调用 Build.getSerial() 方法。
3.枚举相机
在 Android 9 设备上运行的应用可以通过调用 getCameraIdList()
发现每个可用的摄像头。 应用不应假定设备只有一个后置摄像头或只有一个前置摄像头。
例如,如果您的应用有一个用来切换前置和后置摄像头的按钮,则设备可能有多个前置或后置摄像头可供选择。 您应浏览一下摄像头列表,检查每个摄像头的特征,然后决定向用户显示哪些摄像头。
网友评论