自動檢測ARouter路由地址分組使用衝突問題

背景

項目中使用ARouter進行路由,因爲不一樣上層業務模塊均可能會使用到同一目標的路由地址,所以,將全部業務模塊的路由地址以一種相似靜態常量的方式設置在Base模塊中。這樣,在實際目前上加上對應此地址的註解,就能夠將其對應加入到路由中。使用方經過ARouter對應的地址方式去路由,便可訪問到對應的目標。java

以Activity路由爲例,經過註解,編譯後在對應模塊路徑下生成的文件名爲Arouter$$Group$$GroupName1.java文件。其中GroupName1爲分組名。 具體路徑爲:android

/build/generated/source/kapt/變體/com/alibaba/android/arouter/routes/
複製代碼

原則上,不一樣模塊應該註解到不一樣的路由地址分組。不然不一樣的模塊下編譯後會生成相同的Arouter$$Group$$GroupName1.java文件,在項目構建安裝後,會發生不可預期的路由地址失敗問題(如其中一個Arouter$$Group$$GroupName1.java文件中的路由生效,另外一個直接路由失敗)。git

網上查了下,發現一樣問題,其餘人也有遇到,具體問題描述GitHub上ARouter項目中issues等: github.com/alibaba/ARo…
github.com/alibaba/ARo…
github.com/alibaba/ARo…github

近期,項目中在進行模塊化改進時,因爲部分註解了路由地址的目標文件被從一個模塊移動到另外一個模塊,導入出現一樣問題發。緣由在於,若是直接經過Android Studio中的三角形綠色圖標直接run android app時,對於不一樣模塊下生成的同名java文件在編譯及打包組裝過程當中是不會提示以下信息的:api

* 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構建,使得此問題直接被隱藏。bash


分析與解決

對比二者實際上構建流程上的差別,發現經過./gradlew命令方式構建,在執行taskapp:transformClassesWithJarMergingForDevDebug時會拋出duplicate entry class錯誤,但Android Studio直接run則沒有執行。app

爲了兼容Android Studio直接run形式,在開發人員開發階段早些發現此類問題,能夠本身實現一個task,加入到構建過程當中的適當階段,以自動檢測在不一樣模塊下ARouter生成的文件重複問題。模塊化

具體思路:
分別統計項目中各個模塊中ARouter生成的java文件,並計次數(須要區分構建類型,一個構建類型算惟一的一次便可,不然對於多個變體狀況下會重複計算),對於同一構建類型,一樣的文件名,生成的文件次數多餘1,顯然應該直接構建失敗,並給出具體提示。gradle

實現:
1,首先自定義task,實現次數檢測和統計:ui

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並加入到構建過程當中的適當階段,以自動檢測潛在的可能的人爲失誤,是一種有效方案。

相關文章
相關標籤/搜索