转载

升级gradle&reactnative遇到的那些坑

一、前言

首先是项目需求:

必须把客户端版本从ReactNative0.43版本升级到0.55版本;

必须把okhttp升级到最新的3.8.1版本

希望gradle从2.14.1升级从4.5.1版本;

分析

升级ReactNative实际上和gradle没什么关系,关键在于:

1、okhttp版本问题

reactnative 043版本依赖的okhttp是3.4.1版本; reactnative 055版本依赖的okhttp是3.8.1版本;;其中okio版本是:1.14.0;新增依赖org.conscrypt:conscrypt-openjdk-uber:1.1.4;

2、fresco版本问题

reactnative 043版本依赖的fresco是0.9.0版本;reactnative 055版本依赖的okhttp是1.5.0版本;而我们想用最新的1.11.0版本; 而1.11.0又不能应用在reactnative里边,有兼容性问题;

3、定制问题

okhttp加入httpdns代码;fresco代码则进行了大量的改造;带来的问题是,合并代码的工作量和测试问题;

4、gradle的问题

需要修改一些脚本,关键在于插件的兼容性,项目里使用了多个插件,anna,dilution,tinker,dexknife,aspectj,meetyoucost,blackhand等等; 需要解决兼容性问题;

二、实践

1、从基础开始升级:okhttp和fresco;

从github合并完代码打包到内部仓库后,我们将okhttp升级到3.11.0,升级fresco到1.11.0;打包新的sdk版本;

从github合并完reactnative,打包到内部仓库;将reactnative从0.43升级到0.55,并依赖最新版本的sdk版本;

以上忽略合并细节和语法兼容性问题处理。至此,配置到主app上,我们迎来了第一个坑:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:transformClassesWithAnnaForZroTestDebug'.
> SHA-256 digest error for org/conscrypt/NativeCrypto.class

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

编译时错误,我们查看了一下这个类,它是这样的:

/**
 * Provides the Java side of our JNI glue for OpenSSL.
 * <p>
 * Note: Many methods in this class take a reference to a Java object that holds a
 * native pointer in the form of a long in addition to the long itself and don't use
 * the Java object in the native implementation.  This is to prevent the Java object
 * from becoming eligible for GC while the native method is executing.  See
 * <a href="https://github.com/google/error-prone/blob/master/docs/bugpattern/UnsafeFinalization.md">this</a>
 * for more details.
 *
 * @hide
 */
@Internal
public final class NativeCrypto {..}

是新版本的okhttp引入的一个库:compile “org.conscrypt:conscrypt-openjdk-uber:1.1.4”

其中这个类注解标明@Internal,标明内部类,不允许外部使用它,然后写了一大堆注释,总体感觉就是外部你不要用他,也不要修改他。 而我们的插桩库anan没有兼容这种情况,于是修改anna源码,新增-exclude { org/conscrypt/; },在transform的时候忽略该路径下了类即可;(不过后边我手动打了jar包,取代了aar依赖就没有这个问题了)

完了之后发现org.conscrypt:conscrypt-openjdk-uber里边的META-INFO有个x86的so库很大,过滤不掉,所以使用grep和zip -d命令删除它生成新的jar包

运行起来后,我们迎来了第二个坑,运行时闪退:

Process: com.melkana, PID: 11733 java.lang.NoSuchMethodError: 
No static method loadLibrary(Ljava/lang/String;)V in class Lcom/facebook/soloader/SoLoader; 
or its super classes (declaration of 'com.facebook.soloader.SoLoader'
at com.facebook.react.bridge.ReactBridge.staticInit(ReactBridge.java:18)
at com.facebook.react.bridge.NativeMap.(NativeMap.java:19)

意思就是在当前的reactnative使用的fresco里依赖的SoLoader里找不到这个这个方法,于是修改fresco源码,gradle.properites里的SOLOADER_VERSION降级,从0.5.1降级到0.4.1。 至此,客户端的ReactNative升级到0.54顺利解决(之后就是ReactNative JS端的升级)。

2、升级gradle到4.5.1;

修改project/build.gradle:classpath ‘com.android.tools.build:gradle:3.0.1’

修改gradle/wrapper里的gradle-wrapper.properties里的distributionUrl为distributionUrl=https://services.gradle.org/distributions/gradle-4.5.1-all.zip

执行gradle clean assemlbeZroTestDebug生成apk;

错误一:

* What went wrong:
A problem occurred evaluating project ':app'.
> Could not set unknown property 'enforceUniquePackageName' for object of type com.android.build.gradle.AppExtension.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

enforceUniquePackageName 这个参数在插件3.0.1已经废弃,直接删除即可;在2.2.2如果没有这个参数可能会出现more than one library with package name ‘xxx’之类的错误;

错误二:

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring project ':app'.
> All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com/r/tools/flavorDimensions-missing-error-message.html

意思是缺失flavor dimension ; 由于我们不需要更多的变种,所以在android.defaultConfig新增flavorDimensions “versionCode”即可,字符串可以随便填写;

错误三:

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build.gradle' line: 473

* What went wrong:
A problem occurred configuring project ':app'.
> Failed to notify project evaluation listener.
   > Could not find method get() for arguments [0] on VariantOutput container of type org.gradle.api.internal.FactoryNamedDomainObjectContainer.
   > No such property: multiDex for class: com.android.build.gradle.internal.transforms.DexTransform

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

其中473行是长这样的:

472 android.applicationVariants.all { variant ->
473    variant.outputs.get(0).processManifest.doLast {

插件301版本不再提供方法,于是将其修改为:

android.applicationVariants.all { variant ->
    variant.outputs.all {
        output->
            output.processManifest.doLast {
                ...
            }
    }

并且remove multiDexEnabled = true配置;301已默认配置;

错误四:

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build.gradle' line: 473

* What went wrong:
A problem occurred configuring project ':app'.
   > No such property: multiDex for class: com.android.build.gradle.internal.transforms.DexTransform

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

此错误是DexKnife插件造成的, 升级插件为:1.7.0.alpha版本即可

错误五:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:javaPreCompileZroTestDebug'.
> Annotation processors must be explicitly declared now.  The following dependencies on the compile classpath are found to contain annotation processor.  Please add them to the annotationProcessor configuration.
    - dagger-compiler-1.2.2.jar (com.squareup.dagger:dagger-compiler:1.2.2)
    - summer-compiler-2.0.6.jar (com.meiyou.framework:summer-compiler:2.0.6)
    - usopp-1.0.15.jar (com.meiyou:usopp:1.0.15)
    - auto-service-1.0-rc2.jar (com.google.auto.service:auto-service:1.0-rc2)
  Alternatively, set android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true to continue with previous behavior.  Note that this option is deprecated and will be removed in the future.
  See https://developer.android.com/r/tools/annotation-processor-error-message.html for more details.

需要配置nanotationProecessor,在app/build.gradle新增如下代码即可:

annotationProcessor "com.meiyou.framework:summer-compiler:2.0.6"
    annotationProcessor "com.meiyou:usopp:1.0.15"
    annotationProcessor "com.google.auto.service:auto-service:1.0-rc2"
    annotationProcessor "com.tencent.tinker:tinker-android-anno:1.9.8"

并且在android.defaultConfig里加上javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }

错误六:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:transformClassesWithDilutions-pluginForZroTestDebug'.
> Unexpected scopes found in folder '/Users/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build/intermediates/transforms/AspectTransform/zroTest/debug'.
> 
>  Required: SUB_PROJECTS. Found: EXTERNAL_LIBRARIES, PROJECT, SUB_PROJECTS


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:transformClassesWithAnnaForZroTestDebug'.
> Unexpected scopes found in folder '/Users/mu/MeiyouCode/PeriodProject/SeeyouClient/app/build/intermediates/transforms/Dilutions-plugin/zroTest/debug'. Required: SUB_PROJECTS. Found: EXTERNAL_LIBRARIES, PROJECT, SUB_PROJECTS
> 
>

意思是:我们在执行Dilution和Anna插件的时候,遇到一个scope不合法的问题;这是因为3.0.1修改了scope的定义。 scopes是transform 的作用域:

PROJECT	只处理当前项目
SUB_PROJECTS	只处理子项目
PROJECT_LOCAL_DEPS	只处理当前项目的本地依赖,例如jar, aar
EXTERNAL_LIBRARIES	只处理外部的依赖库
PROVIDED_ONLY	只处理本地或远程以provided形式引入的依赖库

我们打开Dilution源码里的Scope方法改为:

@Override
    public Set<QualifiedContent.Scope> getScopes() {
        def name = QualifiedContent.Scope.PROJECT_LOCAL_DEPS.name()
        def deprecated = QualifiedContent.Scope.PROJECT_LOCAL_DEPS.getClass()
                .getField(name).getAnnotation(Deprecated.class)

        if (deprecated == null) {
            println "cannot find QualifiedContent.Scope.PROJECT_LOCAL_DEPS Deprecated.class "
            return Sets.immutableEnumSet(QualifiedContent.Scope.PROJECT
                    , QualifiedContent.Scope.PROJECT_LOCAL_DEPS
                    , QualifiedContent.Scope.EXTERNAL_LIBRARIES
                    , QualifiedContent.Scope.SUB_PROJECTS
                    , QualifiedContent.Scope.SUB_PROJECTS_LOCAL_DEPS)
        } else {
            println "find QualifiedContent.Scope.PROJECT_LOCAL_DEPS Deprecated.class "
            return Sets.immutableEnumSet(QualifiedContent.Scope.PROJECT
                    , QualifiedContent.Scope.EXTERNAL_LIBRARIES
                    , QualifiedContent.Scope.SUB_PROJECTS)
        }
    }

支持,升级gradle编译成功;

后续还涉及到aspectj升级问题; 以及dexknife的源码问题;

三、常用技能

全局搜索工具:项目下的SearchString.py

原理是遍历当前目录+解压zip包+grep搜索的,理论上一定是精确的,除非目录没覆盖掉,已经使用了很多次,可放心使用 目前仅支持aar依赖,如果要工程依赖,请自己配置搜索目录;

try catch 插桩

最近大家在7.1版本感受到了 运行时总会弹出“测试环境异常检测空指针”的错误。 这是因为做了一个功能对try catch进行拦截,可以支持异常类型配置,在anna_list.pro里配置的exceptions加入对应的exception类型就可以了 源码如下:

@Override
            public void visitTryCatchBlock(Label start, Label end, Label label, String exceptionTypeName) {

                if(exceptionTypeName!=null && isTargetException(exceptionTypeName) && label!=null){
                    //print(" ==>isTargetException");
                    ArrayList<String> exceptionList = matchedHandle.get(label);
                    if(exceptionList == null)
                        exceptionList = new ArrayList<>();
                    exceptionList.add(exceptionTypeName);
                    matchedHandle.put(label, exceptionList);
                }
                super.visitTryCatchBlock(start, end, label, exceptionTypeName);
            }

            @Override
            public void visitLabel(Label label) {
                super.visitLabel(label);
                if(label!=null){
                    ArrayList<String> exceptionList = matchedHandle.get(label);
                    if(exceptionList!=null){
                        Label matched = new Label();
                        Label end = new Label();
                        //捕获的是目标exception的实例才进行处理
                        final int N = exceptionList.size() - 1;
                        if (N >= 1) {
                            for (int i = 0; i < N; i++) {
                                compareInstance(IFNE, exceptionList.get(i), matched);
                            }
                        }
                        compareInstance(IFEQ, exceptionList.get(N), end);
                        visitLabel(matched);
                        dup();
                        //调用pushException方法
                        invokeStatic(Type.getObjectType("com/meetyou/anna/client/impl/AnnaManager")
                                , new Method("handleException", "(Ljava/lang/Throwable;)V"));
                        visitLabel(end);

                        matchedHandle.remove(label);
                    }
                }

            }
原文  http://iceanson.github.io/升级gradle遇到的那些坑
正文到此结束
Loading...