背景
谷歌在 2018 年的 I/O 大会上宣布了把 Support Library 重构至 AndroidX 命名空间的计划,在 Support Library 28版本上完成了重构,并且发布了 AndroidX 1.0。为了能够享受 Jetpack 所带来的便利,需要将旧的 Support Library 迁移至 AndroidX。 因此,AndroidX本质上其实就是对Android Support Library进行的一次升级,升级内容主要在于以下两个方面。第一是包名。之前Android Support Library中的API,它们的包名都是在android.support.*下面的,而AndroidX库中所有API的包名都变成了在androidx.*下面。这是一个很大的变化,意味着以后凡是android.*包下面的API都是随着Android操作系统发布的,而androidx.*包下面的API都是随着扩展库发布的,这些API基本不会依赖于操作系统的具体版本。第二是命名规则。吸取了之前命名规则的弊端,AndroidX所有库的命名规则里都不会再包含具体操作系统API的版本号了。
那么迁移androidx对于客户端开发来说到底有哪些好处呢?这里可以分别根据短期和长期的收益去进行判断。
短期看
-
Flutter 1.17版开始全线使用AndroidX,不再兼容Support。
-
官方推出的一些新组件只有在Androidx对应库中才包含,比如ViewPager2、CameraX
-
很多新的工具库新版本已经迁移到AndroidX,比如常用的Lottie、Butterknife,包括一些集团二方库后续也有可能升级,如果有使用的话需要作兼容。
-
更好的包管理: 独立版本、独立命名以及更高频率的更新。所有support系列组件都可以使用统一版本。
长期看
-
Android官方以后不会在Support版本上修复bug或者增加新功能,也就是说不再维护,support系列组件最后版本停留在28。
-
Google正在努力推广 AndroidX 命名空间,未来所有新推出的组件库,都将只有AndroidX版本
鉴于以上的收益,阿里巴巴(1688)主客也决定进行androidx迁移。
迁移方法
迁移前现状
1688主客在androidx迁移前使用的support库为26版本,同时有部分库的gradle版本仍然处于3.3,需要进行升级。
迁移准备
首先通过打出denpendencies tree可以看到目前正在使用的所有support包。再通过japi-compliance-checker api兼容性排查工具以及谷歌官方androidx发布版本文档来检查新老版本差异。遇到兼容性有问题的库时,通过反编译工具全局搜索出app使用的地方,然后逐一进行替换。本次迁移1688主客一方库的原则是首先迁移有兼容性问题的模块,其次迁移常用的模块,那些很少修改或不再维护的模块暂时不进行手动迁移。二方库和三方库可以利用迁移工具自动迁移。
开始迁移
使用Android Studio提供的功能Refactor > Migrate to AndroidX进行迁移。首先会弹出一个对话框,提示你备份工程代码,并告知可能需要手动处理一些错误。

点击Migrate后Android Studio会对工程中所有文件进行搜索需要处理的文件列表,点击Do Refactor开始迁移。
此时在gradle.properties文件中会被添加以下属性:
android.useAndroidX=true //表示启用androidx
android.enableJetifier=true //表示对依赖库进行迁移
迁移检测
运行打包之后查看依赖,可以看到原先support包都已升级成AndroidX

迁移工具问题
尝试编译后有可能会出现一些导包错误,如下图

显然Android Studio提供的工具还有很大的缺陷,由于官方提供了support库和androdx库类映射关系,所以这里我们也使用python脚本获取映射关系,通过映射关系对工程中所有文件进行扫描迁移。备注:该脚本不支持多行替换和依赖替换。
脚本地址: https://github.com/yuweiguocn/MigrateToAndroidX
脚本迁移流程
首先还是在在gradle.properties文件中添加以下属性:
android.useAndroidX=true//表示启用androidx android.enableJetifier=true//会对依赖库进行迁移
然后打开终端在工程根目录执行以下命令:
git clone git@github.com:yuweiguocn/MigrateToAndroidX.git python MigrateToAndroidX/migrate.py
迁移问题
RecyclerView api不兼容导致崩溃
public RecyclerView.ItemDecoration getItemDecorationAt(int index) { int size = this.getItemDecorationCount(); if (index >= 0 && index < size) { return (RecyclerView.ItemDecoration)this.mItemDecorations.get(index); } else { throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size); } }
recyclerview在api 28 以上版本使用getItemDecorationAt函数时当index>=size时会抛出异常。
使用之处

修改方式:根据需求可以在getItemDecorationAt前方法加上判断,使用api28新加入的getItemDecorationCount方法判断decration的size,如果为0的话就不进行后续的操作
GapWork类封装
对GapWorker 类进行了魔改,封装了SafeGapWorker类如果使用androidx自动迁移的话包名会有问题,因此需要重新修改SafeGapWorker的包名,同时在引用该类时也使用新的包名


weex_enhance库中design api不兼容导致崩溃
/**
* api 28
*/
public void setDragCallback(@Nullable AppBarLayout.BaseBehavior.BaseDragCallback callback) {
this.onDragCallback = callback;
}
/**
* api 26
*
public void setDragCallback(@Nullable DragCallback callback) {
mOnDragCallback = callback;
}

解决方式:重新适配接口
通过注解生成的代码没有使用androidx的库而仍然使用了androidx
解决方式:重新修改注解处理器的代码,或者将注解生成的代码改为手动编写
偶现crash
java.lang.IllegalStateException:Can't access ViewModels from detached fragment: atandroidx.fragment.app.Fragment.getViewModelStore
java.lang.NullPointerException:at androidx.fragment.app.Fragment.performDetach
解决方式:强制指定AndroidX的fragment和activity两个库版本为1.0.0
26版本的api和28版本的api不兼容,导致方法签名不匹配,运行时出错
举例
TabLayout ->setOnTabSelectedListener
BaseTransientBottomBar$ContentViewCallback ->animateConte
解决方式:对应的模块以28版本的supoort包为依赖重新打包即可
Anroidx新特性
ViewPager2
ViewPager2 用来替换 ViewPager,在 ViewPager2 中底层使用了一个 RecyclerView ,所以和之前的 ViewPager 项目, ViewPager2 功能更加完善,比如可以支持 Right-to-left (RTL) 布局、上下滚动翻页效果的支持、修改页面内容的支持。viewpager可以从之前的ViewPager实现中轻松迁移(API 尽可能一致)
垂直方向支持
ViewPager2 支持垂直分页
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager_two"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
从右到左支持 (RTL布局)
默认为从左到右滑动方向,可以通过设置layoutDirection="rtl"设置为从右到左滑动
可修改的 Fragment 集合
ViewPager2 支持对可修改的 Fragment 集合进行分页浏览,在底层集合发生更改时调用 notifyDatasetChanged() 来更新界面。
DiffUtil
ViewPager2 在 RecyclerView 的基础上构建而成,这意味着它可以访问 DiffUtil 实用程序类。这一点带来了多项优势,但最突出的一项是,这意味着 ViewPager2 对象本身会利用 RecyclerView 类中的数据集更改动画。
RecyclerView
ConcatAdapter
利用这个全新的适配器,可以轻松地串联同一 RecyclerView 中的多个适配器。
延迟状态恢复
现在,RecyclerView 适配器可以推迟状态恢复的时间,直到其内容加载完毕。
KTX
Android KTX 是一组 Kotlin 扩展程序,属于 Android Jetpack 系列。它优化了供 Kotlin 使用的 Jetpack 和 Android 平台 API。Android KTX 旨在让您利用 Kotlin 语言功能(例如扩展函数/属性、lambda、命名参数和参数默认值),以更简洁、更愉悦、更惯用的方式使用 Kotlin 进行 Android 开发。Android KTX 不会向现有的 Android API 添加任何新功能。这些扩展程序利用了多种 Kotlin 语言功能,其中包括:
-
扩展函数
-
扩展属性
-
Lambda
-
命名参数
-
参数默认值
-
协程
Fragment
FragmentFactory
现在可以在FragmentManager上设置一个FragmentFactory,以管理 Fragment 实例的创建,从而消除必须具有无参数构造函数这一严格要求。
基于类的add()和replace()
在FragmentTransaction中添加了add()和replace()的新重载,这些重载采用Class<? extends Fragment>和Bundle(可选)参数。
onDestroyView()调用时机
Fragment 现在会等到退出动画、退出框架转换和退出 AndroidX 转换(使用Transition1.3.0时)完成后再调用onDestroyView()。
Lifecycle ViewModel SavedState 集成
现在将by viewModels()、by activityViewModels()、ViewModelProvider构造函数或ViewModelProviders.of()与 Fragment 一起使用时,会使用SavedStateViewModelFactory作为默认出厂设置。
FragmentContainerView
在以前的版本中,经常使用FrameLayout来进行加载Fragment,而FragmentContainerView就是继承自FrameLayout,它可以可靠的处理Fragment,有更好的协调功能,可以更好的管理fragment加载时的动画效果。
ViewPager 1 适配器弃用
ViewPager2 1.0.0发布后,用于与ViewPager进行交互的FragmentPagerAdapter和FragmentStatePagerAdapter类已弃用。
Jetpack Compose
Jetpack Compose是用于构建原生Android UI的现代工具包。JetpackCompose开发依赖Androidx命名空间下的库。总体来说,Jetpack Compose有如下优势。
加速开发
Jetpack Compose 为我们提供了很多开箱即用的Material 组件,如果的APP是使用的material设计的话,那么使用Jetpack Compose 能让你节省不少精力。
强大的UI工具

上图是使用Jetpack Compose 开发UI时,在Android Studio 上的预览,可以看到,在左边编码时,右边能同时展现UI即时预览,比如在明/暗模式下的状态切换,都能在右边及时展示出来。
它与我们现在使用的Android Studio 中的text/Design相似,但是它更加先进,使用很简单,这个功能只能在Android Studio 4.0以上预览版开发compose 时使用。
直观的Kotlin API
Jetpack Compose将完全使用Kotlin进行编写。Kotlin相对于Java来说能更好地兼容空指针异常,更好支持lambda表达式,减少代码量,同时对安卓原生开发者来说有着更一致的开发工具链
1688落地场景
随着Androidx的成功迁移,在1688主客上也可以去试用一些依赖于androidx的组件。这里就以viewpager2来举例。

1688短视频的新版浏览列表页使用到了viewpager2来作为框架的基础组件。由于viewpager2是基于recyclerview实现的,因此可以支持垂直方向上的翻页,也可以复用recyclerview的内存管理机制。短视频列表页利用此特性做到了流畅的上下翻页。同时viewPager2对Fragment支持只能用FragmentStateAdapter,FragmentStateAdapter在遇到预加载时,只会创建Fragment对象,不会把Fragment真正的加入到布局中,所以自带懒加载效果。短视频列表页的每一个视频页都代表了一个Fragment。由于viewpager2可以进行预加载,所以每当进入短时频列表页时会去执行后续几个Fragment的onCreate函数,先初始化一些基础的布局,并加载最初一个视频的资源。当下滑切换到下一个fragment时再去执行onStart函数,而在onStart函数中监听了Viewpager2的OnPageChangeCallback函数,当触发callback的onItemSelected函数时,去拉取后续的几个视频的资源。
详细的类图可以参考如下。

总结
经过几个月的迁移以及灰度流程后,目前迁移已全量稳定上线。总的来说,迁移的过程可能并不轻松,1688主客代码所包含的第三方库非常多且杂乱,自己的一方库中也有很多早已不再维护,这都给迁移过程带来了很大的困难。所幸谷歌官方以及其他开发人员各个提供了很好的迁移工具,极大地简化了迁移成本。同时,本身androidx升级后就存在许多兼容性问题,1688迁移后遇到的问题可能只是冰山一角,想要趟平所有坑还需要大家共同的努力。尽管有着这样那样的困难,这次迁移仍然是很有必要的,一是要顺应官方技术的发展,即时更新自己的技术基建,避免日后不得不更新时遇到更大的阻碍;二是可以很快的在业务中应用新迁移的技术,比如在1688直播短视频业务中就利用到了androidx迁移后提供的viewpager2组件完成了上下滑框架的升级。希望本文能给大家带来一些微薄的参考意见,也欢迎一起来探讨遇到的问题~