upgrade android support library to 25.4.0

Android升级Support库版本至25.4.0

[TOC]

0x00 背景

support库是google官方提供的支持库,其主要功能在于兼容android高/低版本系统(如:support-v4support-v7库),同时伴随着support库的不同版本,也会发布同版本的各种UI组件库(如DesignRecyclerView等)。support库的每次更新升级,一般都会包括bug修复、性能优化以及功能改进等等。

目前我们App使用的版本是23.0.1,截止目前google发布的最新稳定版本是25.4.0,中间相差了将近20个版本,其中包含大量的bug修复;同时又因为DesignRecyclerView等UI库的很多新组件也依赖于support库的升级,所以我们计划将support-v4support-v7DesignRecyclerView等库统一升级至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.

详见: RecyclerView 23.2.0

如果布局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版本。

  1. Allows multidexing of instrumentation APK. Deprecates MultiDexTestRunner (AndroidJUnitRunner should be used instead).
  2. Provides better protection against some bad archive extraction management of the app.
  3. Fixes a bug that could lead to abandoned temporary files.
  4. Provides faster installation when done in concurrent process.
  5. 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操作。

0x04 参考