原文連接javascript
一、用通俗易懂的講解方式,講解一門技術的實用價值
二、詳細書寫源碼的追蹤,源碼截圖,繪製類的結構圖,儘可能詳細地解釋原理的探索過程
三、提供Github 的 可運行的Demo工程,可是我所提供代碼,更可能是提供思路,拋磚引玉,請酌情cv
四、集合整理原理探索過程當中的一些坑,或者demo的運行過程當中的注意事項
五、用gif圖,最直觀地展現demo運行效果*php若是以爲細節太細,直接跳過看結論便可。
本人能力有限,如若發現描述不當之處,歡迎留言批評指正。css
最好結合視頻和源碼以及PPT圖片來深刻學習本篇文章內容
java
學到老活到老,路漫漫其修遠兮。與衆君共勉 !node
最近得高人指點,恰巧工做中作過一個移植其餘app的某個功能模塊的任務,過程簡直痛不欲生。
而後思考如何對app的各個功能模塊進行靈活拔插配置,最大程度減小移植代碼出錯的可能性,保證功能模塊的完整移植*android
獨立開發git
更改gradle.properties的配置,使得每一個功能模塊都成爲application, 能夠獨立打包成apk,單獨運行。單個模塊,獨立測試。github
集成打包編程
更改gradle.properties的配置,使得原先每一個單獨模塊,都變成library,被 主模塊引用,這時候只有主模塊可以打包apk,全部功能都集成在這個apk內。api
故事背景
當大家公司有多個安卓開發人員,開發出核心業務相同,可是UI不一樣,其餘業務不一樣的一系列App時(若是核心業務是X,大家有5個開發人員,作出了A,B,C,D,E 5個app,都包含核心業務X,可是除了X以外,其餘的業務模塊各不相同)這時候,若是領導要把A裏面的一個非核心功能,挪到B裏面...
現狀
開發B的程序猿可能要罵娘,由於他在從移植A的代碼中剝離代碼 遇到了不少高耦合,低內聚 的類結構,挪過來以後,牽一髮而動全身,動一點小地方,整個代碼滿江紅。
理想
若是這個時候,咱們經過代碼框架的配置,可以把A裏面的一個模塊,
做爲一個module移植到 工程內部,而後主module 來引用這個module,略微寫一些代碼來使得這個功能模塊在app中生效。那麼不管是多少個功能模塊,均可以做爲總體來 給其餘app複用。這樣開發人員也不用相互罵娘了,若是挪過來的模塊存在bug或者其餘問題,也不用甩鍋,模塊本來是誰開發的,找誰就行了。
咱們開發app的功能模塊,一個業務,多是經過一個Activity或者 一個Fragment 做爲對外的窗口,也多是。所謂窗口,就是這個業務,相對於其餘模塊,"有且只有"一個入口,沒有任何其餘能夠觸達到這個業務的途徑。*業務代碼之間相互隔離,絕對不能夠有相互引用。那麼,既然相互不會引用,那A模塊必定要用到B模塊的數據,怎麼辦呢?下文提供解決方案。
一、代碼結構現狀以及理想狀態一覽
二、功能組件化的實現思路,實現組件移植拔插
三、參考ARouter源碼,寫出本身的Router框架,統一經過Router來進行模塊的切換 以及 組件之間數據的交互
四、使用組件api化,在模塊不少的狀況下優化公共模塊的結構
先來看兩張圖
代碼有模塊化的跡象,可是沒有對業務模塊進行很是明顯的模塊化(不明白啥意思是吧?不明白就對了,app這個module裏面其實還有不少東西沒有展現出來,請看下圖:試想,把全部的模塊集中到一個module的一個包裏面,當你要移植某一個功能的時候,想一想那酸爽....固然若是你口味別緻,那當我沒說)
理想化的話,參照:理想.png; 項目結構井井有條,脈絡清晰
外殼層:app module
內部代碼只寫 app的骨骼框架,好比說,你的app是這個樣子的結構:
下方有N個TAB,經過Fragment來進行切換模塊。這種架構確定很多見。
這個時候,外殼層 app module,就只須要寫上 上面這種UI架構的框架代碼就好了,至於有多少個模塊,須要代碼去讀取配置進行顯示。神馬?你問我怎麼寫這種UI框架?網上一大把,若是實在找不到,來個人 github項目地址
๑乛◡乛๑
業務層
咱們的業務模塊,對外接口多是一個
Activity
*(好比說,登陸模塊,只對外提供一個LoginActivity
,有且僅有這一個窗口)或者 是一個Fragment
,就像上圖(典型的app架構.png), 若是app的UI框架是經過切換Fragment
來卻換業務模塊的話。用business
這個目錄,將全部的業務模塊包含進去,每一個模塊又是獨立的module
,這樣既實現了業務代碼隔離,又能一眼看到全部的業務模塊,正所謂,一目瞭然。
功能組件層
每個業務模塊,不可避免的須要用到一些公用工具類,有的是第三方SDK的再次封裝,有的是本身的工具類,或者本身寫的自定義控件,還有多是 全部業務模塊都須要的 輔助模塊,都放在這裏。
路由框架層
設計這一層,是想讓app內的全部Activity,業務模塊Fragment,以及模塊之間的數據交互,都由 這一層開放出去的接口來負責
gradle統一配置文件
工程內部的一些全局gradle變量,放在這裏,整個工程都有效
module編譯設置
setting.gradle 配置要編譯的module; 也能夠作更復雜的操做,好比,寫gradle代碼去自動生成一些module,免除人爲建立的麻煩.
可以兼顧 每一個模塊的單獨開發,單獨測試 和 總體打包,統一測試。 聽起來很神奇的樣子,可是其實就一個核心:gradle編程。
打開gradle.properties文件:
註解應該很清晰了,經過一個全局變量,就能夠控制當前是要 模塊化單元測試呢?仍是要集成打包apk測試。
1- app 外殼層module 的build.gradle(注意:寫在dependencies)
if (isModule.toBoolean()) {
implementation project(":business:activity_XXX") //...在這裏引用更多業務模塊 } 複製代碼
2- 每一個業務module的build.gradle
第一處:斷定 isModule,決定當前module是要當成library仍是application
if (isModule.toBoolean()) {
apply plugin:'com.android.library'
} else {
apply plugin:'com.android.application'*
}
複製代碼
第二處:更改defaultConfig裏面的部分代碼,爲何要改?由於噹噹前module做爲library的時候,不能有applicationId "XXXX"這一句
defaultConfig {
if (!isModule.toBoolean()) {
applicationId"study.hank.com.XXXX"*
}
....
}
複製代碼
第三處:當業務模塊module做爲library的時候,不能夠在 AndroidManifest.xml中寫 Launcher Activity,不然,你打包app module的時候,安裝完畢,
sourceSets {
main {
if (isModule.toBoolean()) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
複製代碼
因爲要區分對待,咱們就須要另外建立一個manifest文件,移除launcher配置便可。參考下圖:
這就是業務模塊組件化的祕密了。
什麼,你問我怎麼 進行功能拔插?
當你不須要某一個模塊的時候,
1)在app的build.gradle裏面 把 引用該模塊的配置去掉;
2)setting.gradle 的include 去掉它
3)app module 裏面,改動代碼,再也不使用這個模塊。(這個我就不截圖了,由於app module的UI框架代碼不是一句話說得清的。請運行個人demo源碼本身看吧)
功能的插入,同理,上面的過程倒過來走一遍,就不浪費篇幅了。
說到路由框架的使用價值,兩點:
一、在app實現了組件化以後,因爲組件之間因爲代碼隔離,不容許相互引用,致使 相互不能直接溝通,那麼,就須要一個
「中間人角色」來幫忙" 帶話"了.二、app內部,不可避免地要進行Activity跳轉,Fragment切換。把這些重複性的代碼,都統一讓路由來作吧。省了很多代碼行數。
閱讀了阿里巴巴ARouter的源碼,參照阿里大神的主要思路,簡化了一些流程,去掉了一些我不須要的功能,增長了一些我獨有的功能,加入了一些本身的想法,寫出了本身的 ZRouter 路由 框架。那就不羅嗦了,上乾貨。
若是如下基礎知識不具有,建議先去學習基礎知識。 或者 也能夠跟着筆者的思路來看代碼,慢慢理解這些知識的實用價值。
java反射機制(路由框架裏大量地使用了 class反射建立 對象)
APT 註解,註解解析機制(註解解析機制貫穿了整個路由框架)
javapoet , java類的元素結構(一些人爲寫起來很麻煩的代碼,一些髒活累活,就經過自動生成代碼來解決)
1- 在app module的自定義Application類裏面,進行初始化,
public class FTApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ZRouter.getInstance().initRegister(this);
}
}
複製代碼
切換Fragment*
ZRouter.getInstance().build(RouterPathConst.PATH_FRAGMENT_MINE).navigation();
複製代碼
跳轉Activity
ZRouter.getInstance().build(RouterPathConst.PATH_ACTIVITY_CHART).navigation();
複製代碼
組件之間的通訊,取得Mine模塊的 accountNo 而後 toast出來
String accountNo = ZRouter.getInstance().navigation(MineOpenServiceApi.class).accountNo();
Toast.makeText(getActivity(), accountNo, Toast.LENGTH_LONG).show();
複製代碼
如咱們以前所設想的,切換Fragment,跳轉Activity,組件之間的通訊 所有隻能經過 ZRouter框架來執行。
3- 退出app時,要釋放ARouer的資源(主要是靜態變量)
ZRouter.getInstance().release();
複製代碼
4- 每一個業務模塊,在將要暴露出去的Fragment或者Activity上,要加上註解
@ZRoute(RouterPathConst.PATH_ACTIVITY_CHART)//註冊Activity
public class ChartActivity extends AppCompatActivity {···}
複製代碼
或者
@ZRoute(RouterPathConst.PATH_FRAGMENT_HOME)//註冊Fragment
public class HomeFragment extends Fragment {···}
複製代碼
或者
@ZRoute(RouterPathConst.PATH_PROVIDER_MINE) // 註冊數據接口
public class MineServiceImpl implements MineOpenServiceApi {···}
複製代碼
講解設計思路,必須用源碼進行參照,請務必參照源碼 。
源碼地址爲:github.com/18598925736…
說明一下本人 閱讀源碼的方法。也許不少人都和曾經的我同樣,看到一份第三方SDK的源碼,不知道從何下手,要麼看了半天還在原地打轉轉,要麼就是這裏看一點,那裏看一點,沒有中心思想,看了半天毫無收穫。
乾貨:看源碼要思路清晰,目的明確。一切技術的價值都只有一個,那就是解決實際問題。既然是解決實際問題,那咱們就從這個SDK暴露出來的最外圍接口爲起點,看這個接口的做用是什麼,解決了什麼問題,順藤摸瓜,找找它解決問題的核心方法,至於順藤摸瓜道路上遇到的枝枝脈脈,要分清哪些是輔助類(每一個人寫輔助類的習慣可能都不一樣,因此沒必要太在乎),哪些是核心類(核心思想通常都是大同小異)。找到了核心思想,再從頭從新過幾遍,SDK的設計思路就瞭然於胸了.
按照個人上面提供的「乾貨」,若是你如今下載了個人Demo源碼,那麼咱們繼續:
若是把看源碼的結構,理解爲 警察查案。那麼就要從最表層的現象開始着手,慢慢查找根源。
HomeFragment.java的54行, 這裏要進行Activity跳轉。
ZRouter.getInstance().build(RouterPathConst.PATH_ACTIVITY_CHART).navigation();
這裏有getInstance()方法,build()方法,還有navigation()方法,一個一個看
getInstance()
是處在ZRouter類內部,是ZRouter的單例模式的get方法,單例模式就不贅述了,我寫了註釋build()
方法也是在ZRouter類內部,邏輯很簡單,就是new Postcard(path)
參數path
是一個string
,方法返回值是一個Postcard
對象navigation()
方法是在Postcard類內部,可是,具體的執行邏輯,依然是在ZRouter
類裏面getInstance()
和build()
方法都很簡單,不須要花太多精力。下面繼續跟隨ZRouter
的navigation()
方法「追查」
ZRouter
的navigation()
方法內容以下:
Object navigation(Postcard postcard) {
LogisticsCenter.complete(postcard);
switch (postcard.getRouteType()) {
case ACTIVITY://若是是Activity,那就跳吧
return startActivity(postcard);
case FRAGMENT://若是是Fragment,那就切換吧
return switchFragment(postcard);
case PROVIDER://若是是Provider,那就執行業務邏輯
return postcard.getProvider();//那就直接返回provider對象
default:
break;
}
return null;
}
複製代碼
發現一個可疑的代碼:
LogisticsCenter.complete(postcard);
看方法名,應該是對postcard對象進行完善。
進去追查
/** * Postcard字段補全 * * @param postcard */
public static void complete(Postcard postcard) {
if (null == postcard) {
throw new RuntimeException("err:postcard 是空的,怎麼搞的?");
}
RouteMeta routeMeta = Warehouse.routeMap.get(postcard.getPath());//
if (null == routeMeta) {//若是路由meta是空,說明可能這個路由沒註冊,也有可能路由表沒有去加載到內存中
throw new RuntimeException("err:路由尋址失敗,請檢查是否path寫錯了");
} else {
postcard.setDestination(routeMeta.getDestination());
postcard.setRouteType(routeMeta.getRouteType());
···
}
}
複製代碼
這段代碼,從一個
map
中,用path
做爲key
,get
出了一個RouteMeat
對象,而後用這個對象的字段值,對參數postcard
的屬性進行賦值。好像有點莫名其妙。看不太懂。不着急,繼續。
剛纔的
navigation()
方法這裏存在switch
分支,分支設計到ACTIVITY,FRAGMENT,PROVIDER
,因爲咱們此次追查的只是activity
相關,因此,忽略掉其餘分支,只追查startActivity(postcard);
下面是該方法的代碼:
private Object startActivity(Postcard postcard) {
Class<?> cls = postcard.getDestination();
if (cls == null) {
if (cls == null)
throw new RuntimeException("沒找到對應的activity,請檢查路由尋址標識是否寫錯");
}
final Intent intent = new Intent(mContext, cls);
if (Postcard.FLAG_DEFAULT != postcard.getFlag()) {//若是不是初始值,也就是說,flag值被更改過,那就用更改後的值
intent.setFlags(postcard.getFlag());
} else {//若是沒有設定啓動模式,即 flag值沒有被更改,就用常規模式啓動
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//常規模式啓動Activity
}
//跳轉只能在主線程中進行
runInMainThread(new Runnable() {
@Override
public void run() {
mContext.startActivity(intent);
}
});
return null;
}
複製代碼
這裏只是一個簡單的跳轉操做,可是,發現了一個關鍵點,跳轉的「目的地」
class
是來自postcard
的destination
. 發現規律了,原來剛纔在LogisticsCenter.complete(postcard);
裏面進行postcard
「完善」的時候,set
進去的destination
原來在這裏被使用到。
那麼問題的關鍵點就發生了轉移了, 這個
destination
Class
是從map
裏面get
出來的,那麼,又是何時被put
進去的呢?
開始追蹤這個map
:Warehouse.routeMap
,經過代碼追蹤,能夠發現,惟一可能往map
裏面put
東西的代碼只有這一句:
/** * 反射執行APT註冊文件的註冊方法 */
private static void registerComm() {
try {
Set<String> classNames = ClassUtils.getFileNameByPackageName(mContext, RouterConst.GENERATION_PACKAGE_NAME);//找到包名下的全部class
for (String className : classNames) {
Class<?> clz = Class.forName(className);
if (IRouterZ.class.isAssignableFrom(clz)) {
IRouterZ iRouterComm = (IRouterZ) clz.getConstructor().newInstance();
iRouterComm.onLoad(Warehouse.routeMap);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
Warehouse.traversalCommMap();
}
}
複製代碼
利用java反射機制,反射建立類的實例,而後執行
onLoad
方法,參數,正是這個map
OK,關於查看源碼的詳細步驟,就寫到這麼多,再羅嗦,大佬們要打人啦。
目前爲止的結論:經過追蹤
ZRouter.getInstance().build(RouterPathConst.PATH_ACTIVITY_CHART).navigation();
咱們一路上遭遇了這些類或接口:
核心類 :
ZRouter(提供Activity跳轉的接口);輔助類或接口
Postcard (「明信片」,封裝咱們要執行操做,此次的操做是 跳Activity)
RouteMeta (「路由參數」,Postcard的基類)
RouteType (「路由類型」,咱們要執行的操做,用枚舉來進行區分)
LogisticsCenter ("物流中心",主要封裝ZRouter類的一些特殊邏輯,好比對Postcard對象進行完善補充 )
Warehouse (「貨艙」,用hashMap來存儲「路由」對象)
IRouterZ ("路由註冊"接口類 ,用於反射建立對象,從而進行路由的註冊)
上面用大量篇幅詳述了 追蹤源碼, 追查框架結構的方法,那麼下面的篇幅就直接說結論了:
路由框架的結構,能夠用一張圖表示:
針對這張圖 簡單說兩句:
路由框架必然有3個部分,註解定義,註解解析,以及路由對外接口。demo
中我把這3個部分定義成了3個module
.
其中,每一個部分的核心代碼是:zrouter-annotation
模塊的ZRoute @interface,IRouterZ
接口zrouter-api
模塊的ZRouter
類zrouter-compiler
模塊的RouterProcessor
類
具體的代碼,不加以說明了。
如何用路由進行Activity
跳轉,我寫了詳細步驟,相信沒人看不懂了。那麼Fragment
的切換,是我自定義的方法,可能有點粗糙,可是也是通俗易懂,就點到爲止。可是,咱們組件化的思想,就是要隔離全部的業務模塊,彼此之間不能進行直接通訊,若是A模塊必定要使用B模塊的一些數據,經過路由框架也能實現。
HomeFragment類的第72行代碼:
String accountNo = ZRouter.getInstance().navigation(MineOpenServiceApi.class).accountNo();
這句代碼的意義是:在Home模塊中,經過路由框架,調用Mine模塊對外開放的接口accountNo();
追蹤這句代碼的navigation()
方法,找到真正的執行邏輯 ZRouter類
141行起:
public <T> T navigation(String serviceName) {
Postcard postcard = LogisticsCenter.buildProvider(serviceName);
if (null == postcard)
return null;
LogisticsCenter.complete(postcard);//補全postcard字段值
return (T) postcard.getProvider();
}
複製代碼
這裏:最終返回了一個Provider對象.
LogisticsCenter
類又有了戲份:LogisticsCenter.buildProvider(serviceName)
和LogisticsCenter.complete(postcard);
分別點進去看:
buildProvider(String)
方法,其實就是從map
中找出RouteMeta
對象,而後返回一個Postcard
.
public static Postcard buildProvider(String name) {
RouteMeta routeMeta = Warehouse.routeMap.get(name);
if (null == routeMeta) {
return null;
} else {
return new Postcard(routeMeta.getPath());
}
}
複製代碼
complete(Postcard)
方法,其實就是完善postcard
的字段,且,針對Provider
,進行特別處理,反射建立Provider
對象,並創建Provider
的緩存機制,防止屢次進行數據交互時進行無心義的反射建立對象。
/** * Postcard字段補全 * * @param postcard */
public static void complete(Postcard postcard) {
if (null == postcard) {
throw new RuntimeException("err:postcard 是空的,怎麼搞的?");
}
RouteMeta routeMeta = Warehouse.routeMap.get(postcard.getPath());//
if (null == routeMeta) {//若是路由meta是空,說明可能這個路由沒註冊,也有可能路由表沒有去加載到內存中
throw new RuntimeException("err:路由尋址失敗,請檢查是否path寫錯了");
} else {
postcard.setDestination(routeMeta.getDestination());
postcard.setRouteType(routeMeta.getRouteType());
switch (routeMeta.getRouteType()) {
case PROVIDER://若是是數據接口Provider的話
Class<? extends IProvider> clz = (Class<? extends IProvider>) routeMeta.getDestination();
//從map中找找看
IProvider provider = Warehouse.providerMap.get(clz);
//若是沒找到
if (null == provider) {
//執行反射方法建立,而且存入到map
try {
provider = clz.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providerMap.put(clz, provider);
} catch (Exception e) {
e.printStackTrace();
}
}
postcard.setProvider(provider);
break;
default:
break;
}
}
}
複製代碼
看到這裏,整個路由框架,包括模塊間的通訊,就講解完畢了。
作個結論:
使用路由框架的目的,是 在項目代碼組件化的背景之下,優化Activity跳轉,Fragment切換的重複代碼的編寫,而統一使用路由框架的對外接口執行跳轉或者切換。同時,經過路由框架的對外接口,實現組件之間的無障礙通訊,保證組件的獨立性。
在探索框架的過程當中,咱們遇到了不少輔助類,可是輔助類怎麼寫,徹底看我的習慣,我是看了阿里巴巴的ARtouer框架以後獲得啓發,按照它的思路來寫本身的路由框架,可是不少輔助類的寫法,我並徹底按它的意思來。可是,核心思想,APT 註解+反射+自動生成代碼 是徹底同樣的。
因此說,打蛇打七寸,看框架要看核心,拿住核心以後,其餘的東西,就算代碼量再大,也是狐假虎威。
回顧一下理想中的項目結構:
背景
這裏的功能組件層 function,是存放各個業務模塊都須要的公共類或者接口。這裏說的公共類,也包含了剛纔所說起的 業務模塊之間進行通訊所須要的接口。
舉例說明:A模塊,須要調用B模塊的test()接口,因爲A不能直接引用B模塊,那這個test接口,只能放在function這個公共模塊內,而後A,B同時引用,B對test接口進行實現並經過註解進行路由註冊,A經過路由對外接口調用B的test方法。
現狀
誠然,這種作法沒毛病,可以實現功能。可是隨着項目模塊的增多,function 裏面會存在不少的業務模塊數據接口。有一種狀況:若是存在A,B,C,D,E 5個模塊,它們都在function內存放了 數據接口,而且5個模塊都引用了function模塊。那麼,當A須要,而且只須要B的數據接口,而不須要C,D,E的接口時,它仍是不得不去引用這些用不着的接口。A不須要這些接口,可是,還不得不引用!這顯然會不合邏輯。而且這種 所有業務數據接口都塞到function模塊裏面的作法,會致使function出現沒必要要的臃腫。
理想
每一個業務模塊的數據接口,只和本模塊的業務有關,因此最好是放在本模塊以內,可是,若是放在本模塊以內,又會致使組件之間不能通訊. 那麼就建立一個專門的 Module來存放每一個業務模塊的接口。想法可行,可是每一個業務模塊的module數量一會兒加倍了,又會形成維護困難的問題。那麼有沒有方法能夠自動生成這些數據接口模塊呢? 還真有~ 神奇的gradle編程 >_<
關鍵詞
組件API化技術
使用gradle配置,對module內的特殊後綴文件進行檢索,並以當前module爲基礎,自動生成新的module.
這個名叫MineOpenServiceApi的接口,本來是.java後綴,如今改爲.api
打開demo的setting.gradle文件:
找到下面的代碼:
include_with_api(':business:fragment_mine')
def include_with_api(String moduleName) {
include(moduleName)
//得到工程根目錄
String originDir = project(moduleName).projectDir
//製做的 SDK 工程的目錄
String targetDir = "${originDir}_api"
//製做的 SDK 工程的名字
String sdkName = "${project(moduleName).name}_api"
System.out.println("-------------------------------------SDK name:" + sdkName)
//刪除掉 SDK 工程目錄 除了 iml
FileTree targetFiles = fileTree(targetDir)
targetFiles.exclude "*.iml"
targetFiles.each { File file ->
file.delete()
}
//從待制做SDK工程拷貝目錄到 SDK工程 只拷貝目錄
copy {
from originDir
into targetDir
//拷貝文件
include '**/*.api'
include '**/AndroidManifest.xml'
include 'api.gradle'
}
//讀取實現模塊的manifest並將package的值後加 .api 做爲API工程的manifest package
FileTree manifests = fileTree(targetDir).include("**/AndroidManifest.xml")
manifests.each {
File file ->
def parser = new XmlParser().parse(file)
def node = parser.attribute('package')
parser.attributes().replace('package', "${node}.api")
new XmlNodePrinter(new PrintWriter(file)).print(parser)
}
//將api.gradle改成build.gradle
File build = new File(targetDir + "/api.gradle")
if (build.exists()) {
build.renameTo(new File(targetDir + "/build.gradle"))
}
// 將.api 文件改成 .java
FileTree files = fileTree(targetDir).include("**/*.api")
files.each {
File file ->
file.renameTo(new File(file.absolutePath.replace(".api", ".java")))
}
//加入 SDK工程
include ":business:" + "$sdkName"
}
複製代碼
這段代碼來自一位」真「大神,它的做用是,檢索指定模塊裏面,有沒有指定後綴名(.api)的文件,有的話,找出來,通過一系列處理(註解很詳細,應該能看懂),自動生成一個module. 生成的module名字比原來的module多一個_api. 表示這個模塊,包含原模塊的全部對外數據接口
有幾處細節須要注意:
數據接口的.java後綴須要改爲.api(整個.api徹底和setting.gradle代碼裏的.api對應,你能夠都換成其餘後綴,好比.apixxxxx)
原模塊裏面,會多出一個api.gradle,這個文件的名字也和 setting.gradle裏的api.gradle對應,也能夠修改![](https://upload-images.jianshu.io/upload_images/4100513-2d57590761b290bb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/211) 複製代碼
這個api.gradle並不會在本模塊被編譯的時候起做用,可是它最終會變成 _api 新模塊的build.gradle,並保持徹底同樣的代碼。 新的_api模塊只是一個library,因此,要去掉 本模塊裏面的build.gradle裏面針對isModule的斷定。
OK,感覺一下組件API化的成果:
理想實現了
如今不用把全部的數據接口都放到function公共模塊內,而只須要在本模塊以內將數據接口文件後綴改爲.api,而後在setting.gradle裏面使用自定義的方法進行include。 就能夠只引用本模塊須要的 數據接口module,而不須要引用多餘的module,並且,防止了function模塊的無心義的膨脹。簡直破費。
組件化的全攻略+Demo 已經所有放送完畢。
視頻和源碼能夠關注我我的介紹
特別說明: Demo只是提供一種組件化的全攻略,可能demo的代碼並無十分完善,好比:原ARouter源碼內的帶參數的跳轉,或者startActivityForResult,因爲時間關係我都去除了。一些輔助向的設計思路,我也並無徹底遵守ARouter源碼。