Android Q Labs| 现代化您的应用

1,645 阅读19分钟

这里说的现代化并不是说利用Android新版本里面那些,所以今天主要讲的是主要三个方面:

1.非 SDK 接口限制在安卓 Q 里面的一些演进,希望开发者们移除对非SDK接口的限制;

2.六十四位的应用,就是希望大家能够尽快地提供你的应用的六十四位版本;

3.targetSdkVersion,就是大家把自己的应用的 targetSdkVersion 提升到比较新的系统版本号 。

接口限制

我们知道其实是在去年 Android P 的时候,在 ndroid九的时候引入了非 SDK 接口的限制,我们的目的是为了帮助开发者只使用公开的接口来完成所有的功能,才获得在将来的 Android 版本中更好的一个兼容性,进一步的提升用户体验,同时也能够降低开发者的维护成本。

非 SDK 接口影响应用兼容性

然后作为基本原则,我们是从开发者很少使用的那些非SDK接口来入手,开始实施限制,并且通过应用 targetSdkVersion 来区分限制的程度,给开发者更长的时间来进行适配。同时我们也对开发者一些合理的需求增加一些新的公开结构支持,就说把这些相关的接口放入公开的 sdk 当中,从而全面的引导开发者一除对非 SDK 接口的依赖。

基本原则

这里是Android P版本的时候的限制名单,它基本上分为黑名单,深灰名单和浅灰名单。而白名单就是SDK本身就是大家所有的应用都是可以随意调用的那些API。

然后我们在 Android Q 里面这个名单有了一定的演进,就是他的黑名单、浅灰名单和白名单的含义是跟之前相同的。

但是深灰名单被分类成两个,一个是相当于是从P的那边带过来的,我们叫做针对P的社会名单,也就是说当你的targetSdkVersion到了P或者P以上的时候,这里的接口就不可以使用了,他们就等同于黑名单。然后还有多出来一块叫做针对Android Q的社会名单,就说当应用到targetSdkVersion到了Android Q或者Android Q以上的时候,就等同于黑名单。

然后这边是具体的限制名单的类型和它们各自的含义。
一般来说,如果你的应用是在一个较低的targetSdkVersion上面,然后从Android P到Android Q的时候如果你没有一些其他的第三方应用的依赖,一般来说不会遇到限制名单的限制,但当你的应用升级到targetSdkVersion到P的时候,就会受到Android P的深灰名单的限制,当升级targetSdkVersion到Android Q的时候,就会受到Android Q的深灰名单的限制,然后在代码级别为了区分出这些身份名单的种类,所以我们新增了一个叫做UnsupporteAppUsage这么一个注解,所有带有注解的接口就处于就代表它处于限制名单中,然后这个注解又可以包含一个叫做maxTargetSdk的参数,然后用来指定特定的targetSdkVersion对接口的访问。

比如说当有一个接口,它上面的注解,就只含有UnsupporteAppUsage注解,但是不含有maxTargetSdk参数的时候就表示这个接口是处于浅灰名单中。如果maxTargetSdk参数为零,或者说其实整个这一行都是一个opinion的,就是说如果没有注解的私有的API,或者是带有这么一行,就是maxTargetSdk参数为零的这行注解的话,那么就说明这个接口是属于黑名单当中。如果maxTargetSdk参数比如说是Android P的时候,那就说明这个接口是处在应用了TargetSdk模型到P或者Android P以下的时候是可以访问的,但是在Q的时候就禁止访问。

下面这个文档在我们的开发者网站上就详细介绍了Android Q中非sdk接口的更新的信息。

从右边的菜单里面能够看到有一个有一块是从灰名单到移入黑名单里面的接口列表,而且我们不但就是列出了所有从会名单移移到黑名单里的接口,而且每个接口后面就有个红色的那些注释,都会告诉开发者说你应该转而使用另一个公开的接口。下面还有一个是从灰名单移入白名单中的接口列表。所以如果你的应用在Android P中使用的一个接口,现在被移到了比如说黑名单当中,那么如果对你来说是一个非常强烈的需求,而且你觉得是个合理的需求的话,那么你可以通过文档里有一个链接阐明理由,可以去申请一个公开接口,这个链接会把你直接带到我们的公开的Buganizer里面,这等于是你直接对Android的产品团队发了一个bug。

同时在国内其实我们知道,因为市场上Android生态的一些碎片化,所以国内的应用经常会使用加固或者是插件化热修复这样的方案。这方案往往使用了很多的非sdK接口,创成为应用在新的Android平台上一个不兼容的一个主要原因,所以其实我们也跟加固和热修复的厂商做了非常多的合作,了解他们需求。然后我们了解到有两个比较常见的需求,一个就是加固以后的应用,在运行的时候,首先要对那种加密的代码在内存中进行解密,然后通过InMemoryDexClassLoader来进行加载。 然后对于插件化或者热修,他们经常会用父ClassLoader来加载补丁以后的类用原来的那个子ClassLoader来加载未补丁的类。所以在安卓Q里面对这些自定义的加载器和加载逻辑,我们也提供了一些新的接口和优化,来帮助解决国内就是加固和热修复的一些兼容性的问题.

在安卓Q里面我们新增了一个新的公开接口,就是APPComponentFactor.instantiateClassLoader可以帮助加固和热修方案,在应用启动的时候就插入到一个进程的创建过程当中,在运行其他代码之前就创建并且设置自己的一个自定义的主ClassLoader。
我们还对InMemoryDexClassLoader进行了优化,之前,如果比如说对加固它内存中解压的那些代码,如果用InMemoryDexClassLoader去加载的时候,就是说每次使用,每次加载都会有都会进行一个一验证。然后这个是非常耗时的,所以我们现在Android Q里面InMemoryDexClassLoader,他会缓存加载类的验证信息,所以下次你再启动应用去夹在同一个累的时候,就会直接跳过耗时的验证过程,能够帮助应用去降低加载时间,提高运行的效率和用户的体验。

然后再对于开发者来说,测试你的应用的方法有好几种,比如说第一种可以去编译自己应用的一个targetSdkVersion到Android Q的一个可DebugBal可调试的版本,然后在Android Q的设备或者模拟器中去运行,第二种你也可以在应用中稍微修改一下代码,调用StrictMode API detectNonSDKAPIUsage()然后去注册一个penaltyListener()。这样的话,如果你的应用在运行的过程中调用了非SDK接口的话,那么就会收到一个叫onVmViolation的一个回调,再次回到里面会有个Violation的参数,你可以在回调中去实现一些自己的自定义的逻辑,比如说记录下来call stick来方便你更精确的定位问题。

那么前两种是那种动态的检测方法,就是非常精确,但是它可能覆盖率不是很高,他需要去依赖你到底跑了哪些测试的test case。所以我们也提供了一个叫做Veridex的一个静态检测工具(此工具是开源的,在AOSP里面直接有它的源代码,我们也提供了各个平台的预编译版本,并且在每个Android Q的Beta发布的时候,都会对VS工具进行更新)。用这个工具你可以只运行一条命令,就去检测你的APK文件,然后它就会扫描出来所有APK文件里可能会用到非SDK接口,但是当然因为它是个静态检测工具,所以它有一些局限性,比如说它不能侦测JNI的调用,然后它对反射调用的诊测也可能不是百分之百准确,他有可能比如说当你在用很复杂的字符串的操作去拼接出来一个你要反射的接口的时候就不知道拼接出来最后的结果接口大概什么样子的,或者说你有一个虽然是反射调用代码级别,看起来是反射调用,但是其实这是一段死代码,那么它还是会曝出来说,你这里可能在调用非SDK接口,但其实在你的应用中,因为它是快时代,所以不可能被调用到,所以说虽然他有这些局限,但是因为它是静态检测,所以它的检测效率很高,你也不需要test case,所以它能够它能够提供更多的代码覆盖率,所以大家可以去结合这种动态检测的静静态检测来去看一下自己的应用到底使用哪些非SK接口。

对测试来说,我们提供了好几种方法来让大家在Android Q上进行测试,第一个是模拟器,就是你用最新的Android studio最新的3.5beta版本,里面会带有就是Android Q版本的模拟器。还有就是我们会提供Android Q beta的Pixel系统印象,大家可以直接如果有Pixel手机的话,可以直接刷机。我们也跟很多的OEM合作,提供了一些OEM的OEM支持的Android beta的设备,也就是说大家可以在我们的开发者网站中找到这些设备列表,然后每个设备它都会链接到提供商的一个网页,就是教你怎么样去把设备刷到Android,Android Q应该是Beta3的版本。 最后我们也在Android Q上提供了常规系统映像GSI,这是我们一个project trouble的一个一个进展,也就说是理论上来说,只要是发布的时候搭载安卓P的手机,那么他应该全面支持拆包的。在Q之后我们会持续的引进Sdk接口限制,我们会创建与新的maxTargetSdk相关的社会名单,把一些非SK接口移到更加合适的限制名单,然后会根据开发者的需求去增加新的公开的接口支持,并且会发布更多的技术文档。
在Android Q之后我们会持续的引进Sdk接口限制,我们会创建与新的maxTargetSdk相关的社会名单,把一些非SK接口移到更加合适的限制名单,然后会根据开发者的需求去增加新的公开的接口支持,并且会发布更多的技术文档。
下一个是六四位的应用。
首先我们必须承认,六十四位构建可能会让你的APK体积变得稍微大一些,但它通常会让你的应用或者游戏运行的更快,因为六十四位指定机支持更高的代码和运营效率,比如说它能使用两倍数量的计程器,然后它能支持双精度的浮点运算,这使得编译器可以做更复杂的是量化的优化。
更好的消息如果你的应用是在Googleplay上发布的话,那么你可以使用APP Bundle作为发布的格式。如果这样的话,用户下载你的应用的时候,他收到的APK的包体积也不会受到太大的影响。因为Googleplay会按照用户设备的架构来仅向他推送他所需要的文件,就比如说你的APP Bundle里包含了32位和六四位两套so的文件,那么根据用户设备的架构类型,googleplay只会向她推送其中的一套架构文件,同样得到同样的道理,你也可以构建一个包含X86和F86-645年的bundle,这样的话你不需要去担心他们对目标为arm的设备的应用体系影响,也就是说你的应用其实是还支持能够在chrome os上面去跑的话,那么你提供这两套文件也是非常有帮助的。

现在六四位已经成为googleplay的一个政策,我们在2017年底的时候就向开发者传达了这个要求。到了今年8月份的时候,所有在google平台上上家的新应用和已有应用的更新都必须包含六十四位版本。但是这里有个例外,就是最后一个时间点,2021年8月,这是因为我们知道还有很多游戏在使用Unity5.6或者更高版,或者更低版本的游戏引擎来开发的。

对于这样的游戏,我们专门有个特例,就是说在googlepla上还可以继续基于这些32位的unity更新两年的时间。 然后到2021年8月开始,就是google play就不会再向任何兼容流思维的设备去推送非六十四位的应用和游戏了。其实到那个时候有可能就是我们甚至都不能保证说安卓设备仍然能够运行32游戏,就因为有可能那个时候有很多的主流设备就变成是仅支持六十四位的架构了。然后对于使用unity引擎的话就的游戏来说,如果你要升级到六十四位,那么你必须使用2017.4.16或者是2018.2或者以后的unity引擎版本。
那么对于开发流速应用来说,总的来说就是四个步骤,第一请使用安卓studio里面的APKAI者就APK分析器来去查看你的应用。 里面所有的32位的依赖,你可以在lib文件夹中看一下,就是X86和armeabi-v7a甚至更早的V5这些目录,这些目录里包含的so就是三十二位的。然后接下来你需要进行一个六十四位构建。
当然在代码级别你可能还要做一些事情,保证所有的颜色代码都能够在六十四位的环境中进行正确的运行,比如说最基本的就是你的代码中不能假设指针类型的固定大小。
然后你也应该把你的应用中所使用的SDK和依赖库都升级到六四为的新版本。如果他们还没有六十四位版本的话,那么你需要去督促他的提供商去解决,或者说你用的是开源的库的话,你自己去把它去built到六十四位。
最后当然不能忘了测试,当你做出来一个六四位版本的应用之后,你可以在本地进行测试,然后或者是你可以使用Googleplay的测试发布通道。
最后我们再说一下targetSdkVersion的升级。
我们知道targetSdkVersion模型其实就是一个整数,每一次安卓一个主版本的时候,它所对应的系统的等级都会提高,其实对应用来说,你设置一个targetSdkVersion,就是告诉系统说我的应用已经对R级别的API进行了兼容性的测试和改,然后系统应该尽量保持对等级的行为的一个兼容性。但这只是一个尽量保持新的系统并不会保证说百分之百去兼容旧版本旧的API上的行为。这里我们也知道targetSdkVersion其实跟应用所运行的系统的版本是没有关系的。
很多开发者认为其实targetSdkVersion可能只是去会影响系统API的一些行为,但其实它的影响还会更深远一些。比如说在SELinux中,底层的文件系统,它可能会根据应用的targetSdkVersion去设置文件的标签,并且去产生一些沙盒性逻辑就比如说当你targetSdkVersion到了28或者以上的时候,那么他们的文件是不可以与其他的其他的应用来直接共享的,你需要通过content provider来进行分享。
那么为什么要升级targetSdkVersion有以下几个方面。
这里是一年多之前的googleplay应用市场。 那个时候是没有targetSdkVersion的限制的,所以说开发者可以自由选择自己想使用的targetSdkVersion。这会我们就会发现,其实整个应用的targetSdkVersion是非常碎片化的,很多应用会故意去选择一些比较低的targetSdkVersion。它的问题就是对用户来说体验会不一致,然后用户买了一个新的手机上面运行的是一个比较新的安卓的系统,但是他在使用一些旧的应用的时候,并没有获得新系统所提供的一些性能和安全性上的收益。然后最后我们还发现很多的应用,他们故意保持在一个比较低的targetSdkVersion上来,做他们一些不好的事情。

所以说从2017年底的时候,我们go派就发布了一个新的政策,主体的思想就是说所有在Googleplay上商家的应用,他们的targetSdkVersion必须是一年之内发布的安卓安卓的主版本号。也就是比如说我们在2017年8月份发布了安卓O那么这里就会要求在2018年8月份,所有新应用的targetSdkVersion要达到26。对于已有应用,我们给了三个月的宽限期,所有已有应用的升级版本,他们的targetSKversion要在2017年11月份之前达到安卓O的版本26。 然后在今年5月份的时候,google play的开发者的后台也增加了检查逻辑。从5月份开始,如果你上传应用的时候targetSdkVersion没有达到26,他就会给你一个警报,但从8月份开始就会直接拒绝。
这里是现在的GooglePlay市场上的应用的targetSdkVersion分布,这里就看到碎片化得到了根本性的改善。这边基本上几个新的版本在三分天下。
我们今年继续更新了GooglPlay的targetSdkVersion政策,其实总体思想和方向跟之前是完全保持一致的,也就是说我们去年8月份发布了安卓P targetSdkVersion是28,那么从今年的8月份开始所有的新应用,在GooglePlay上商家的时候,他的targetSdkVersion一定要38或者以上。然后11月份所有已有应用的更新版的targetSdkVersion的一定要是28或者以上。
那么在购物市场之外怎么办?其实我们也做了很多的努力。首先我们要看到的是这些恶意软件各异的应用,他们的targetSdkVersion往往都是比较低的,比如说是21或者19。
我们也很高兴地看到,一些第三方的市场,比如说中国主流的OEM和一些第三方市场都签署了一个高等级APA的一个公约。
在安卓的系统的层面,我们会增加一些GooglePlay Protect的警报。就是说如果你在新的安卓的手机上去安装一个应用的话,那么不管你从哪里安装,只要你的手机是支持GooglePlay Protect的话,protect也会通过也会做类似的检查,当你新安装的应用targetSdkVersion比较低的时候,它就会发出一个这样的警报。

然后我们也知道有些应用其实并不是用户安装的,可能是有些设备上预安装了很多应用,那么对于这这种情况我们得有一个新的GMS的要求。
还有一个就是权限复核,我们知道在比如说在targetSdkVersion小于23的时候是不支持动态的权限申请的,所以说对于这样对于这些targetSdkVersion比较低的应用安卓在安装在安卓Q上面以后第一次运行的时候,那么它会弹出来一个这样的权限复合的一个界面,非常明确的告诉用户说应用需要用哪些权限,这时候用户可以去看一下并且做出调整,就是去解除掉一些权限。这也只是在第一次运行的时候会出来一次。
最后对于targetSdkVersion实在非常低的应用,我们会有一个运行时警告。 这个是在安卓P的时候引入的,在安卓P的时候,如果用户去运行一个targetSdkVersion小于17的应用,那么在每次运行的时候都会有这个警报。在安卓Q里面这个被调高了23,当应用targetSdkVersion小于23的时候,每次运行的时候都会有警告,然后这边会有一个检查更新,如果能够支持ACTION_SHOW_APP_INFO的话,它会把你带到一个应用市场去看有没有新版的应用可供下载。
所以今天大概就跟大家介绍这三个问题,希望开发者们回去检查你使用了哪些非SDK接口,尽量的去转而使用一些公开的SDK,然后尽快的提供出一套六十四位的版本,最后尽量把你的应用的targetSdkVersion升级到更高的版本。