targetSdkVersion 升级到 28,并迁移到 AndroidX,最全升级指南

转:https://www.jianshu.com/p/9916c00f7fa2

前言

由于目前大部分应用市场要求新上架应用应基于 Android 8.0 (API 等级 26,即 targetSdkVersion 大于等于 26)及以上开发。所以对现有的 APP 进行全面升级。
既然是要求最低 26,我们干脆就直接升级到 28,并迁移到 AndroidX。你可能会问,为什么选择 28,因为官方建议使用 28 进行迁移 AndroidX。

现状

  1. Android Studio 版本 3.5.3
  2. targetSdkVersion=23,compileSdkVersion=23
  3. Gradle 版本为 3.4
  4. Gradle 插件版本为 2.2.3
  5. android.support 为 28

目标

  1. Android Studio 保持该版本
  2. targetSdkVersion=28,compileSdkVersion=28
  3. Gradle 版本为 5.4.1
  4. Gradle 插件版本为 3.5.3
  5. 迁移到 AndroidX

这个升级的跨度有点大,隐约觉得到会有很多坑出现,既然我们目标已经很明确了,不用慌,一步步来。

实施

targetSdkVersion 和 compileSdkVersion 改为28

1. 出现 Add Google Maven repository and sync project 错误。

解决方案:在项目目录下的 build.gradle 两个地方添加上 google()

buildscript {
    repositories {
        google()
        jcenter()
    }
}
allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
    }
}

2. 出现 Gradle DSL method not found: 'google()' ,错误信息如下:

Gradle DSL method not found: 'google()'
Possible causes:
The project '你的项目名称' may be using a version of the Android Gradle plug-in that does not contain the method (e.g. 'testCompile' was added in 1.1.0).
Upgrade plugin to version 3.5.3 and sync project

The project '你的项目名称' may be using a version of Gradle that does not contain the method.
Open Gradle wrapper file

The build file may be missing a Gradle plugin.
Apply Gradle plugin

原因是当前 Gradle 插件版本太低,没有该方法。我们将 Gradle 修改为 5.4.1,Gradle 插件版本修改为 3.5.3。

Gradle 改为 5.4.1,Gradle 插件改为 3.5.3

1. 升级 Gradle 导致相关 api 失效,错误信息如下:

Could not get unknown property 'apkVariantData' for object of type com.android.build.gradle.internal.api.ApplicationVariantImpl.

原因是 Gradle 3.0 以后 apkVariantData 这个 api 已经被移除,所以出现找不到,网上大部分解决方案都是降低 Gradle 版本,我们就是为了升级,现在又说降低,明显是不符合我们的需求。继续查找问题所在。因为我们本身项目没有使用到这个 api,所以初步判断是第三方库使用到。通过二分法,最终确定了是我们引入的 AndResGuard 这个库导致的,我们当前使用的版本是 1.2.3,官方最新的已经是 1.2.17,我们尝试下升级为官方最新版本,问题解决。

2. 产品类型错误,错误信息如下:

ERROR: All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com/r/tools/flavorDimensions-missing-error-message.html
Affected Modules: app

翻译过来的意思是:所有风味现在都必须属于命名的风味维度,并提供给我们一个地址,我们直接点击查看,文档里写的非常详细,最终解决方案在 app 下的 build.gradle,添加如下代码:

android {
    // 省略...
    flavorDimensions "version" // 增加这一行代码
    
    productFlavors {
        // 省略...
    }
}

参考:

3. android.useDeprecatedNdk 不再被支持使用,错误信息如下:

INFO: The following project options are deprecated and have been removed: 
android.useDeprecatedNdk
NdkCompile is no longer supported
Affected Modules: app, library

由于当前项目没有使用到 NDK 相关,我们直接将 gradle.properties 里 android.useDeprecatedNdk 移除。

4. compile 已过时,并已被替换,错误信息如下:

INFO: Configuration 'compile' is obsolete and has been replaced with 'implementation' and 'api'.
It will be removed soon. For more information see: http://d.android.com/r/tools/update-dependency-configurations.html
Affected Modules: app

我们将项目里 compile 根据项目自身需求替换为 implementation 或 api 即可,两者有何不同,或其他用法可进入提供的网址查看。

到这里我们的项目又可以正常跑起来了,是不是觉得也没那么难。

参考:

迁移到 AndroidX

终于到了这惊险又刺激的最后一步了,不成功便成仁。我们先看官网的前提条件。

前提条件
执行迁移之前,请先将应用更新到最新版本。我们建议您将项目更新为使用支持库的最终版本:版本 28.0.0。这是因为,1.0.0 版本的 AndroidX 工件是与支持库 28.0.0 工件等效的二进制文件。

前提条件我们已经满足了,直接通过 Android Studio,Refactor > Migrate to AndroidX。Android Studio 会帮我们进行支持库的替换。大部分的包导入也会自动替换,如有剩下没自动替换的,我们手动进行替换即可。类似的如下替换:

com.android.support:support-v4:28.0.0 -> androidx.legacy:legacy-support-v4:1.0.0
com.android.support:recyclerview-v7:28.0.0 -> androidx.recyclerview:recyclerview:1.1.0
...

在 gradle.properties 中,Android Studio 已经为我们自动添加了如下代码:

android.useAndroidX=true
android.enableJetifier=true

1. java 8 支持,错误信息如下:

Execution failed for task ':app:mergeExtDexDevDebug'.
> Could not resolve all files for configuration ':app:devDebugRuntimeClasspath'.
   > Failed to transform artifact 'butterknife-runtime.aar (com.jakewharton:butterknife-runtime:10.2.1)' to match attributes {artifactType=android-dex, dexing-enable-desugaring=false, dexing-is-debuggable=true, dexing-min-sdk=25, org.gradle.usage=java-runtime}.
      > Execution failed for DexingNoClasspathTransform: C:\Users\ry\.gradle\caches\transforms-2\files-2.1\7095ceecc071665593ef76235a41c81a\butterknife-runtime-10.2.1-runtime.jar.
         > Error while dexing.
           The dependency contains Java 8 bytecode. Please enable desugaring by adding the following to build.gradle
           android {
               compileOptions {
                   sourceCompatibility 1.8
                   targetCompatibility 1.8
               }
           }
           See https://developer.android.com/studio/write/java8-support.html for details. Alternatively, increase the minSdkVersion to 26 or above.

这么详细的错误就不多解释了,按提示添加相关代码即可。

参考:

成功迁移

经过以上步骤和踩坑,基本上算是完成了迁移工作,剩下的就是一些兼容和适配的问题了。这里我也列举一些遇到的兼容和适配问题。

兼容和适配

1. Android Studio 在 Android 9.0 机器上直接 Run 报错如下:

The application could not be installed: INSTALL_FAILED_TEST_ONLY

原因是 Android Studio 在 Run 的时候会自动添加 android:testOnly=“true” 导致安装失败。我们来看一下官方对 android:testOnly 这个属性的描述。

android:testOnly
指示此应用是否仅用于测试目的。例如,它可能会在自身之外公开功能或数据,这样会导致安全漏洞,但对测试很有用。此类 APK 只能通过 adb 安装,您不能将其发布到 Google Play。
当您点击 Run 图标时,Android Studio 会自动添加此属性。

有两种解决方案:

  • 不使用 Android Studio 的 Run 按钮直接安装,而是依次点击 Build > Build Bundle(s)/APK(s) > Build APK(s) 方式进行安装
  • 在 gradle.properties 添加 android.injected.testOnly=false 将该属性强制设置为 false。

参考:

2. APP 运行出现 java.lang.NoClassDefFoundError:

// 第一种 Android 9.0 出现
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/conn/scheme/SchemeRegistry;
// 第二种 Android 10 出现
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/message/BasicNameValuePair;

出现这个错误的原因我们也可以在官网找到,更详细的描述可以通过点击参考的链接查看。

在 org.apache.http.* 中查找类时,这些应用需要委托给应用 ClassLoader。 如果它们委托给系统 ClassLoader,则应用在 Android 9 或更高版本上将失败并显示 NoClassDefFoundError。

解决方案官网也有给出,在 AndroidManifest.xml 的 application 标签下添加以下内容:

<uses-library android:name="org.apache.http.legacy" android:required="false"/>

参考:

3. Android 9.0 及以上网络请求时抛出异常,如下:

java.net.UnknownServiceException: CLEARTEXT communication not supported: [ConnectionSpec(cipherSuites=[TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA], tlsVersions=[TLS_1_2, TLS_1_1, TLS_1_0], supportsTlsExtensions=true), ConnectionSpec(cipherSuites=[TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA], tlsVersions=[TLS_1_0], supportsTlsExtensions=true)]

原因是:从 Android 9(API 级别 28)开始,系统默认情况下已停用明文支持。
解决方案,在 AndroidManifest.xml 的 application 标签内添加 android:usesCleartextTraffic="true"

参考:

4. Android 8.0 及以上无法正常弹出通知。

根据官网的说明,从 Android 8.0(API 级别 26)开始,所有通知都必须分到一个渠道,否则通知将不会显示。这里我直接贴了官网的代码:

private void createNotificationChannel() {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is new and not in the support library
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        CharSequence name = getString(R.string.channel_name);
        String description = getString(R.string.channel_description);
        int importance = NotificationManager.IMPORTANCE_DEFAULT;
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
        channel.setDescription(description);
        // Register the channel with the system; you can't change the importance
        // or other notification behaviors after this
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }
}

参考:

结语

本次升级并迁移 AndroidX 遇到的问题暂时就是这些,不要怕出问题,解决问题才能使我们更强大。通过 google、官网、二分法等都能很好的分析并解决问题。如有小伙伴升级过程遇到比较棘手的问题可以在评论区留言,本篇文章将不定期进行更新。

参考链接

posted @ 2022-07-20 11:10  @王新@  阅读(1923)  评论(0编辑  收藏  举报