美文网首页Android开发经验谈Android开发
targetSdkVersion 升级到 28,并迁移到 And

targetSdkVersion 升级到 28,并迁移到 And

作者: Ch_y_s | 来源:发表于2020-04-09 15:17 被阅读0次

    更新日志

    • 2020-04-09 首次发布

    前言

    由于目前大部分应用市场要求新上架应用应基于 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、官网、二分法等都能很好的分析并解决问题。如有小伙伴升级过程遇到比较棘手的问题可以在评论区留言,本篇文章将不定期进行更新。

    参考链接

    相关文章

      网友评论

        本文标题:targetSdkVersion 升级到 28,并迁移到 And

        本文链接:https://www.haomeiwen.com/subject/rsycmhtx.html