精华内容
下载资源
问答
  • Android 9.0适配

    千次阅读 2019-08-01 10:08:26
    Android 9.0适配一.新功能1.WiFi RTT室内定位2.刘海屏支持(1)调试(2)页面的刘海使用模式(3)刘海相关API(4)适配方式3.解码图片4.动画二.所有应用的变更1.访问受限2.非SDK接口访问限制3.安全行为变更4.UTF解码5.xt_...

    一.新功能

    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)设置里可以调整刘海样式,用以测试
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    (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方法:获取屏幕中间-避开所有刘海区域的最大区域,可以在这个区域内显示不想被刘海遮挡内容,如下图:
      在这里插入图片描述
      注意:

    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可以任意旋转、固定则不能旋转
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

    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 9.0适配指北

    2020-04-26 18:04:11
    Android 9.0系统中提供了3种layoutInDisplayCutoutMode属性来允许应用自主决定该如何对刘海屏设备进行适配。 LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 这是一种默认的属性,在不进行明确指定的情况下,系统会自动...

    一、刘海屏适配

    1、layoutInDisplayCutoutMode属性

    Android 9.0系统中提供了3种layoutInDisplayCutoutMode属性来允许应用自主决定该如何对刘海屏设备进行适配。

    • LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
      这是一种默认的属性,在不进行明确指定的情况下,系统会自动使用这种属性。这种属性允许应用程序的内容在竖屏模式下自动延伸到刘海区域,而在横屏模式下则不会延伸到刘海区域
    • LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
      这种属性表示,不管手机处于横屏还是竖屏模式,都会允许应用程序的内容延伸到刘海区域
    • LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 这种属性表示,永远不允许应用程序的内容延伸到刘海区域

    2、配置layoutInDisplayCutoutMode的方式

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
    }
    

    layoutInDisplayCutoutMode使用默认值,即我们不去设置。即使我们不做任何的适配工作,绝大多数的程序在默认情况下也是可以自动适配刘海屏手机的,并不会产生应用程序无法使用等问题的发生。
    但是,假如你开发的是一款视频类应用或者游戏的话(横屏显示),充分利用屏幕的空间明显可以带来更好的用户体验,界面上留着一条大黑边对用户总归是不够友好的。这个时候我们就可以通过指定layoutInDisplayCutoutMode属性的值,来让应用程序具备更好的屏幕适配性。
    在这里插入图片描述红色箭头处有条大黑边。如果我们将layoutInDisplayCutoutMode设置为LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES,大黑边就会消失。并且设置沉浸式布局

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus && Build.VERSION.SDK_INT >= 19) {
            val decorView = window.decorView
            decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    or View.SYSTEM_UI_FLAG_FULLSCREEN
                    or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
        }
    }
    

    效果如下图:
    在这里插入图片描述
    这样在玩游戏时是不是爽多了。但是这种情况会有一个问题,下面来看看。

    3、需要适配的地方

    但是界面上交互的一些按键可能被刘海挡住,这样用户就不能点击了。效果如下:
    在这里插入图片描述
    我们左边的按键被刘海挡住一部分。这种情况就需要我们进行适配了。

    DisplayCutout类主要用于获取凹口位置和安全区域的位置等。主要方法如下所示:
    getBoundingRects():返回Rects的列表,每个Rects都是显示屏上非功能区域的边界矩形。
    getSafeInsetLeft ():返回安全区域距离屏幕左边的距离,单位是px。
    getSafeInsetRight ():返回安全区域距离屏幕右边的距离,单位是px。
    getSafeInsetTop ():返回安全区域距离屏幕顶部的距离,单位是px。
    getSafeInsetBottom():返回安全区域距离屏幕底部的距离,单位是px。
    安全区域示意图如下:
    在这里插入图片描述
    适配代码如下:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        root_layout.setOnApplyWindowInsetsListener { view, windowInsets ->
            val displayCutout = windowInsets.displayCutout
            if (displayCutout != null) {
                val left = displayCutout.safeInsetLeft
                val top = displayCutout.safeInsetTop
                val right = displayCutout.safeInsetRight
                val bottom = displayCutout.safeInsetBottom
                val leftParams: FrameLayout.LayoutParams = btn_left.layoutParams as FrameLayout.LayoutParams
                leftParams.setMargins(left, top, right, bottom)
            }
            windowInsets.consumeSystemWindowInsets()
        }
    }
    

    root_layout是界面的根布局,上面代码就是获取安全区域,将按钮设置在安全区域以外。效果如下:
    在这里插入图片描述
    现在按钮就没有被刘海挡住了。

    二、non-SDK接口限制

    一般来说,SDK 接口是指在 Android 框架软件包索引中记录的接口。 对非 SDK 接口的处理是 API 抽象化的实现细节;其会随时更改。
    Android P 引入了针对非 SDK 接口的新使用限制,无论是直接使用还是通过反射或 JNI 间接使用。 无论应用是引用非 SDK 接口还是尝试使用反射或 JNI 获取其句柄,均适用这些限制。
    名单分类:

    • Light grey list: targetSDK>=P时,警告;
    • Dark grey list:targetSDK<P时,警告;>=p时,不允许调用;
    • Black list:三方应用不允许调用;

    三、Battery Improvements

    谷歌在P版本之前没有一个完整的功耗解决方案,OEM厂商分别开发各自的功耗方案,管控手段都包括了清理应用,功耗得到优化,但是同时也影响了三方应用的一些功能正常使用,谷歌为了解决这个问题在P版本提出了自己的功耗解决方案。
    主要方案:
    AAB(Auto Awesome Battery):
    1、通过ML算法将应用进行分类,不同类型的应用功耗管控策略不一样
    2、Firebase Cloud Messaging (FCM):管控三方消息接收的频率
    3、谷歌提供了统一的应用的管控方法:Forced App Standby (FAS),谷歌不会通过清理应用来优化功耗
    Extreme Battery Saver(EBS)谷歌超级省电模式;
    Smart screen brightness:屏幕亮度调节优化算法。
    影响
    谷歌功耗方案对三方应用各种管控,存在导致应用后台功能无法正常使用的可能,特别是:IM、邮箱、闹钟、音乐(直播)、地图导航、运动健康、下载、日历等应用影响比较大。目前通过谷歌提供的调试命令验证:所有的应用都有可能会被分到管控的类型,对三方的后台功能是有影响的。

    展开全文
  • Android 9.0 适配指南

    万次阅读 多人点赞 2019-08-05 22:24:32
    又到了我一年一度写Android适配文章的时间,本身这篇应该会早几个月发出来,但是前两三个月主要忙于Flutter的项目,所以这篇文章才姗姗来迟。不过毕竟是9.0适配,还不算太晚哈! 1.前言 从去年开始就有消息说...

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

    1.前言

    国内从去年开始就有消息说,应用上架或者更新要求TargetSdkVersion最低要为26以上,也就是最低也要适配到8.0。今年来也都逐步地开始落实。比如下图的小米应用商店公告
    在这里插入图片描述
    当然Google Play的要求更为严格:
    在这里插入图片描述

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

    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文件:

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

    AndroidManifest.xml中的application添加:

    <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 文件中添加:

    
    <manifest ... >
        <application>
    		<uses-library android:name="org.apache.http.legacy" android:required="false"/>
                ...
        </application>
    </manifest>
    
    

    4.前台服务

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

    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的适配工作需要改动和注意的点相比较以前版本的适配来说并不多,从本篇的篇幅就可以看出来,详细的变化可以参看文末的链接。后面如果遇到什么坑,我也会及时补充进来。感谢你的阅读!!

    参考

    展开全文
  • 安卓9.0适配方案和踩坑

    千次阅读 2019-06-27 17:28:22
    年初的时候就已经适配安卓9.0,但由于业务需求一直没有使用上,前段时间发布了,结果有用户反馈在安卓9.0的手机上更新下载App发生了闪退。这个时候发现9.0对权限、加密和Apache HTTP client发生了相关变化。 一. ...

    年初的时候就已经适配了安卓9.0,但由于业务需求一直没有使用上,前段时间发布了,结果有用户反馈在安卓9.0的手机上更新下载App发生了闪退。这个时候发现9.0对权限、加密和Apache HTTP client发生了相关变化。

    一. 首先我遇到的第一个错误是:Caused by: java.lang.ClassNotFoundException: Didn't find class "org.apache.http.protocol.BasicHttpContext" on path: DexPathList”,查找了一下原因:

    非Activity-Context启动Activity,现在强制执行 FLAG_ACTIVITY_NEW_TASK 要求,Apache HTTP 客户端弃用,影响采用非标准 ClassLoader 的应用

    在项目中用到了 Apache HTTP client 的相关类,<mark>就会抛出找不到这些类的错误</mark>。这是因为官方已经<mark>在 Android P 的启动类加载器中将其移除</mark>,如果仍然需要使用 Apache HTTP client.

    这种问题的解决方法有两种:

    1. 在 Manifest 文件中加入:

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

    2. 可以直接将 Apache HTTP client 的相关类打包进 APK 中。

    二. 第二个错误是:CLEARTEXT communication to life.115.com not permitted by network security policy:

    原因是:Android P 限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉;

    解决方案:

    第一步,在资源文件新建xml目录,新建文件 network_security_config,名字可以自己命名。

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

    第二步,在AndroidManifest中引用

    <application
        android:name=".app.SophixStubApplication"
        android:allowBackup="true"
        android:icon="@mipmap/jycicon"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
    
        <uses-library
          android:name="org.apache.http.legacy"
          android:required="false" />
    </application>

    三. 第三个问题就是:android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

    问题的原因是Context中有一个startActivity方法,Activity继承自Context,重载了startActivity方法。如果使用 Activity的startActivity方法,不会有任何限制,而如果使用Context的startActivity方法的话,就需要开启一个新 的task,遇到上面那个异常的,都是因为使用了Context的startActivity方法。

    解决办法就是:

    加一个flag,intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    其中这个地方我是用于下载apk的,具体的代码如下:

    Intent intent2 = new Intent(Intent.ACTION_VIEW);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                  intent2.setAction("android.intent.action.VIEW");
                  intent2.addCategory("android.intent.category.DEFAULT");
                  intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_GRANT_READ_URI_PERMISSION);
                  Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.jyc99.jyc.fileprovider", responseInfo.result);
                  intent2.setDataAndType(contentUri, "application/vnd.android.package-archive");
                } else {
                  intent2.setAction("android.intent.action.VIEW");
                  intent2.addCategory("android.intent.category.DEFAULT");
                  intent2.setDataAndType(Uri.fromFile(responseInfo.result), "application/vnd.android.package-archive");
                  intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }

    其中特别注意是:intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_GRANT_READ_URI_PERMISSION);

    四. Android 9.0 加密报错

    问题原因:The Crypto provider has been deleted in Android P (and was deprecated in Android N), so the code will crash.
    报错代码:SecureRandom.getInstance(SHA1PRNG, "Crypto"),这个是因为Crypto provider 在Android9.0中已经被Google删除了,调用的话就会发生crash。

    使用如下方法生成SecretKeySpec 对象:

    private static SecretKeySpec deriveKeyInsecurely(String password) { 
      byte[] passwordBytes = password.getBytes(StandardCharsets.US_ASCII);
      return new SecretKeySpec(InsecureSHA1PRNGKeyDerivator.deriveInsecureKey(passwordBytes, AESUtils.KEY_SIZE), "AES"); 
    }
    

    五.前台服务需要添加权限

    在安卓9.0版本之后,必须要授予FOREGROUND_SERVICE权限,才能够使用前台服务,否则会抛出异常。如果我们没有在AndroidManifest中注册FOREGROUND_SERVICE权限,在Service启动的时候会抛出SecurityException异常。

    对此只需要在AndroidManifest添加对应的权限即可,这个权限是普通权限,不需要动态申请。

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

    六. 其他Api的修改

    其中错误信息是:

    java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed

    解决方案是:

    if (Build.VERSION.SDK_INT >= 26) {
                        canvas.clipPath(mPath);
                    } else {
                        canvas.clipPath(mPath, Region.Op.REPLACE);
                    }

    以上就是我在适配安卓9.0系统中所遇到的坑,每次安卓新版本发布都需要充分了解其中更新的新特新,这样可以避免少踩坑,少改bug。这些适配肯定还不够完善,有补充的大神可以留言评论告知,谢谢了!

    展开全文
  • android 9.0适配笔记

    2020-10-23 14:21:22
    2、android 9.0默认使用的是网络传输层安全协议(TLS),https代替http. 网络适配应该 在 res 目录下新建xml文件夹,添加network_security_config.xml文件 <?xml version="1.0" encoding="utf-8"?> <...
  • code小生,一个专注 Android 领域的技术平台公众号回复 Android 加入我的安卓技术群作者:安卓搬砖人链接:https://www.jianshu.com/...
  • Android 9.0 适配

    2019-08-14 16:05:12
    Android 8.0 适配:targetSdkVersion >= 28 一、Android P 限制明文流量的网络请求 1、问题 not permitted by network security policy。 2、原因:Android P 限制了明文流量的网络请求,非加密的流量请求...
  • android 9.0适配

    2021-06-22 17:38:19
    如果您的应用在运行 Android 9 设备的后台运行,系统将对您的应用采取以下限制: 您的应用不能访问麦克风或摄像头 使用连续报告模式的传感器(例如加速度计和陀螺仪)不会接收事件。 使用变化或一次性报告模式的...
  • 限制 HTTP 网络请求Android 9.0 中限制了 HTTP(明文传输)网络请求,若仍继续使用HTTP请求,则会在日志中提示以下异常(只是无法正常发出请求,不会导致应用崩溃):java.net.UnknownServiceException: CLEARTEXT ...
  • Android Pie(9.0) New Features 内容: 刘海屏适配 通知功能的变更 隐私权变更 对使用非 SDK 接口的限制 和 适配策略 非Activity-Context启动Activity Apache HTTP 客户端弃用,影响采用非标准 ClassLoader ...
  • Android 9.0 https适配问题

    千次阅读 2019-06-12 15:16:02
    Android9.0中,谷歌要求默认使用加密连接,这意味着 Android P 将禁止 App 使用所有未加密的连接,因此运行 Android P 系统的安卓设备无论是接收或者发送流量,未来都不能明码传输,需要使用下一代(Transport ...
  • Android9.0兼容性适配

    千次阅读 2019-07-25 12:37:40
    Android 9 Pie终于面向全球发布,有道是“宜未雨而绸缪,毋临渴而掘井”,为了避免9.0更新将引发的一系列适配问题,我们需要提早了解一下9.0带来了哪些与开发者息息相关的影响。 Android P 模拟器搭建 目前Android...
  • 适配android9.0的dumpdex

    2019-02-22 09:03:39
    修改代码,适配9.0,可正常脱壳。无需root,可配合virtualxposed使用,将lib包放在/sdcard/dumpdex内即可。
  • 本帖最后由 huafans01237792352 于 2018-7-25 23:30 编辑可以看看最早的主题,7.20版本,版本号8.0.0https://cn.club.vmall.com/forum.php?mod=viewthread&tid=16673184………………………………7.23的版本,...
  • android9.0适配

    千次阅读 2018-12-05 10:16:07
    自己做的一个app小工具在android8.0上运行正常,9.0上就无法拉取数据,查看了9.0更新内容发现android P限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉。 解决方案: 新建资源文件,XXXX.xml; &...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,171
精华内容 1,268
关键字:

安卓9.0适配

友情链接: foyl.rar