自从去年Google强制升级target API到26之后,Google就开始计划着下一波的升级,而这次是要升级到9, 也就是target API level 28,而且日期也已经定了 “November 1, 2019″。

以前每次API升级都是小打小闹,并不会有那种大规模的改动,而这次改动就有点大了。当然这次强制target API升级对国内开发者的影响会小一些,因为毕竟不用发布到Google Play,而国内应用市场这么多,各个要求也都参差不齐,所以短期还是安全的。

这次改动其实动的最大的就是非官方API的调用,也包含”Java Reflection”,基本上可以说国内要用到的那些动态APP升级方案都基本被伤了,网上搜了一下,也找到一些厅淫技巧,可以绕过去官方的这个限制,正所谓道高一尺,魔高一丈,但总的来说这样做是不推荐的,因为不知道系统又做个升级,就把这些方案给干掉。官文给了黑名单列表和灰名单列表,黑名单是一定要修复的,灰名单只是一个过渡阶段,后面看Google的心情什么时候在系统层面强制执行,那就必须都修复。2019年的Google IO大会在即,就看这次Google有什么表示。

具体我们需要做什么/take away

把官方相关的文档都看过了,下面这些是我认为比较重要的地方

  • Migration to androidx/JetPack
  • Network TLS enabled by default, so need double check https is enable by default.
  • Non SDK APIs call scanning, and fix all the black APIs usage.
  • All the “Java Reflection” usage in the code should be migration
  • JNI Native code usage, JNI via env->GetFieldID(), JNI via env->GetMethodID()
  • Apache HTTP client deprecation
  • FLAG_ACTIVITY_NEW_TASK requirement is now enforced, if the context is not Activity.
  • Foreground service permission,Apps wanting to use foreground services must now request the FOREGROUND_SERVICE permission first.
  • 3rd party libraries comply with the Non SDK Restrictions, need check/upgrade one by one.

其中”Migration to androidx/JetPack”并不是必须的,只是官方其实一直推荐大家都升级,官方宣称以后不会给android.suppor提供技术支持。

Java Reflection 非官方扫描命令

非官方API调用会是一个比较大的麻烦,因为这个不单单需要修复应用内的有关调用,这个还涉及到其它第三方library。

下面这个命令可以过滤到所有相关”Java Reflection”的调用,之后就是case by case去修。

 grep -n -R -i -E  "\.getMethod|\.getDeclared|\.getField\(|\.getFields\(|Class.forName" */src

这些相对来说还比较直接,但如果有用到像JNI Native so的这就需要去查询相关C/C++源码,看是否有相关的调用了。而如果没有源代码,那可能就会比较麻烦,需要去像提供源头确认。

Tools to scan the black/grey list

Google还提供了相关工具用来静态扫描APK中的非法调用

./appcompat.sh --dex-file=app-debug.apk

可以在上面下载这个工具,需要在Linux下运行,会产生下面的这个列表,最后给出了有多少黑名单,多少灰名单。其实过完这个列表之后,发现然并卵,并没有给出具体涉及的应用内的调用路径,这叫人情何以堪,还有这个工具也直接表明了对”Reflection”会有遗漏,而且它也不能对运行时的API使用,给出结果,所以看到这个扫描结果,真的觉得有点鸡肋,所以目前我还是通过上面grep命令,查到直接的调用结果更靠谱一些,当前这个列表只是作为参考用。

NOTE: appcompat.sh is still under development. It can report
API uses that do not execute at runtime, and reflection uses
that do not exist. It can also miss on reflection uses.
#1: Linking greylist Lsun/misc/Unsafe;->arrayBaseOffset(Ljava/lang/Class;)I use(s):
       Lcom/google/common/primitives/UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator;-><clinit>()V

#2: Linking greylist Lsun/misc/Unsafe;->arrayIndexScale(Ljava/lang/Class;)I use(s):
...
#8: Reflection blacklist Landroid/graphics/drawable/Drawable;->getOpticalInsets use(s):
       Landroidx/appcompat/widget/DrawableUtils;->getOpticalBounds(Landroid/graphics/drawable/Drawable;)Landroid/graphics/Rect;
...
#48: Reflection greylist Llibcore/icu/ICU;->addLikelySubtags use(s):
       Landroidx/core/text/ICUCompat;-><clinit>()V

#49: Reflection greylist Lsun/misc/Unsafe;->allocateInstance use(s):
       Lcom/google/android/gms/internal/zzaqa;->bo()Lcom/google/android/gms/internal/zzaqa;
       Lcom/google/gson/internal/UnsafeAllocator;->create()Lcom/google/gson/internal/UnsafeAllocator;

#50: Reflection greylist Lsun/misc/Unsafe;->theUnsafe use(s):
       Lcom/google/android/gms/internal/zzaqa;->bo()Lcom/google/android/gms/internal/zzaqa;
       Lcom/google/common/primitives/UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator$1;->run()Ljava/lang/Object;
       Lcom/google/gson/internal/UnsafeAllocator;->create()Lcom/google/gson/internal/UnsafeAllocator;

50 hidden API(s) used: 3 linked against, 47 through reflection
       48 in greylist
       2 in blacklist
       0 in greylist-max-o
       0 in greylist-max-p
To run an analysis that can give more reflection accesses,
but could include false positives, pass the --imprecise flag.
Limitations of the veridex tool include the following:
It can't detect invocations through JNI.
It can detect only a subset of invocations through reflection.
Its analysis for inactive code paths is limited to API level checks.

androidx/JetPack migration

自从去年Google发布了JetPack,官方就一直推荐大家升级到androidx, 之前一直被大家吐槽的android support v4/v7/vXXX这次终于有了一个最终的解决方案。以后再也不用为这些个破烂版本号烦心了。

所以这个升级其实会影响到所有有关android support 和material design相关的API。

当然Google官方还是很贴心的,提供了一键升级到androidx的解决方案

Refactor-> Migrate to AndroidX

只是这个方案看似贴心,但其实也是一堆坑等着你去填,因为现在的项目结构会比较复杂,所以一键升级完之后,需要有一堆的编译错误等着你去修。关键是修完这些编译错误,还是无法保证所有的升级都完成了。

事实发现有些错误只是会在运行时出现,所以编译没有错误,不代表运行不会有错误。这个坑就有点可怕了。

所以看来还是需要手工去做一些验证,因为主要改动是android.support ->androidx的改动 还有material design相关API。

所以第一步,生成相关的patch文件

git diff develop > upgrade_28.patch

之后再通过grep命令来查找所有xml相关的改动,因为毕竟widget/view的种类有限,所以可以查到所以的类型,然后手工再一个一个确认一下。

 grep -i -E "<androidx|com\.google"  upgrade28.patch|grep -v -E "import|api|androidTestImplementation"|sort|uniq |sed 's/^+//g'|gsed 's/ //g'|sort|uniq
	<com.google.android.material.textfield.TextInputEditText
			</com.google.android.material.textfield.TextInputLayout>
			<com.google.android.material.textfield.TextInputLayout
		<androidx.recyclerview.widget.RecyclerView
-dontnotecom.google.android.material.**
-dontwarncom.google.android.material.**
-keepclasscom.google.android.material.**{*;}
</com.google.android.material.appbar.AppBarLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AlertDialogLayoutxmlns:android="http://schemas.android.com/apk/res/android"
<androidx.appcompat.widget.AppCompatButton
<androidx.appcompat.widget.AppCompatCheckBox
<androidx.appcompat.widget.AppCompatEditText
<androidx.appcompat.widget.AppCompatImageView
<androidx.appcompat.widget.AppCompatSpinner
<androidx.appcompat.widget.ButtonBarLayout
<androidx.appcompat.widget.DialogTitle
<androidx.appcompat.widget.Toolbar
<androidx.cardview.widget.CardView
<androidx.cardview.widget.CardViewxmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.Barrier
<androidx.constraintlayout.ConstraintLayout
<androidx.constraintlayout.Guideline
<androidx.constraintlayout.widget.ConstraintLayout
<androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.Group
<androidx.coordinatorlayout.widget.CoordinatorLayout
<androidx.coordinatorlayout.widget.CoordinatorLayoutxmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.NestedScrollView
<androidx.core.widget.NestedScrollViewxmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.Space
<androidx.drawerlayout.widget.DrawerLayoutxmlns:android="http://schemas.android.com/apk/res/android"
<androidx.legacy.widget.Space
<androidx.preference.Preference
<androidx.preference.PreferenceScreenxmlns:android="http://schemas.android.com/apk/res/android">
<androidx.preference.SwitchPreferenceCompat
<androidx.recyclerview.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerViewxmlns:android="http://schemas.android.com/apk/res/android"
<androidx.recyclerview.widget.SwitchCompat
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
<androidx.viewpager.widget.PagerTabStrip
<androidx.viewpager.widget.ViewPager
<com.google.android.material.appbar.AppBarLayout
<com.google.android.material.appbar.AppBarLayoutxmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.appbar.CollapsingToolbarLayout
<com.google.android.material.floatingactionbutton.FloatingActionButton
<com.google.android.material.tabs.TabLayout
<com.google.android.material.tabs.TabLayoutxmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.textfield.TextInputEditText
<com.google.android.material.textfield.TextInputLayout

确认好上面这些都没问题之后,还有一个漏洞需要补上,需要确认所有的android.support不再有任何引用,使用下面这个命令就可以查到漏网之鱼

grep -R "android\.support" */src

关于第三方library

目前并没有什么好的办法,只能一个一个去查证,看是否兼容API 28的要求。当然这个需要去再做更多的调查,看是否有什么更好的解决方案。假设真的需要一个一个去验证,那就真的是比较麻烦了。

关于推迟target API 28的想法

因为目前Google给的日期是要到11月1号,所以还有8个月左右的时间去做升级。所以其实也不用这么着急,但计划一定要先做起来。当前我们项目中需要升级到androidx去支持最新的Espreso Automation,所以compile SDK version一定要先升级到28,但至于target SDK,就不是必须的。可以暂时先停留在27。当然这个需要进一步验证,如果这样可行的话,这不失为一个折中方案,因为要完全确定所有Non SDK usage需要比较漫长的时间,而且也可能在个过程中需要涉及现在第三方library的升级。

Reference

参考了好多篇官方的文档,所以建议大家都去看看官方升级文档。

Android P(9.0/API Level 28) 升级不完全教程
Tagged on:                         

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.