Android 9.0 适配指南

又到了我一年一度写Android适配文章的时间,自己这篇应该会早几个月发出来,可是前两三个月主要忙于Flutter的项目,因此这篇文章才姗姗来迟。不过毕竟是9.0的适配,还不算太晚哈!php

1.前言

国内从去年开始就有消息说,应用上架或者更新要求TargetSdkVersion最低要为26以上,也就是最低也要适配到8.0。今年来也都逐步地开始落实。好比下图的小米应用商店公告html

在这里插入图片描述
固然 Google Play的要求更为严格:
在这里插入图片描述

还包括从8月份开始在Google Play上发布的应用必须支持64位架构。能够看到适配工做真的不能像之前同样为所欲为了。好在我以前也有写过相关的适配攻略,Android适配系列:java

2.准备工做

进入正题,首先将咱们项目中的targetSdkVersion改成 28。接下来运行你的项目,看有没中枪。安全

3.网络

1.Http请求失败

在9.0中默认状况下启用网络传输层安全协议 (TLS),默认状况下已停用明文支持。也就是不容许使用http请求,要求使用https。网络

好比我使用的是okhttp,会报错:架构

java.net.UnknownServiceException: CLEARTEXT communication to xxxx not permitted by network security policy
复制代码

解决方法是须要咱们添加网络安全配置。首先在 res 目录下新建xml文件夹,添加network_security_config.xml文件:app

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
复制代码

AndroidManifest.xml中的application添加:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config">
            ...
    </application>
</manifest>
复制代码

以上这是一种简单粗暴的配置方法,要么支持http,要么不支持http。为了安全灵活,咱们能够指定支持的http域名:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
	<!-- Android 9.0 上部分域名时使用 http -->
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">secure.example.com</domain>
        <domain includeSubdomains="true">cdn.example1.com</domain>
    </domain-config>
</network-security-config>
复制代码

固然不止这些配置,还有抓包配置、设置自定义CA以及各类场景下灵活的配置,详细的方法能够查看官方文档

2.Apache HTTP 客户端弃用

在 Android 6.0 时,就已经取消了对 Apache HTTP 客户端的支持。 从 Android 9.0 开始,默认状况下该库已从 bootclasspath 中移除。可是耐不住有些SDK中还在使用,好比我见到的友盟QQ分享报错问题

因此要想继续使用Apache HTTP,须要在应用的 AndroidManifest.xml 文件中添加:

<uses-library android:name="org.apache.http.legacy" android:required="false"/>
复制代码

4.前台服务

能够试着搜索一下你的代码,看是否有调用startForegroundService 方法来启动一个前台服务。

startForegroundService 主要来源估计都是8.0适配时候加上的:

Intent intentService = new Intent(this, MyService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    startForegroundService(intentService);
} else {
    startService(intentService);
}
复制代码

9.0 要求建立一个前台服务须要请求 FOREGROUND_SERVICE 权限,不然系统会引起 SecurityException

java.lang.RuntimeException: Unable to start service com.weilu.test.MyService@81795be with Intent { cmp=com.weilu.test/.MyService }: 
java.lang.SecurityException: Permission Denial: startForeground from pid=28631, uid=10626 requires android.permission.FOREGROUND_SERVICE
        at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3723)
        at android.app.ActivityThread.access$1700(ActivityThread.java:201)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1705)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:207)
        at android.app.ActivityThread.main(ActivityThread.java:6820)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876)
复制代码

解决方法就是AndroidManifest.xml中添加FOREGROUND_SERVICE权限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
复制代码

5.启动Activity

在9.0 中,不能直接非 Activity 环境中(好比ServiceApplication)启动 Activity,不然会崩溃报错:

java.lang.RuntimeException: Unable to create service com.weilu.test.MyService: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want? at android.app.ActivityThread.handleCreateService(ActivityThread.java:3578) at android.app.ActivityThread.access$1400(ActivityThread.java:201) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1690) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:207) at android.app.ActivityThread.main(ActivityThread.java:6820) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876) 复制代码

这类问题通常会在点击推送消息跳转页面这类场景,解决方法就是 Intent 中添加标志FLAG_ACTIVITY_NEW_TASK

Intent intent = new Intent(this, TestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
复制代码

6.异形屏适配

这类异形屏叫法不少,刘海屏、水滴屏、挖孔屏、美人尖。。。

  1. 其实若是你的页面不须要全屏显示,那么不须要额外的适配工做。

  2. 若是页面是全屏显示(好比启动页)。为了防止你的内容被遮挡,大部分场景下都是可使用获取状态栏高度来处理遮挡的适配问题。由于状态栏的高度都是大于等于刘海的高度。

固然,若是你想利用起来刘海区域,就须要获取刘海位置等信息进行适配。在Android 9.0中官方提供了DisplayCutout 类,能够肯定刘海区域的位置,国内的部分厂商在8.0就有了本身的适配方案。

具体的我就不过多介绍了,推荐你们看如下文章:

7.权限

首先是权限组的变动:

上图能够看到,在9.0 中新增权限组CALL_LOG 并将 READ_CALL_LOGWRITE_CALL_LOGPROCESS_OUTGOING_CALLS 权限从PHONE中移入该组。

1.限制访问通话记录

若是应用须要访问通话记录或者须要处理去电,则您必须向 CALL_LOG权限组明确请求这些权限。 不然会发生 SecurityException

2.限制访问电话号码

  • 要经过 PHONE_STATE Intent 操做读取电话号码,同时须要 READ_CALL_LOG 权限和 READ_PHONE_STATE 权限。
  • 要从 PhoneStateListener的onCallStateChanged() 中读取电话号码,只须要 READ_CALL_LOG 权限。 不须要 READ_PHONE_STATE 权限。

8.其余

  • 在 Android 9 中,调用Build.SERIAL 会始终返回 UNKNOWN 以保护用户的隐私。若是你的应用须要访问设备的硬件序列号,那么须要先请求 READ_PHONE_STATE 权限,而后调用 Build.getSerial()

  • 注意非 SDK 接口的限制。主要是一些热修复、插件化框架涉及比较多,注意及时升级新版本。

  • 多进程使用WebView注意没法共用同一数据目录。 详细点击查看


总的来讲,9.0的适配工做须要改动和注意的点相比较之前版本的适配来讲并很少,从本篇的篇幅就能够看出来,详细的变化能够参看文末的连接。后面若是遇到什么坑,我也会及时补充进来。感谢你的阅读!!

参考

相关文章
相关标签/搜索