懶人模式開啟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文件腳本如下:

1 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目錄下的,腳本實現如下:

 1 def includeWithApi(String moduleName,String baseModuleName) {
 2     //先正常加載這個模塊
 3     include(moduleName)
 4 
 5     //找到這個模塊的路徑
 6     String originDir = project(moduleName).projectDir
 7     //這個是新的路徑
 8     String targetDir = "${originDir}-api"
 9     //新模塊的路徑
10     def sdkName = "${project(moduleName).name}-api"
11     //新模塊名字
12     String apiName="${moduleName.substring(1,moduleName.length())}-api"
13 
14 
15     //這個是公共模塊的位置,我預先放了一個 ApiBuildGralde.gradle 文件進去
16     String apiGradle = project(baseModuleName).projectDir
17 
18     // 每次編譯刪除之前的文件
19     deleteDir(targetDir)
20 
21     //復制.api文件到新的路徑
22     copy() {
23         from originDir
24         into targetDir
25         exclude '**/build/'
26         exclude '**/res/'
27         include '**/*.api'
28     }
29 
30     //直接復制公共模塊的AndroidManifest文件到新的路徑,作為該模塊的文件
31     copy() {
32         from "${apiGradle}/src/main/AndroidManifest.xml"
33         into "${targetDir}/src/main/"
34     }
35 
36     //file("${targetDir}/src/main/java/com/dhht/${apiName}/").mkdirs()
37 
38     //修改AndroidManifest文件
39     //fileReader("${targetDir}/src/main/AndroidManifest.xml",apiName);
40 
41     //復制 gradle文件到新的路徑,作為該模塊的gradle
42     copy() {
43         from "${apiGradle}/ApiBuildGralde.gradle"
44         into "${targetDir}/"
45     }
46 
47     //刪除空文件夾
48     deleteEmptyDir(new File(targetDir))
49 
50     //重命名一下gradle
51     def build = new File(targetDir + "/ApiBuildGralde.gradle")
52     if (build.exists()) {
53         build.renameTo(new File(targetDir + "/build.gradle"))
54     }
55 
56     // 重命名.api文件,生成正常的.java文件
57     renameApiFiles(targetDir, '.api', '.java')
58 
59     //正常加載新的模塊
60     include ":$sdkName"
61 
62 }

 

修改setting.gradle文件如下:

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

 

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

 

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

 

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

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

 1 ext{
 2      //自動添加***-api依賴
 3     autoImportApiDependency = {extension -> //extension project對象
 4         def children = project.rootProject.childProjects
 5         //遍歷所有child project
 6         children.each {child ->
 7             //判斷 是否同時存在 *** module 和 ***-api module
 8             if(child.key.contains("-api") && children.containsKey(child.key.substring(0,child.key.length() - 4))){
 9                 print "\n"
10 
11                 def targetKey = child.key.substring(0,child.key.length() - 4)
12                 def targetProject = children[targetKey]
13 
14                 targetProject.afterEvaluate {
15 
16                     print '*********************\n'
17                     print targetProject.dependencies
18                     //通過打印 所有dependencies,推斷需要添加如下兩個依賴
19                     targetProject.dependencies.add("implementation",targetProject.dependencies.create(project(":" + child.key)))
20                     targetProject.dependencies.add("implementationDependenciesMetadata",targetProject.dependencies.create(project(":" + child.key)))
21 
22                     //打印 module 添加的依賴
23                     targetProject.configurations.each {configuration ->
24                         print '\n---------------------------------------\n'
25                         configuration.allDependencies.each { dependency ->
26 
27                             print configuration.name + "--->" +dependency.group + ":" + dependency.name + ":" + dependency.version +'\n'
28                         }
29 
30                     }
31 
32 
33 
34                     print '*********************\n'
35                 }
36 
37             }
38 
39 
40         }
41     }
42 }

 

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

1 apply from: 'Config.gradle'
2 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模塊化實戰總結

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM