版权声明:本文为博主原创文章,未经博主容许不得转载
文章分类:Android知识体系 - 版本适配
复制代码
本文主要是从官方文档中筛选出一些常见的适配项,如有任何纰漏或须要补充的,欢迎你们在评论区指出。php
Android 9.0 中限制了 HTTP(明文传输)网络请求,若仍继续使用HTTP请求,则会在日志中提示如下异常(只是没法正常发出请求,不会致使应用崩溃):java
java.net.UnknownServiceException: CLEARTEXT communication to xxx not permitted by network security policy
复制代码
适配的方法以下:android
第一种apache
在资源目录中新建一个 xml 文件做为网络安全配置文件,例如 xml/network_security_config.xml,而后在文件中填写如下内容:canvas
<?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>
复制代码
第二种(感谢 AllenChiang 同窗的提醒)安全
Android 6.0 中引入了是否容许网络使用明文传输的配置:bash
<application android:usesCleartextTraffic=["true" | "false"]>
复制代码
原来默认为 true,但在 Android 9.0 中默认值改成了 false,所以将配置手动设为 true 便可解决明文传输被限制的问题网络
因为官方在 Android 9.0 中移除了全部 Apache HTTP Client 相关的类,所以咱们的应用或是一些第三方库若是使用了这些类,就会抛出找不到类的异常:app
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/conn/scheme/SchemeRegistry;
复制代码
若须要继续使用 Apache HTTP Client ,可经过如下方法进行适配:
在 AndroidManifest.xml 中添加如下内容:
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
复制代码
或者在应用中直接将 Apache HTTP Client 相关的类打包并进行引用
一直以来,官方提供的接口分为了 SDK 接口和非 SDK 接口。SDK 接口即官方支持的接口,开发者能够直接调用不会有任何限制。通常而言,SDK 接口都记录在官方的接口索引中,没有记录的就视为非 SDK 接口,例如一些使用了 @hide
标注的方法。
以往开发者对于非 SDK 接口的调用一般是利用反射或者JNI间接调用的方式进行,但这样的调用方式若是处理不当会比较容易出现一些未知的错误。为了提高用户体验和下降应用发生崩溃的风险,Android 9.0 对应用能使用的非 SDK 接口实施了限制,具体的限制手段请见下表:
此外,为了开发者可以顺利过渡到 Android 9.0,官方对非 SDK 接口进行了分类,共分为三类,light-greylist(浅灰名单)、dark-greylist(深灰名单)以及blacklist(黑名单):
能够经过如下方式进行测试(详情请至官方文档):
建议使用第三种方式,该工具的扫描结果会列出应用对于三个限制名单中的接口的调用细节。
在 Android 9.0 中,应用在使用前台服务以前必须先申请 FOREGROUND_SERVICE
权限,不然就会抛出 SecurityException 异常。
此外,因为 FOREGROUND_SERVICE
权限只是普通权限,所以开发者只需在 AndroidManifest.xml 中注册此权限便可,系统会自动对此权限进行受权:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
复制代码
在 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 时须要注意这个问题。
Android 9.0 中为了改善应用稳定性和数据完整性,应用没法再让多个进程共用同一 WebView 数据目录。此类数据目录通常存储 Cookie、HTTP 缓存以及其余与网络浏览有关的持久性和临时性存储。
若是开发者须要在多进程中使用 WebView,则必须先调用 WebView.setDataDirectorySuffix()
方法为每一个进程设置用于存储 WebView 数据的目录。若多进程 WebView 之间须要共享数据,开发者需本身经过 IPC 的方式实现。
此外,若开发者只想在一个进程中使用 WebView,而且但愿严格执行这个规则,能够经过在其余进程中调用 WebView.disableWebView()
方法,这样其余进程建立 WebView 实例就会抛出异常。
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. */
@Deprecated
public 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 等类型
}
复制代码
Android 9.0 以前,开发者可使用 Build.SERIAL
获取设备的序列号。如今这个方法被弃用了,Build.SERIAL
将始终设置为 "UNKNOWN" 以保护用户的隐私。
适配的方法为先请求 READ_PHONE_STATE
权限,而后调用 Build.getSerial()
方法。