Android 9.0适配

Android 9.0适配

  • 一.新功能
    • 1.WiFi RTT室内定位
    • 2.刘海屏支持
      • (1)调试
      • (2)页面的刘海使用模式
      • (3)刘海相关API
      • (4)适配方式
    • 3.解码图片
    • 4.动画
  • 二.所有应用的变更
    • 1.访问受限
    • 2.非SDK接口访问限制
    • 3.安全行为变更
    • 4.UTF解码
    • 5.xt_qtaguid文件访问
    • 6.FLAG_ACTIVITY_NEW_TASK
    • 7.屏幕旋转
    • 8.相机枚举
  • 三.目标(target)API28的变更
    • 1.前台服务
    • 2.隐私权
    • 3.框架安全性
    • 4.Apache Http客户端
    • 5.界面变更
    • 6.通知channel
    • 7.screenOrientation

一.新功能

1.WiFi RTT室内定位

Android 9 添加了对 IEEE 802.11mc Wi-Fi 协议(也称为 Wi-Fi Round-Trip-Time (RTT))的平台支持,从而支持室内定位功能。

https://developer.android.com/about/versions/pie/android-9.0#rtt

2.刘海屏支持

(1)调试

Android 9 增加了对刘海屏的适配支持,在管理员功能->绘制(drawing)->刘海(cutout)设置里可以调整刘海样式,用以测试
Android 9.0适配_第1张图片Android 9.0适配_第2张图片
Android 9.0适配_第3张图片
Android 9.0适配_第4张图片

(2)页面的刘海使用模式

窗口属性layoutInDisplayCutoutMode设置页面对刘海屏的支持模式:

  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:默认模式,页面非全屏模式,刘海区域正常展示(状态栏颜色等);页面全屏模式,不使用刘海区域,内容区域移动以避开刘海区域

  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:页面无论是否全屏模式,刘海区域都不使用(避开)

  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:页面无论是否全屏模式,刘海区域都可以使用,但需要页面使用SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN(沉浸式布局),才可以使layout从刘海区域开始布局

设置代码示例:

//页面全屏
requestWindowFeature(Window.FEATURE_NO_TITLE)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
//沉浸式布局
val decorView = window.decorView
var systemUiVisibility = decorView.systemUiVisibility
val flags = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
             or View.SYSTEM_UI_FLAG_FULLSCREEN)
systemUiVisibility = systemUiVisibility or flags
window.decorView.systemUiVisibility = systemUiVisibility
//页面刘海使用模式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
  this.window.attributes = this.window.attributes.apply {
    layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
  }
}

(3)刘海相关API

有时我们内容扩展到了刘海区域,但是也要适当的避开刘海区域,就需要知道刘海具体的区域了,Android 9 提供了相应的API:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
  val displayCutout = window.decorView.rootWindowInsets?.displayCutout
  if (displayCutout == null) {//为空则没有刘海区域
    ...
  } else {
    //每个刘海区域的位置(Rect对象)
    val cutouts:List<Rect> = displayCutout.boundingRects
    //避开所有刘海的安全区域
    val safeRect = Rect(displayCutout.safeInsetLeft,displayCutout.safeInsetTop,displayCutout.safeInsetRight,SCREEN_HEIGHT - displayCutout.safeInsetBottom)
  }
}
  1. DisplayCutout对象:设备刘海信息对象

  2. DisplayCutout.boundingRects:获取所有刘海区域的区域,每个Rect表示一个刘海区域相对于屏幕的ltrb值

  3. DisplayCutout.safeInsetLeft/T/R/B方法:获取屏幕中间-避开所有刘海区域的最大区域,可以在这个区域内显示不想被刘海遮挡内容,如下图:
    Android 9.0适配_第5张图片
    注意:

  4. 这个区域只是避开了刘海区域,不一定避开了状态栏,因为官方规定是刘海高度小于状态高度,所以可能会造成状态栏遮挡部分内容,需要做适配

  5. 当切换为横屏时,原有的顶部刘海会到左边,相应的safeTop变为safeLeft,其他值同理;但是在onConfigurationChanged()方法内部,无法及时获取最新的safeLTRB,很尴尬。。。

(4)适配方式

  1. 不进行适配,都默认采用LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT模式也可以,只不过对于全屏模式的页面,UI效果不好

  2. 进行适配,可以将全屏模式页面设置为LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES模式,然后利用沉浸式布局,将内容绘制在刘海区域,不过要注意重要内容不要被遮挡,如果遮挡,可以用刘海相关API算出刘海区域,将内容避开刘海区域展示

  3. 最佳实践:https://developer.android.com/guide/topics/display-cutout/

3.解码图片

Android 9 提供ImageDecoder类来代替BitmapFactory和BitmapFactory.Options类,可以通过API实现图片的加载、裁剪、圆角、包括Gif图等处理,对于Gif图(AnimatedImageDrawable)的处理会应用到每一帧

val source = ImageDecoder.createSource(file/byteArray...)
val drawable = ImageDecoder.decodeDrawable(source,{decoder,_,_->
	decoder.setTargetSampleSize(2)//设置裁剪粒度
	//图层绘制                                                   
	decoder.setPostProcessor{canvas->
  	...
  }                                              
})

https://developer.android.com/about/versions/pie/android-9.0#decoding-images

4.动画

Android 9 引入了AnimatedImageDrawable类,用于绘制和显示 GIF 和 WebP 动画图像

@Throws(IOException::class)
private fun decodeImage() {
    val decodedAnimation = ImageDecoder.decodeDrawable(
        ImageDecoder.createSource(resources, R.drawable.my_drawable))
    //start()后,第一帧开始播放
    (decodedAnimation as? AnimatedImageDrawable)?.start()
}

https://developer.android.com/about/versions/pie/android-9.0#animation

二.所有应用的变更

1.访问受限

1.1后台访问限制

  1. 应用处在后台时,不可访问麦克风、摄像头、传感器

  2. 如需使用,可以使用前台服务

1.2通话记录访问限制

  1. READ_CALL_LOG、WRITE_CALL_LOG、PROCESS_OUTGOING_CALLS移到了新的CALL_LOGS权限组

  2. 适配时,解决好权限(组)适配即可,参照6.0权限适配Android 动态权限机制

  3. 通过intent读取电话号时,需要READ_CALL_LOG和READ_PHONE_STATE权限

  4. 通过PhoneStateListener获取电话号时,仅需要READ_CALL_LOG权限

1.3Wifi访问限制

  1. WifiManager的getConnectionInfo()方法的调用,需要ACCESS_WIFI_STATE权限;Android 9 开始,如果要通过返回的WifiInfo对象,获取BSSID和SSID的值,还需要满足:

    • app有ACCESS_FINE_LOCATION或者ACCESS_COARSE_LOCATION权限

    • 开启设备定位服务

    不满足任意一条则得不到正确的id:BSSID:02:00:00:00:00:00 SSID:< unknown ssid>

  2. NETWORK_STATE_CHANGED_ACTION的广播,不再包含SSID、BSSID、连接信息等数据,需要的话使用WifiManager的getConnectionInfo()

1.4电话信息访问限制

Android 9 开始设备定位服务关闭时,TelephonyManager的这些方法不返回结果:

getAllCellInfo()、listen()、getCellLocation()、getNeighboringCellInfo()

2.非SDK接口访问限制

对非SDK方法的调用,无论是通过直接引用、反射引用、JNI引用,都有以下限制:

  1. 浅灰名单中的接口,仍可以调用,debug环境给予logcat警告

  2. 深灰名单中的接口,如果target为28,则与黑名单行为一致;target小于28,仍可以调用,并会在debug环境给予logcat警告

  3. 使用黑名单中的SDK接口,无论target为多少,会有如下异常(崩溃)行为

    https://developer.android.com/about/versions/pie/restrictions-non-sdk-interfaces#results-of-keeping-non-sdk

  4. 经测试,发现部分深灰名单的部分接口并没有异常,比如SystemProperties.getXxx()相关方法

  5. 静态分析工具veridex,可以检测apk中的非SDK接口违规调用

  6. 运行时检测,可以通过观察logcat来发现:Accessing hidden field|method …

  7. veridex和各种名单: https://android.googlesource.com/platform/prebuilts/runtime/+/master/appcompat

    百度网盘下载地址:https://pan.baidu.com/s/1NojiqPjvJMzJ_AuHd5jukQ?errno=0&errmsg=Auth Login Sucess&&bduss=&ssnerror=0&traceid=

  8. 适配时,要确保没有调用黑名单的接口,如果target升为28,那么也要确保没有调用深灰名单的接口,其余的仍可以调用,不过为了向前兼容,能改的最好改了

3.安全行为变更

  1. AES、DESEDE、OAEP、EC和https://www.bouncycastle.org/里的算法参数在Android 9 被废弃,target小于28时给予警告,大于等于28时时抛出NoSuchAlgorithmException

  2. Crypto-Java提供程序Android 9 平台已移除,调用SecureRandom.getInstance(“SHA1PRNG”, “Crypto”),将会发生 NoSuchProviderException

4.UTF解码

在Android 9 或更高版本中,解码修改后的UTF-8/CESU-8序列,请使用DataInputStream.readUTF()函数或NewStringUTF()-JNI函数

5.xt_qtaguid文件访问

  1. Android 9 开始,不允许访问/proc/net/xt_qtaguid文件夹下的文件,与使有没有该文件夹的设备保持一致

  2. 依赖这些文件的TrafficStats 和 NetworkStatsManager API继续工作,其他API不受支持,在不同设备上行为可能不一致

6.FLAG_ACTIVITY_NEW_TASK

  1. Android 9 开始,从非Activity环境启动Activity需要FLAG_ACTIVITY_NEW_TASK的flag,否则不会启动,并在系统中输出日志

  2. Android 9 之前是强制执行,7.0之前如果没有flag则抛出异常,7.0和8.0不传也能启动,是一个系统bug,Android 9 修复了

7.屏幕旋转

  1. Android 9 之前,可以通过通知栏面板或者设置页面,设置屏幕旋转模式:自动旋转模式、纵向模式

  2. Android 9 开始,将纵向模式改为:旋转锁定模式;自动旋转模式行为没有改变

  3. 旋转锁定模式时,用户可以通过底部导航栏的旋转提示按钮,将当前页面旋转为其可以旋转(screenOrientation设置)的方向,而不是永远固定为纵向

  4. sensor(传感器)相关模式和固定方向(如portrait)模式,都会忽略旋转锁定模式:sensor可以任意旋转、固定则不能旋转
    Android 9.0适配_第6张图片
    Android 9.0适配_第7张图片
    Android 9.0适配_第8张图片

8.相机枚举

  1. Android 9 可以通过调用CameraManager.getCameraIdList()方法,检查每个可用摄像头,而不是假设只有一个

  2. App应该合理的决定,向用户展示哪些摄像头

三.目标(target)API28的变更

1.前台服务

  1. target为28时,创建前台服务Service.startForeground(),需要有FOREGROUND_SERVICE权限;不加该权限则SecurityException崩溃

  2. FOREGROUND_SERVICE权限为普通静态权限,在Manifest里声明即可

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    

2.隐私权

target为28时,Build.SERIAL会返回UNKNOWN,可以在申请READ_PHONE_STATE权限后,调用Build.getSerial()获取

3.框架安全性

  1. target为28时,NetworkSecurityPolicy.isCleartextTrafficPermitted()方法默认返回false,如需明文要在网络安全性配置中显示声明为true

  2. target为28时,多个进程不能共享一个Webview数据目录(存放cookie、缓存等持久性数据),需要通过调用WebView.setDataDirectorySuffix()方法为不同进程的WebView设置单独的数据目录

  3. 如需多个进程想访问同一份数据,可以手动在进程间复制数据,如cookie

4.Apache Http客户端

  1. 6.0之前,Apache Http客户端作为标准SDK的一部分,放在bootclasspath中,开发者可以直接使用

  2. 6.0开始,标准SDK不支持Apache Http客户端,从bootclasspath移除,放入系统的可选库中,需要在gradle中使用useLibrary来添加可选库使用

    android {
        compileSdkVersion 28
        useLibrary 'org.apache.http.legacy'
      	...
    }
    

    注意:useLibrary使用同时,最好按照规范,在manifest里配置uses-library,如3所说

  3. Android 9 开始,如果target为28,若想继续使用Apache Http客户端,除了gradle的useLibrary声明外,还必须要在manifest中声明(此项target小于28时,不声明也可):

    <uses-library android:name="org.apache.http.legacy" android:required="false"/>
    
    • required为true:设备系统可选库必须含有此类库,否则PackageManager不允许安装apk;

    • required为false:系统可以安装apk,不保证有此类库;此时需要开发者进行类库存在性保证,如使用反射方式

    • minSdkVersion < 23时,required必须为false,因为6.0以前虽然是没有类库的,但是bootclasspath中有相关类,可以使用Apache Http相关类,如果为true的话6.0以前就安装不了了

5.界面变更

  1. target为28时,0面积View不能再被聚焦(获取焦点)

  2. target为28时,Activity触摸模式下,不再分配初始焦点,需要手动请求requestFocus()

6.通知channel

channel的改动是在8.0系统的,不过android在8.1系统上又有改动,由于很多app都是从targetApi=26直接升到targetApi28,所以可能会直接引发27(8.1)的改动带来的问题,所以channel问题在这里需要被关注一下。

具体问题解析可以参照这篇文章:Android 8.0/8.1channel适配

  1. 8.0开始,要求每个notification需要有channel,且channel应该先创建好,否则没有channel时,notification不会显示,并在debug时会弹toast警告,release时忽略(会报系统logcat)

  2. Android 8.1 开始(target27),如果没有已经存在的channel,则会抛异常(target>=27时直接崩溃);当target是26时,则会在系统进程log-error

  3. 适配时,target>=27时,需要检查所有的notification要有channel设置,且channel必须先创建;target<27时,在8.0及以上的设备,也应该这样做

7.screenOrientation

和上一点一样,也是由于8.1的改动,导致了可能直到9.0适配才会发现的问题。

具体问题解析可以参照这篇文章:Android 8.0/8.1screenOrientation适配

你可能感兴趣的:(知识积累,android相关)