參考資料:
http://www.lxweimin.com/p/60c1b9ddd8ab
上一篇我們學習了ARouter,講到ARouter是組件化開發的基礎,那現在讓我們開始組件化開發吧。
1.組件化,模塊化概念
對于組件化的開發,首先要了解模塊化及組件化的概念,這正是是好多小伙伴模糊的,所以我們有必要說明一下。
1.1 組件
組件的英文單詞是component,意思是組件、部件、元件。在App工程上,件是構成業務或者功能模塊的基本單位也就是組件不能被繼續拆分,原則上,組件與組件之間互不依賴。比如我們的圖片上傳功能,可以叫圖片上傳組件,但不能叫圖片上傳模塊,而且組件具有可替換性和重復利用性,可替換性指比如我們的地圖定位組件可以用百度的,也可以用高德的。重復性是指我的地位功能可能在首頁被調用,在其他頁面也要被調用。
1.2 模塊
模塊的英文單詞是Module,由多個組件構成。也就是從粒度上來看,模塊要比組件大,也就是模塊包含組件。舉個例子:以安居客為例,安居客app內有二手房和新房相關功能, 這個二手房和新房就屬于模塊,但這二手房和新房模塊都用到了分享房源的功能,而這個分享功能就屬于組件了。
1.3 組件和模塊的關系
我們以上圖為例簡單說一下,首頁界面的天貓,聚劃算,餓了么都屬于模塊同時也是一個業務,我們可以叫做業務模塊,而我們繼續點擊天貓進入天貓超市,里面的分享功能,支付所用到的sdk等都算組件。那我們總結一下:模塊化和組件化只不過是我們根據項目的需求,定義不同,一個工程可以由多個模塊組成,每個模塊可以由多個組件構成,模塊可以單獨存在,正是多個模塊構成了整個項目。不論是模塊化還是組件化,它們的目的都是把項目解耦便于代碼的管理。高內聚、低耦合使開發人員分工明確,提高開發效率。
1.4 業務
我們上面說了模塊也可以叫做業務模塊,那么什么是業務呢,我們還是以上圖為例:在淘寶的首頁天貓,聚劃算,充值中心等這些看起來完全不同的業務我們稱之為Business業務。而天貓點擊進入會有搜索業務,預約功能,簽到等功能,聚劃算點擊進入也會有搜索業務,預約功能,簽到等功能,像這種業務我們稱之為基礎業務。我們來張圖說明一下Business業務和基礎業務及組件之間的關系:
圖中Business業務公用基礎業務,而基礎業務的實現可能依賴于某組件。
1.4 Library
我們剛才說了組件,比如上圖提到的圖片上傳組件,網絡組件,還有分享組件等,那這里要介紹另外一個概念:Library。你比如我們的分享組件用的是友盟分享sdk,我們的網絡組件依賴的是okhttp庫,這些依賴的三方庫都稱之為Library。
2.組件化實踐
參考資料:
https://mp.weixin.qq.com/s/-gC8JpmmCZWzcOsH5ZzLtQ
https://mp.weixin.qq.com/s/8_8gGpkpO2QFNkWgSRBwIg
我準備從以下幾個方面來介紹組件化:
- 代碼解耦
- 組件或模塊的單獨運行
- 數據的傳遞與ui跳轉
- 生命周期管理
2.1代碼解耦
ok,我們先看看我們的demo實現效果,就先簡單上張圖吧,不整gif圖片了:
你看到的第一眼可能覺得簡單,沒什么大不了,但是這不同于我們平時的實現方法,平時我們的首頁,資訊,我的都是在app中實現,但今天我們的目錄結構是這樣的:
我先來說一下各個目錄的含義:
- app
app模塊是我們的殼工程,平時我們最核心的代碼都要往這里寫,但今天不一樣,app模塊的職能改變了,它最主要的目的是對其他模塊進行整合,確保app能正常運行,里面只有一些簡單的代碼。 -
commonlib
commonlib 是一個依賴的Library,為什么說她是library呢?因為它的職能是把整個project所用到的library,公用的lib都放到這里,供其它模塊引用,就不需要每個模塊都寫一遍了。以demo為例:
image.png
注意:采用api代替implementation代替的目的就是為了讓其他模塊能夠引用到
- module_home,module_zixun,module_user,
這三個模塊是整個project最核心的地方, 對應我們上面效果圖中的首頁,資訊,我的三個tab。
這樣做的好處顯而易見 :張三開發首頁,李四開發資訊,王五開發我的 互不影響,提高開發效率 ,這也是組件化的優勢 - x5webview
x5webview是作為組件存在的,是我基于騰訊TBS瀏覽服務封裝的(類似webview作用),和分享組件,網路請求組件是一個級別,為了其他模塊的調用。
這里就基本實現了代碼的解耦,你可能有一個疑問?你這一會組件一會模塊是不是有點懵,其實個人認為組件化和模塊化只不過是概念不同,模塊化包含組件化,組件化是模塊化開發中不可缺少的,二者只不過是劃分方式不同,實現方面沒有太大區別。
2.2 組件或模塊的單獨運行
組件或模塊的單獨運行時組件化的又一亮點。還是以我們的demo為例,如果我們想單獨運行module_home模塊改怎么辦?
首先你要知道一點組件變為一個能獨立運行的app要變動那幾個地方?我個人認為一般有三個地方需要變動:
- 組件或模塊的build.gradle 中的apply plugin: 'com.android.library' 變 apply plugin: 'com.android.application'
- 組件或模塊的build.gradle 中的defaultConfig配置中的applicationId 根據情況動態變動,如果作為組件或模塊存在則不需要,反之亦然。
-
功能清單文件的變動,你想如果作為組件或模塊存在我們是不需要下面這些東西的:
image.png
ok,那我們就依次解決這三個問題:
對于是否要將模塊或組件單獨運行,我們需要定義變量去控制,還是以demo為例,我們在project的gradle.properties文件中定義如下:
于是第一個問題解決了,我們只需要在模塊的build.gradle中頂部添加如下代碼:
于是第二個問題也解決了,我們只需要在defaultConfig中增加以下代碼即可:
這里在提個醒默認情況下我們的applicationId值是我們的包名一致
于是第三個問題也解決了,我們還是在對應的build.gradle文件中的android中增加以下代碼:
注意這個我們是要配置相關路徑的,如:src/main/runalone/AndroidManifest.xml
那我們在這個位置就有相關文件夾,大家請看:
里面的內容如下(就是平常的啦):
單獨運行的清單文件:
作為組件或模塊的清單文件:
既然實現了組件的單獨運行那么單獨調試也就解決了。
2.3 數據的傳遞與ui跳轉跳轉
ui的跳轉我們主要借助于阿里的Aroute,數據的傳遞可以Aroute和EventBus結合使用,效果更佳。
ARoute的介紹請參考我之前的文章Android-ARouter
還是以項目為例:
- 跳轉調用其它組件(這里主要是分享組件)
比如我們要從首頁模塊調用x5組件,那么請看相關代碼:
首頁相關代碼,再點擊跳轉X5按鈕后:
@OnClick(R2.id.homemodule_button)
public void onViewClicked() {
ARouter.getInstance().build(COMPONENT_X5).withString("url","https://www.baidu.com/").navigation();
}
x5分享組件相關代碼:
數據的回調可以結合EvnetBus,這里就不詳細說了。
- 利用IProvider跨moudle的服務調用,主要用于非Activity,因為我在寫demo中發現在Activity中不好使。直接上代碼了:
應用場景是我們zhongmodule_zixun模塊中的Fragment要調用X5組件中的X5Test類中需要用到的方法:
-
首先在common_lib中定義X5CompService接口繼承IProvider接口,如下:
image.png
2.x5組件中的X5Test類實現X5CompService接口
3.在zhongmodule_zixun模塊中的Fragment中獲取X5CompService實例進行調用,進行了簡單的toast
注意:既然是zhongmodule_zixun模塊中的Fragment中獲取X5CompService的實現類進行調用,那么它就要把X5組件作為依賴:
2.4 生命周期管理
生命周期的管理我們主要是通過common_lib中的baseApplication(注:其他module中的Application都要繼承baseApplication,確保唯一性)來管理,這里我直接貼出BaseApplication中的所有代碼:
public class BaseApplication extends Application {
//是否開啟調試
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();
}
/**
* 程序終止的時候執行
*/
@Override
public void onTerminate() {
super.onTerminate();
exitApp();
}
/**
* 退出應用
*/
public void exitApp() {
activityManage.finishAll();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
/**
* 初始化路由
*/
private void initRouter() {
//必須在初始化之前寫入這兩行
if (isDebug) {
//打印日志
ARouter.openLog();
//開始調試
ARouter.openDebug();
}
//ARouter的實例化
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管理工具類,詳情請看相關代碼。
2.5 其他
- 混淆
混淆我們是放在各個Module還是app的proguard-rules.pro文件中,答案是app的proguard-rules.pro文件中,因為如果在組件中進行混淆,一旦代碼出現了bug,這個時候就很難根據日志去追蹤bug產生的原因,而且不同組件分別進行混淆非常不方便維護和修改。 -
使用ButterKnife遇到的問題
當我將組件單獨運行時是沒有問題的,可如果作為module時就會出現什么需要常量等問題,解決辦法是:
image.png
將原本的R.id.homemodule_button改為R2.id.homemodule_button,如果找不到R2,請確保你project的build.gradle中:
但依賴的butterknife版本是8.4.0:
每一個用到的模塊中的build.gradle中都需要配置(默認模塊都依賴common_lib,否則單獨添加8.4.0依賴):
apply plugin: 'com.jakewharton.butterknife' 以及
就這么多吧,詳情請看組件化Demo.