懶人模式開啓Android模塊自動化Api之旅

推薦閱讀:

滴滴Booster移動App質量優化框架-學習之旅 一

Android 模塊Api化演練

不一樣視角的Glide剖析(一)

在將業務進行模塊化時,避免不了模塊頁面路由和模塊通信, 大多數我們會用到ARouter,EventBus三方庫。 模塊化過程中有一個尷尬的問題擺在面前:Event事件、Router path放在哪裏? 因爲很多業務module都需要收發Event事件,進行頁面路由,所以只能將Event, Router path下沉到基礎庫。 這樣導致的結果是基礎庫越來越大,至多 把Event事件、Router path擺放在獨立的module,然後基礎庫依賴這個庫,如下圖所示:

我們希望業務模塊發送的事件,註解使用的Router path都在模塊自己這裏定義,而不是下層到基礎庫,當其他module需要路由、事件、 接口就暴露出來。關於這點《微信Android模塊化架構重構實踐》 也提到了這件事情,並且自創性的使用了一種叫“.api”化的方式來解決這件事情。原理是在編譯期將公用接口下沉到基礎庫同層級, 供其他module使用,而這段代碼的維護仍然放到非基礎庫中。這種base庫不會膨脹,代碼維護的責任制更明確,確定挺不錯。如下圖:

在ModuleA,B把XXXBusEvents、XXXRouterParams,暴露的公用接口文件後綴名以.api (並不要求一定.api後者,只要跟後續的自動Api化插件或者腳本一致就行)命名, rebuild之後自動生成ModuleA-api,ModuleB-api 模塊,ModuleA,B也會自動添加各自對應 api module依賴。

講完了原理,下面就可以實現,這裏使用ARouter,EventBus,只對Java文件進行Api化,步驟如下:

新建工程,創建base、moduleA、moduleB 模塊在moudleA,moduleB中創建api文件:

默認情況下,Android stuio 是不能識別.api文件,如果想編輯.api後綴的java文件, 爲了能讓Android Studio繼續高亮該怎麼辦?可以在File Type中把.api作爲java文件類型,操作如下圖:

設置好後,可以在.api文件中像java文件一樣愉快擼代碼了,其他類可以引用.api中的類。

查看setting.gradle文件腳本如下:

include ':app', ':base',':modulea',':moduleb'

include 4個module,做個測試,在setting.gradle include test,同步後,test目錄下只有iml文件, 沒有build.gradle、AndroidManifest.xml等文件,所以除了拷貝.api文件到對應目錄並重命名爲.java, 還需要額外創建這兩個文件,這裏我事先在base module中準備了通用module的build.gradle文件, 拷貝到對應目錄即可,AndroidManifest.xml就拷貝base module目錄下的,腳本實現如下:

def includeWithApi(String moduleName,String baseModuleName) {
    //先正常加載這個模塊
    include(moduleName)

    //找到這個模塊的路徑
    String originDir = project(moduleName).projectDir
    //這個是新的路徑
    String targetDir = "${originDir}-api"
    //新模塊的路徑
    def sdkName = "${project(moduleName).name}-api"
    //新模塊名字
    String apiName="${moduleName.substring(1,moduleName.length())}-api"


    //這個是公共模塊的位置,我預先放了一個 ApiBuildGralde.gradle 文件進去
    String apiGradle = project(baseModuleName).projectDir

    // 每次編譯刪除之前的文件
    deleteDir(targetDir)

    //複製.api文件到新的路徑
    copy() {
        from originDir
        into targetDir
        exclude '**/build/'
        exclude '**/res/'
        include '**/*.api'
    }

    //直接複製公共模塊的AndroidManifest文件到新的路徑,作爲該模塊的文件
    copy() {
        from "${apiGradle}/src/main/AndroidManifest.xml"
        into "${targetDir}/src/main/"
    }

    //file("${targetDir}/src/main/java/com/dhht/${apiName}/").mkdirs()

    //修改AndroidManifest文件
    //fileReader("${targetDir}/src/main/AndroidManifest.xml",apiName);

    //複製 gradle文件到新的路徑,作爲該模塊的gradle
    copy() {
        from "${apiGradle}/ApiBuildGralde.gradle"
        into "${targetDir}/"
    }

    //刪除空文件夾
    deleteEmptyDir(new File(targetDir))

    //重命名一下gradle
    def build = new File(targetDir + "/ApiBuildGralde.gradle")
    if (build.exists()) {
        build.renameTo(new File(targetDir + "/build.gradle"))
    }

    // 重命名.api文件,生成正常的.java文件
    renameApiFiles(targetDir, '.api', '.java')

    //正常加載新的模塊
    include ":$sdkName"

}

修改setting.gradle文件如下

include ':app', ':base'
includeWithApi(":modulea",":base")
includeWithApi(":moduleb",":base")

rebuild後,就可以看到moduleA-api,moduleB-api,並有對應的java文件如下圖:

添加moduleA路由到moduleB,moduleB給moduleA發送事件邏輯,進行打包,會報如下錯誤:

很顯然,ARouter註解處理器無法識別.api文件,path置爲null處理,在moduleA,B添加對應的***-api模塊依賴,就可以打包成功了。

奔着偷懶的原則,不想每次手動添加***-api模塊依賴,自動動態添加依賴,實現gradle腳本如下:

ext{
     //自動添加***-api依賴
    autoImportApiDependency = {extension -> //extension project對象
        def children = project.rootProject.childProjects
        //遍歷所有child project
        children.each {child ->
            //判斷 是否同時存在 *** module 和 ***-api module
            if(child.key.contains("-api") && children.containsKey(child.key.substring(0,child.key.length() - 4))){
                print "\n"

                def targetKey = child.key.substring(0,child.key.length() - 4)
                def targetProject = children[targetKey]

                targetProject.afterEvaluate {

                    print '*********************\n'
                    print targetProject.dependencies
                    //通過打印 所有dependencies,推斷需要添加如下兩個依賴
                    targetProject.dependencies.add("implementation",targetProject.dependencies.create(project(":" + child.key)))
                    targetProject.dependencies.add("implementationDependenciesMetadata",targetProject.dependencies.create(project(":" + child.key)))

                    //打印 module 添加的依賴
                    targetProject.configurations.each {configuration ->
                        print '\n---------------------------------------\n'
                        configuration.allDependencies.each { dependency ->

                            print configuration.name + "--->" +dependency.group + ":" + dependency.name + ":" + dependency.version +'\n'
                        }

                    }



                    print '*********************\n'
                }

            }


        }
    }
}

autoImportApiDependency 方法封裝在Config.gradle,在根build.gradle中調用:

apply from: 'Config.gradle'
ext.autoImportApiDependency(this)

可以正常打包,併成功運行了。

 

遇坑集錦

1.kotlin集成ARouter,儘管設置了AROUTER_MODULE_NAME,依然報如下錯誤: ARouter::Compiler An exception is encountered, [null] 可以考慮是否是gradle和 kotlin 版本的問題。

2.業務模塊moduleA處於集成模式時,即集成到App殼工程中去,也會將單一模塊做 成App啓動的源碼和資源打包apk中,儘管設置了sourceSets,也沒效果。

問題就出在debug文件夾的名字,把debug文件夾改成其他名字,就沒有這個問題了,是不是很奇怪!沒去究其原因。

傳送門:懶人模式開啓Android模塊自動化Api之旅github

 

參考資料:

微信 Android 模塊化架構重構實踐(上)

Android實現模塊 api 化

美團貓眼電影Android模塊化實戰總結

 

如果您對博主的更新內容持續感興趣,請關注公衆號!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章