升級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 -> 473variant.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); } } }