參考資料:
http://m.itdecent.cn/p/60c1b9ddd8ab
上一篇我們學(xué)習(xí)了ARouter,講到ARouter是組件化開發(fā)的基礎(chǔ),那現(xiàn)在讓我們開始組件化開發(fā)吧。
1.組件化,模塊化概念
對(duì)于組件化的開發(fā),首先要了解模塊化及組件化的概念,這正是是好多小伙伴模糊的,所以我們有必要說明一下。
1.1 組件
組件的英文單詞是component,意思是組件、部件、元件。在App工程上,件是構(gòu)成業(yè)務(wù)或者功能模塊的基本單位也就是組件不能被繼續(xù)拆分,原則上,組件與組件之間互不依賴。比如我們的圖片上傳功能,可以叫圖片上傳組件,但不能叫圖片上傳模塊,而且組件具有可替換性和重復(fù)利用性,可替換性指比如我們的地圖定位組件可以用百度的,也可以用高德的。重復(fù)性是指我的地位功能可能在首頁被調(diào)用,在其他頁面也要被調(diào)用。
1.2 模塊
模塊的英文單詞是Module,由多個(gè)組件構(gòu)成。也就是從粒度上來看,模塊要比組件大,也就是模塊包含組件。舉個(gè)例子:以安居客為例,安居客app內(nèi)有二手房和新房相關(guān)功能, 這個(gè)二手房和新房就屬于模塊,但這二手房和新房模塊都用到了分享房源的功能,而這個(gè)分享功能就屬于組件了。
1.3 組件和模塊的關(guān)系

我們以上圖為例簡單說一下,首頁界面的天貓,聚劃算,餓了么都屬于模塊同時(shí)也是一個(gè)業(yè)務(wù),我們可以叫做業(yè)務(wù)模塊,而我們繼續(xù)點(diǎn)擊天貓進(jìn)入天貓超市,里面的分享功能,支付所用到的sdk等都算組件。那我們總結(jié)一下:模塊化和組件化只不過是我們根據(jù)項(xiàng)目的需求,定義不同,一個(gè)工程可以由多個(gè)模塊組成,每個(gè)模塊可以由多個(gè)組件構(gòu)成,模塊可以單獨(dú)存在,正是多個(gè)模塊構(gòu)成了整個(gè)項(xiàng)目。不論是模塊化還是組件化,它們的目的都是把項(xiàng)目解耦便于代碼的管理。高內(nèi)聚、低耦合使開發(fā)人員分工明確,提高開發(fā)效率。
1.4 業(yè)務(wù)
我們上面說了模塊也可以叫做業(yè)務(wù)模塊,那么什么是業(yè)務(wù)呢,我們還是以上圖為例:在淘寶的首頁天貓,聚劃算,充值中心等這些看起來完全不同的業(yè)務(wù)我們稱之為Business業(yè)務(wù)。而天貓點(diǎn)擊進(jìn)入會(huì)有搜索業(yè)務(wù),預(yù)約功能,簽到等功能,聚劃算點(diǎn)擊進(jìn)入也會(huì)有搜索業(yè)務(wù),預(yù)約功能,簽到等功能,像這種業(yè)務(wù)我們稱之為基礎(chǔ)業(yè)務(wù)。我們來張圖說明一下Business業(yè)務(wù)和基礎(chǔ)業(yè)務(wù)及組件之間的關(guān)系:

圖中Business業(yè)務(wù)公用基礎(chǔ)業(yè)務(wù),而基礎(chǔ)業(yè)務(wù)的實(shí)現(xiàn)可能依賴于某組件。
1.4 Library
我們剛才說了組件,比如上圖提到的圖片上傳組件,網(wǎng)絡(luò)組件,還有分享組件等,那這里要介紹另外一個(gè)概念:Library。你比如我們的分享組件用的是友盟分享sdk,我們的網(wǎng)絡(luò)組件依賴的是okhttp庫,這些依賴的三方庫都稱之為Library。
2.組件化實(shí)踐
參考資料:
https://mp.weixin.qq.com/s/-gC8JpmmCZWzcOsH5ZzLtQ
https://mp.weixin.qq.com/s/8_8gGpkpO2QFNkWgSRBwIg
我準(zhǔn)備從以下幾個(gè)方面來介紹組件化:
- 代碼解耦
- 組件或模塊的單獨(dú)運(yùn)行
- 數(shù)據(jù)的傳遞與ui跳轉(zhuǎn)
- 生命周期管理
2.1代碼解耦
ok,我們先看看我們的demo實(shí)現(xiàn)效果,就先簡單上張圖吧,不整gif圖片了:

你看到的第一眼可能覺得簡單,沒什么大不了,但是這不同于我們平時(shí)的實(shí)現(xiàn)方法,平時(shí)我們的首頁,資訊,我的都是在app中實(shí)現(xiàn),但今天我們的目錄結(jié)構(gòu)是這樣的:

我先來說一下各個(gè)目錄的含義:
- app
app模塊是我們的殼工程,平時(shí)我們最核心的代碼都要往這里寫,但今天不一樣,app模塊的職能改變了,它最主要的目的是對(duì)其他模塊進(jìn)行整合,確保app能正常運(yùn)行,里面只有一些簡單的代碼。 -
commonlib
commonlib 是一個(gè)依賴的Library,為什么說她是library呢?因?yàn)樗穆毮苁前颜麄€(gè)project所用到的library,公用的lib都放到這里,供其它模塊引用,就不需要每個(gè)模塊都寫一遍了。以demo為例:
image.png
注意:采用api代替implementation代替的目的就是為了讓其他模塊能夠引用到
- module_home,module_zixun,module_user,
這三個(gè)模塊是整個(gè)project最核心的地方, 對(duì)應(yīng)我們上面效果圖中的首頁,資訊,我的三個(gè)tab。
這樣做的好處顯而易見 :張三開發(fā)首頁,李四開發(fā)資訊,王五開發(fā)我的 互不影響,提高開發(fā)效率 ,這也是組件化的優(yōu)勢(shì) - x5webview
x5webview是作為組件存在的,是我基于騰訊TBS瀏覽服務(wù)封裝的(類似webview作用),和分享組件,網(wǎng)路請(qǐng)求組件是一個(gè)級(jí)別,為了其他模塊的調(diào)用。
這里就基本實(shí)現(xiàn)了代碼的解耦,你可能有一個(gè)疑問?你這一會(huì)組件一會(huì)模塊是不是有點(diǎn)懵,其實(shí)個(gè)人認(rèn)為組件化和模塊化只不過是概念不同,模塊化包含組件化,組件化是模塊化開發(fā)中不可缺少的,二者只不過是劃分方式不同,實(shí)現(xiàn)方面沒有太大區(qū)別。
2.2 組件或模塊的單獨(dú)運(yùn)行
組件或模塊的單獨(dú)運(yùn)行時(shí)組件化的又一亮點(diǎn)。還是以我們的demo為例,如果我們想單獨(dú)運(yùn)行module_home模塊改怎么辦?
首先你要知道一點(diǎn)組件變?yōu)橐粋€(gè)能獨(dú)立運(yùn)行的app要變動(dòng)那幾個(gè)地方?我個(gè)人認(rèn)為一般有三個(gè)地方需要變動(dòng):
- 組件或模塊的build.gradle 中的apply plugin: 'com.android.library' 變 apply plugin: 'com.android.application'
- 組件或模塊的build.gradle 中的defaultConfig配置中的applicationId 根據(jù)情況動(dòng)態(tài)變動(dòng),如果作為組件或模塊存在則不需要,反之亦然。
-
功能清單文件的變動(dòng),你想如果作為組件或模塊存在我們是不需要下面這些東西的:
image.png
ok,那我們就依次解決這三個(gè)問題:
對(duì)于是否要將模塊或組件單獨(dú)運(yùn)行,我們需要定義變量去控制,還是以demo為例,我們?cè)趐roject的gradle.properties文件中定義如下:

于是第一個(gè)問題解決了,我們只需要在模塊的build.gradle中頂部添加如下代碼:

于是第二個(gè)問題也解決了,我們只需要在defaultConfig中增加以下代碼即可:

這里在提個(gè)醒默認(rèn)情況下我們的applicationId值是我們的包名一致
于是第三個(gè)問題也解決了,我們還是在對(duì)應(yīng)的build.gradle文件中的android中增加以下代碼:

注意這個(gè)我們是要配置相關(guān)路徑的,如:src/main/runalone/AndroidManifest.xml
那我們?cè)谶@個(gè)位置就有相關(guān)文件夾,大家請(qǐng)看:

里面的內(nèi)容如下(就是平常的啦):
單獨(dú)運(yùn)行的清單文件:

作為組件或模塊的清單文件:

既然實(shí)現(xiàn)了組件的單獨(dú)運(yùn)行那么單獨(dú)調(diào)試也就解決了。
2.3 數(shù)據(jù)的傳遞與ui跳轉(zhuǎn)跳轉(zhuǎn)
ui的跳轉(zhuǎn)我們主要借助于阿里的Aroute,數(shù)據(jù)的傳遞可以Aroute和EventBus結(jié)合使用,效果更佳。
ARoute的介紹請(qǐng)參考我之前的文章Android-ARouter
還是以項(xiàng)目為例:
- 跳轉(zhuǎn)調(diào)用其它組件(這里主要是分享組件)
比如我們要從首頁模塊調(diào)用x5組件,那么請(qǐng)看相關(guān)代碼:
首頁相關(guān)代碼,再點(diǎn)擊跳轉(zhuǎn)X5按鈕后:
@OnClick(R2.id.homemodule_button)
public void onViewClicked() {
ARouter.getInstance().build(COMPONENT_X5).withString("url","https://www.baidu.com/").navigation();
}
x5分享組件相關(guān)代碼:

數(shù)據(jù)的回調(diào)可以結(jié)合EvnetBus,這里就不詳細(xì)說了。
- 利用IProvider跨moudle的服務(wù)調(diào)用,主要用于非Activity,因?yàn)槲以趯慸emo中發(fā)現(xiàn)在Activity中不好使。直接上代碼了:
應(yīng)用場景是我們zhongmodule_zixun模塊中的Fragment要調(diào)用X5組件中的X5Test類中需要用到的方法:
-
首先在common_lib中定義X5CompService接口繼承IProvider接口,如下:
image.png
2.x5組件中的X5Test類實(shí)現(xiàn)X5CompService接口

3.在zhongmodule_zixun模塊中的Fragment中獲取X5CompService實(shí)例進(jìn)行調(diào)用,進(jìn)行了簡單的toast

注意:既然是zhongmodule_zixun模塊中的Fragment中獲取X5CompService的實(shí)現(xiàn)類進(jìn)行調(diào)用,那么它就要把X5組件作為依賴:

2.4 生命周期管理
生命周期的管理我們主要是通過common_lib中的baseApplication(注:其他module中的Application都要繼承baseApplication,確保唯一性)來管理,這里我直接貼出BaseApplication中的所有代碼:
public class BaseApplication extends Application {
//是否開啟調(diào)試
private boolean isDebug =true;
//全局唯一的context
private static BaseApplication application;
//Activity管理器
private ActivityManage activityManage;
@Override
public void onCreate() {
super.onCreate();
application = this;
activityManage = new ActivityManage();
//初始化路由
initRouter();
}
/**
* 程序終止的時(shí)候執(zhí)行
*/
@Override
public void onTerminate() {
super.onTerminate();
exitApp();
}
/**
* 退出應(yīng)用
*/
public void exitApp() {
activityManage.finishAll();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
/**
* 初始化路由
*/
private void initRouter() {
//必須在初始化之前寫入這兩行
if (isDebug) {
//打印日志
ARouter.openLog();
//開始調(diào)試
ARouter.openDebug();
}
//ARouter的實(shí)例化
ARouter.init(this);
}
/**
* 獲取全局唯一上下文
*
* @return BaseApplication
*/
public static BaseApplication getApplication() {
return application;
}
/**
* 返回Activity管理器
*/
public ActivityManage getActivityManage() {
if (activityManage == null) {
activityManage = new ActivityManage();
}
return activityManage;
}
}
ActivityManage使我寫的Activity管理工具類,詳情請(qǐng)看相關(guān)代碼。
2.5 其他
- 混淆
混淆我們是放在各個(gè)Module還是app的proguard-rules.pro文件中,答案是app的proguard-rules.pro文件中,因?yàn)槿绻诮M件中進(jìn)行混淆,一旦代碼出現(xiàn)了bug,這個(gè)時(shí)候就很難根據(jù)日志去追蹤bug產(chǎn)生的原因,而且不同組件分別進(jìn)行混淆非常不方便維護(hù)和修改。 -
使用ButterKnife遇到的問題
當(dāng)我將組件單獨(dú)運(yùn)行時(shí)是沒有問題的,可如果作為module時(shí)就會(huì)出現(xiàn)什么需要常量等問題,解決辦法是:
image.png
將原本的R.id.homemodule_button改為R2.id.homemodule_button,如果找不到R2,請(qǐng)確保你project的build.gradle中:

但依賴的butterknife版本是8.4.0:

每一個(gè)用到的模塊中的build.gradle中都需要配置(默認(rèn)模塊都依賴common_lib,否則單獨(dú)添加8.4.0依賴):
apply plugin: 'com.jakewharton.butterknife' 以及

就這么多吧,詳情請(qǐng)看組件化Demo.



