Android升级Support库版本至25.4.0
[TOC]
0x00 背景
support库是google官方提供的支持库,其主要功能在于兼容android高/低版本系统(如:support-v4
、support-v7
库),同时伴随着support库的不同版本,也会发布同版本的各种UI组件库(如Design
、RecyclerView
等)。support库的每次更新升级,一般都会包括bug修复、性能优化以及功能改进等等。
目前我们App使用的版本是23.0.1,截止目前google发布的最新稳定版本是25.4.0,中间相差了将近20个版本,其中包含大量的bug修复;同时又因为Design
、RecyclerView
等UI库的很多新组件也依赖于support库的升级,所以我们计划将support-v4
、support-v7
、Design
、RecyclerView
等库统一升级至25.4.0版本。
0x01 打包环境的影响及解决方案
1. 升级JDK & Android SDK
- 升级JDK到1.8
- 升级Android SDK到25
2. 升级CentOS或GLIBC
由于我们的jenkins打包服务器的系统是CentOS 6.5,其默认支持的GLIBC库版本最高为2.12,而android sdk高版本需要的GLIBC版本为2.14,所以要么升级系统CentOS到7.x版本,要么升级GLIBC版本到2.14.
当Android SDK需要的GLIBC版本不匹配时,出错信息大致如下:
[exec] /data0/android-tools/android-sdk-linux/build-tools/24.0.2/aapt: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /data0/android-tools/android-sdk-linux/build-tools/24.0.2/aapt)
[exec] /data0/android-tools/android-sdk-linux/build-tools/24.0.2/aapt: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /data0/android-tools/android-sdk-linux/build-tools/24.0.2/lib64/libc++.so)
在CentOS系统中,使用strings /lib64/libc.so.6 | grep GLIBC
命令可以查看其支持的GLIBC库的版本。
3. 指定gradle使用jdk1.8
由于android sdk需要在jdk1.8环境才能正常编译、打包,所以要么在系统中配置java环境变量为jdk1.8版本;要么在执行gradle时指定其运行环境为jdk1.8。
指定gradle使用jdk1.8,具体配置如下:
gradlew clean -Dorg.gradle.java.home=/usr/java/jdk1.8
也可以直接在gradle.properties
中指定jdk版本,如下所示:
org.gradle.java.home=/usr/java/jdk1.8
4. support库与build-tools版本
由于我们app原本使用的build-tools版本是22.0.1,当保持build-tools版本不变,只升级support库版本时,在HUAWEI P7-L00 4.4.2上面运行时,会出现如下错误,推测是因为support库版本和build-tools主版本号不一致导致,所以如果升级support库版本,最好保持和build-tools主版本号一致,防止各种诡异问题。
E/AndroidRuntime: FATAL EXCEPTION: main
Caused by: android.content.res.Resources$NotFoundException: File res/drawable/abc_vector_test.xml from drawable resource ID #0x7f820053
at android.content.res.Resources.loadDrawable(Resources.java:2154)
at com.huawei.android.content.res.ResourcesEx.loadDrawable(ResourcesEx.java:723)
at android.content.res.Resources.getDrawable(Resources.java:741)
at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:374)
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:202)
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:190)
at android.support.v7.widget.AppCompatDrawableManager.checkVectorDrawableSetup(AppCompatDrawableManager.java:711)
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:195)
at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:190)
at android.support.v7.content.res.AppCompatResources
0x02 相关代码的影响及解决方案
1. 使用google官方的Maven仓库
support库升级到25.4.0时,需要使用google官方的maven仓库(https://maven.google.com)需要翻墙,解决方案如下:
allprojects {
repositories {
maven {
url "https://dl.google.com/dl/android/maven2/"
}
jcenter()
}
}
2. targetSdkVersion
当我们将app的targetSdkVersion升级到25时,由于libssl.so已经被系统移除了,此时当我们的So依赖该库时,在App运行时会直接崩溃,所以targetSdkVersion可以仍然保持为23,也可以自己做兼容。注: android 6.0已经使用BoringSSL
替换了OpenSSL
。
3. CoordinatorLayout
support库升级到25.4.0时,使用CoordinatorLayout
控件的页面,Activity的主题必须继承Theme.AppCompat
,否则会直接崩溃,错误日志如下:
Caused by: java.lang.IllegalArgumentException: You need to use a Theme.AppCompat theme (or descendant) with the design library.
at android.support.design.widget.ThemeUtils.checkAppCompatTheme(ThemeUtils.java:33)
at android.support.design.widget.CoordinatorLayout.<init>(CoordinatorLayout.java:206)
at android.support.design.widget.CoordinatorLayout.<init>(CoordinatorLayout.java:200)
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:430)
at android.view.LayoutInflater.createView(LayoutInflater.java:656)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:798)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:738)
at android.view.LayoutInflater.inflate(LayoutInflater.java:495)
at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
at android.view.LayoutInflater.inflate(LayoutInflater.java:377)
4. RecyclerView
- ItemView高度占满RecyclerView
RecyclerView在23.2版本支持setAutoMeasureEnabled()
,当item布局是match_parent
时,这个item会占满整个RecyclerView的高度/宽度,所以当从低版本升级到高版本时需要将match_parent
替换为精确的宽度/高度,或者使用wrap_content
。
原因如下:
The RecyclerView widget provides an advanced and flexible base for creating lists and grids as well as supporting animations. This release brings an exciting new feature to the LayoutManager API: auto-measurement! This allows a RecyclerView to size itself based on the size of its contents. This means that previously unavailable scenarios, such as using WRAP_CONTENT for a dimension of the RecyclerView, are now possible. You’ll find all built in LayoutManagers now support auto-measurement.
Due to this change, make sure to double check the layout parameters of your item views: previously ignored layout parameters (such as MATCH_PARENT in the scroll direction) will now be fully respected.
如果布局xml非常多,修改麻烦的话,可以考虑在Adapter.onCreateViewHolder()
方法中直接修改itemView的属性即可,下面是一个竖直RecyclerView的样例:
ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams();
if (layoutParams == null) {
layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
itemView.setLayoutParams(layoutParams);
} else if (layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT) {
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
itemView.setLayoutParams(layoutParams);
}
- 隐藏ItemView
在RecyclerView中即使itemView整个设置了GONE,仍然会占据空间,所以如果需要隐藏整个itemView的话,需要设置其宽度、高度为0.
public static void hideItemView(View itemView) {
if (itemView == null) {
return;
}
itemView.setVisibility(View.GONE);
ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams();
if (layoutParams == null) {
layoutParams = new ViewGroup.LayoutParams(0, 0);
}
layoutParams.height = 0;
layoutParams.width = 0;
itemView.setLayoutParams(layoutParams);
}
0x03 后续升级建议
1. multidex
升级multidex库到1.0.2版本。
- Allows multidexing of instrumentation APK. Deprecates MultiDexTestRunner (AndroidJUnitRunner should be used instead).
- Provides better protection against some bad archive extraction management of the app.
- Fixes a bug that could lead to abandoned temporary files.
- Provides faster installation when done in concurrent process.
- Fixes an installation bug on API 19 and 20.
升级1.0.2之后直接编译,会出现以下问题:
Error:Conflict with dependency 'com.android.support:multidex'. Resolved versions for app (1.0.2) and test app (1.0.1) differ.
See http://g.co/androidstudio/app-test-app-conflict for details.
解决这个问题,则需要在test依赖中手动指定multidex
的依赖是1.0.2
dependencies {
androidTestCompile 'com.android.support:multidex:1.0.2'
}
也可以在全部依赖中强制使用1.0.2版本即可。
configurations.all {
resolutionStrategy {
force 'com.android.support:multidex:1.0.2'
}
}
2. Fragment
可以考虑使用Fragment的
commitNow()
同步方法代替异步commit()
,避免异步导致出现Can not perform this action after onSaveInstanceState
异常。可以考虑使用FragmentTransaction.setAllowOptimization()优化fragment操作。