背景
項目中使用ARouter進行路由,由於不同上層業務模塊都可能會使用到同一目標的路由地址,因此,將所有業務模塊的路由地址以一種類似靜態常量的方式設置在Base模塊中。這樣,在實際目前上加上對應此地址的注解,就可以將其對應加入到路由中。使用方通過ARouter對應的地址方式去路由,即可訪問到對應的目標。
以Activity路由為例,通過注解,編譯后在對應模塊路徑下生成的文件名為Arouter$$Group$$GroupName1.java文件。其中GroupName1為分組名。 具體路徑為:
/build/generated/source/kapt/變體/com/alibaba/android/arouter/routes/ 復制代碼
原則上,不同模塊應該注解到不同的路由地址分組。否則不同的模塊下編譯后會生成相同的Arouter$$Group$$GroupName1.java文件,在項目構建安裝后,會發生不可預期的路由地址失敗問題(如其中一個Arouter$$Group$$GroupName1.java文件中的路由生效,另一個直接路由失敗)。
網上查了下,發現同樣問題,其他人也有遇到,具體問題描述GitHub上ARouter項目中issues等: github.com/alibaba/ARo…
github.com/alibaba/ARo…
github.com/alibaba/ARo…
近期,項目中在進行模塊化改進時,由於部分注解了路由地址的目標文件被從一個模塊移動到另一個模塊,導入出現同樣問題發。原因在於,如果直接通過Android Studio中的三角形綠色圖標直接run android app時,對於不同模塊下生成的同名java文件在編譯及打包組裝過程中是不會提示如下信息的:
* What went wrong:
Execution failed for task ':app:transformClassesWithJarMergingForDevDebug'. > com.android.build.api.transform.TransformException: java.util.zip.ZipException: duplicate entry: com/alibaba/android/arouter/routes/ARouter$$Group$$trans_second.class 復制代碼
但如果直接通過./gradlew命令方式構建,是可以直接出現如上錯誤提示的。大多數情況下,項目開發中直接通過run構建,使得此問題直接被隱藏。
分析與解決
對比兩者實際上構建流程上的差異,發現通過./gradlew命令方式構建,在執行taskapp:transformClassesWithJarMergingForDevDebug時會拋出duplicate entry class錯誤,但Android Studio直接run則沒有執行。
為了兼容Android Studio直接run形式,在開發人員開發階段早些發現此類問題,可以自己實現一個task,加入到構建過程中的適當階段,以自動檢測在不同模塊下ARouter生成的文件重復問題。
具體思路:
分別統計項目中各個模塊中ARouter生成的java文件,並計次數(需要區分構建類型,一個構建類型算唯一的一次即可,否則對於多個變體情況下會重復計算),對於同一構建類型,同樣的文件名,生成的文件次數多余1,顯然應該直接構建失敗,並給出具體提示。
實現:
1,首先自定義task,實現次數檢測和統計:
task checkARouterDuplicatedJavaFiles {
doLast { def fileMap = [:] def buildTypeList = [] def hasPathBuildTypeList = [] project.extensions.findByName("android").applicationVariants.all { variant -> def buildTypeName = variant.buildType.name if(!buildTypeList.contains(buildTypeName)) { buildTypeList.add(buildTypeName) } } project.rootProject.subprojects { subProject -> def subProjectBuildDir try { subProjectBuildDir = subProject.buildDir if (subProjectBuildDir == null) return subProjectBuildDir.eachFileRecurse(FileType.DIRECTORIES) { dir -> if (dir.path.contains("/generated/source/kapt/") && dir.path.endsWith("com/alibaba/android/arouter/routes")) { def filePrefix = "" for (buildType in buildTypeList) { if(dir.path.toLowerCase().contains(buildType + "/") && !hasPathBuildTypeList.contains(subProject.getName() + "/generated/source/kapt/" + buildType + "/")){ filePrefix = buildType + "/" dir.eachFile(FileType.FILES) { file -> if (fileMap[filePrefix + file.name] == null) { fileMap[filePrefix + file.name] = 0 } fileMap[filePrefix + file.name]++ } hasPathBuildTypeList.add(subProject.getName() + "/generated/source/kapt/" + filePrefix) return } } } } } catch (Exception e) { // ignore println e.toString() } } fileMap.each { key, value -> if (value > 1) { throw new GradleException("ARouter: " + key + " fileCount: " + value + " ,路由地址設置有誤!") } } } } 復制代碼
上述代碼中的hasPathBuildTypeList邏輯是因為app module中的變體與library module中的變體設置不一樣,以處理對應的兼容邏輯。
2,將此task加入到構建流程的適當階段。通過對比實際的構建過程中執行的task列表,最終決定將名稱為“assembleDevDebug”的task依賴自定義的checkARouterDuplicatedJavaFilestask,並將自定義的task依賴名稱為“transformClassesWithCom.alibaba.arouterForDevDebug”的task。 具體實現為:
project.tasks.whenTaskAdded { Task task ->
if (task.name == "assembleDevDebug") { task.dependsOn(checkARouterDuplicatedJavaFiles) } else if (task.name == "transformClassesWithCom.alibaba.arouterForDevDebug") { checkARouterDuplicatedJavaFiles.dependsOn(task) } } 復制代碼
其中,DevDebug為我們開發環境下默認的構建變體。
最終可以確保自定義的checkARouterDuplicatedJavaFilestask可以在構建過程中完成對應的檢測。 如果通過./gradlew命令構建,依然可以達到以系統taskapp:transformClassesWithJarMergingForDevDebug為先。
通過Android Studio run,如果重現此類情形,最終效果為:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:checkARouterDuplicatedJavaFiles'. at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:100) ... at java.lang.Thread.run(Thread.java:745) Caused by: org.gradle.api.GradleException: ARouter: release/ARouter$$Group$$GroupName1.java fileCount: 2 ,路由地址設置有誤! at build_4p6esrqwzg61igroldd1aht2w$_run_closure5$_closure42.doCall(/Users/corn/AndroidStudioProjects/MyCorn/app/build.gradle:395) ... 復制代碼
構建失敗,並給出提示。
結語
ARouter官方建議在不同的模塊下本就不應該使用同樣的分組,分組名可以使用模塊名或其他名稱,但分組名與模塊本質上是一種映射關系,無論如何設置,考慮到其他模塊需要使用到此路由地址,當這種映射關系與模塊化相結合時,這一可能存在沖突的矛盾直接在分組名的定義上,通過技術手段是無法直接完全隔離開的。因此,考慮通過自定義task並加入到構建過程中的適當階段,以自動檢測潛在的可能的人為失誤,是一種有效方案。
作者:HappyCorn
鏈接:https://juejin.im/post/5ce7df33518825767072b5ef
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
