在組件化之前的一種業(yè)務(wù)業(yè)務(wù)劃分架構(gòu)是一種單一分層的結(jié)構(gòu)镐依,整個APP是一個Module喊式,不同的業(yè)務(wù)拆分在不同的包下:
- 不管分包做的多好僻他,隨著項目的增大笆包,項目會失去層次感环揽,導(dǎo)致接受項目時會比較吃力。(臃腫)
- 包名約束是一種弱約束庵佣,一不小心就會導(dǎo)致不同的包名之間相互引用歉胶,導(dǎo)致包名之間耦合度高。(耦合度高)
- 多人聯(lián)合開發(fā)中巴粪,在版本管理中很容易出現(xiàn)代碼沖突和代碼覆蓋的問題通今。(版本管理困難)
組件化是一個化繁為簡的過程,將多個功能模塊進(jìn)行拆分肛根、重組的過程辫塌。即將APP按照業(yè)務(wù)劃分為不同的模塊,最后在打包為完整的APP時再整合為一起派哲。
如上圖所示:可分為APP殼工程臼氨、業(yè)務(wù)組件層、功能組件層和基礎(chǔ)庫層芭届。
APP殼工程負(fù)責(zé)管理各個業(yè)務(wù)組件和打包APK储矩,沒有具體的業(yè)務(wù)功能。
業(yè)務(wù)組件層是根據(jù)不同的業(yè)務(wù)拆分的不同的業(yè)務(wù)組件褂乍。
功能組件層是為上層提供基礎(chǔ)的功能服務(wù)持隧。
基礎(chǔ)庫中包含了各種開源庫以及和業(yè)務(wù)無關(guān)的各種自研工具庫。
比如可以新建項目如下:
切換到 project視圖逃片,與app同目錄舆蝴,建立business文件夾,然后在其中建立兩個Android Library题诵,分比為food和waimai洁仗。
新建config.gradle文件,在其中定義變量:
ext {
//標(biāo)識是以組件化模式運(yùn)行還是集成化模式運(yùn)行性锭,
// 如果是true赠潦,以集成化方式運(yùn)行,如果是false草冈,以組件化方式運(yùn)行
isModule = false
}
isModule用來標(biāo)識food是以Android 項目運(yùn)行還是作為一個普通的Android Module她奥。
在project 的build.gradle文件中引入該文件:
apply from : "config.gradle"
分別修改app瓮增、food和waimai的build.gradle文件:
app的build.gradle文件:
///根據(jù)變量的取值來決定運(yùn)行方式
if(rootProject.ext.isModule){
apply plugin: 'com.android.library'
}else{
apply plugin: 'com.android.application'
}
....
defaultConfig {
if(!rootProject.ext.isModule){
applicationId "com.example.zujianhuapro"
}
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
....
food的build.gradle文件:
if (rootProject.ext.isModule) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
...
defaultConfig {
if (rootProject.ext.isModule) {
applicationId "com.example.food"
}
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
...
waimai的同上。
可自行改變isModule 的值查看效果哩俭。
為什么要使用組件化
- 各個組件專注自身功能的實現(xiàn)绷跑,模塊中代碼高度內(nèi)聚,只負(fù)責(zé)一項任務(wù)凡资,也就是常說的單一職責(zé)原則砸捏。
- 各業(yè)務(wù)研發(fā)可以互不干擾,提升協(xié)作效率隙赁。
- 業(yè)務(wù)組件可以進(jìn)行插拔垦藏,靈活多變。
- 業(yè)務(wù)組件之間將不再直接應(yīng)用和依賴伞访,各個業(yè)務(wù)模塊組件更加獨(dú)立掂骏,降低耦合。
- 加快編譯速度厚掷,提高開發(fā)效率弟灼。
最簡化最核心的就是動態(tài)的切換library和application。
組件化最需要解決的問題--頁面跳轉(zhuǎn)
路由跳轉(zhuǎn)一般可采用隱式跳轉(zhuǎn)和顯示跳轉(zhuǎn)兩種方式冒黑,但是在組件化結(jié)構(gòu)中袜爪,因為不同組件不會相互依賴,所以無法采用顯示跳轉(zhuǎn)的方式薛闪,只可以采用隱式跳轉(zhuǎn),但是隱式跳轉(zhuǎn)也存在問題俺陋,使用隱式跳轉(zhuǎn)時豁延,必須先在生命文件中用intent-filter來限定隱式Action的啟動,其他的Module才可以使用隱式的Action跳轉(zhuǎn)到響應(yīng)的Activity腊状。但是在組件化中使用這種方式并不友好诱咏,不僅多人開發(fā)困難,還存在安全隱患缴挖。
而路由組件正是為此而存在的袋狞。
首先需要得到目標(biāo)的頁面地址,然后在路由表中尋址映屋,找到目標(biāo)頁后苟鸯,得到Activity的Class對象,然后啟動目標(biāo)頁棚点。
可以看出早处,路由組件的關(guān)鍵在于路由表,而路由表就是一系列特定的URL和特定的Activity之間的映射集合瘫析,是一個Map結(jié)構(gòu)砌梆,key是一個字符串即URL默责,value是Activity的Class對象。
路由表需要保證只有一份咸包,所以需要使用單例模式桃序,而路由框架需要被所有的組件依賴到。
在項目中新建路由Module烂瘫,如下所示
在app媒熊、food和waimai中引入改Module,
implementation project(':router-api')
在路由Module中新建如下類
/**
* 項目名稱 zujianhuaPro
* 創(chuàng)建人 xiaojinli
* 創(chuàng)建時間 2020/8/29 9:31 AM
* 路由表忱反,需要被所有的組件依賴泛释,所以需要使用單例模式
**/
public class Router {
private static Router mRouter;
private static Context mContext;
//路由表
private static Map<String,Class<? extends Activity>> routers = new HashMap<>();
public void init(Application application){
mContext = application;
}
public static Router getInstance(){
if(mRouter == null){
synchronized (Router.class){
if(mRouter == null){
mRouter = new Router();
}
}
}
return mRouter;
}
//向路由表中注冊一個Activity
public void register(String path,Class<? extends Activity> cls){
routers.put(path,cls);
}
//啟動路由表中的一個Activity
public void startActivity(String path){
Class<? extends Activity> cls = routers.get(path);
if(cls == null){
return;
}
Intent intent = new Intent(mContext,cls);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
}
關(guān)鍵方法有三個,一個是實現(xiàn)單例模式方法温算,因為需要被所有的業(yè)務(wù)Module引用怜校,所以需要保持唯一性。二是向路由表中注冊一個頁面的register方法注竿,三是啟動一個路由表中頁面的方法茄茁。
使用時如下所示:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Router.getInstance().init(getApplication());
Router.getInstance().register("/food/FoodActivity", FoodActivity.class);
Router.getInstance().register("/waimai/WaiMaiActivity", WaiMaiActivity.class);
}
public void jumpToFood(View view) {
Router.getInstance().startActivity("/food/FoodActivity");
}
}
在殼Activity中初始化路由組件以及注冊頁面信息,就可實現(xiàn)跳轉(zhuǎn)巩割。
在food組件中跳轉(zhuǎn)到waimai組件可使用如下方式
Router.getInstance().startActivity("/waimai/WaiMaiActivity");
在waimai組件中跳轉(zhuǎn)到food組件可使用如下方式
Router.getInstance().startActivity("/food/FoodActivity");
但是這種方式存在一個明顯的缺點裙顽,即我們需要自己在路由表中注冊所有的頁面,不易維護(hù)宣谈。
下面我們會通過APT來進(jìn)行優(yōu)化愈犹。
什么是APT
APT是注解處理器,是javac處理注解的一種工具闻丑,他用來在編譯時掃描和處理注解漩怎,簡單來說就是在編譯期間,通過注解采集信息嗦嗡,生成.java文件勋锤,減少重復(fù)代碼的編寫。很多框架都是用了APT侥祭,包括Butterknife和Glide等叁执。
通過上面的路由表可以看出,我們需要的關(guān)鍵信息是Activity的一個字符串參數(shù)以及Activity的Class對象矮冬,我們都可以通過注解來采集到谈宛。
創(chuàng)建JavaLib,依賴下面的依賴監(jiān)控到編譯期
//構(gòu)建 -----》》》》----【編譯時期】------》》》打包-------》》》安裝
// As-3.4.1 + gradle-5.1.1-all + auto-service:1.0-rc4
//在編譯器可以通過以下依賴讓我們自定義的AbstractProcessor工作胎署,即可以通過這兩個服務(wù)讓我們在編譯器做一些事情
compileOnly'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'
首先我們需要創(chuàng)建一個JavaLib入挣,來創(chuàng)建一個注解
@Target(ElementType.TYPE) //作用在類上
@Retention(RetentionPolicy.CLASS) //保留到CLASS,因為我們在編譯器需要硝拧,所以需要保留到Class
public @interface Route {
String value(); //詳細(xì)路徑名径筏,比如/main/MainActivity葛假,接收一個字符串參數(shù)
String group() default "";//路由組名,比如main
}
其次我們需要再定義一個JavaLib滋恬,來創(chuàng)建一個處理注解的類聊训,需要繼承AbstractProcessor,默認(rèn)實現(xiàn)process方法恢氯。需要注意添加如下注解:
//自動注冊带斑,以便該類可以在編譯器干活
@AutoService(Processor.class)
//允許支持的注解類型,讓注解處理器處理
@SupportedAnnotationTypes(ProcessorConfig.ROUTER_PACKAGE)
//指定JDK編譯版本勋拟,必須寫
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//注解處理器接收的參數(shù)
@SupportedOptions({ProcessorConfig.OPTIONS,ProcessorConfig.APT_PACKAGE})
此外我們需要在其他的Module中引入該處理器Module勋磕,注意需要使用annotationProcessor關(guān)鍵字
annotationProcessor project(path: ':route_compile')
待一切都準(zhǔn)備完成之后,點擊錘子符號即Make Project按鈕敢靡,如果一切都沒有問題挂滓,處理器文件夾下會出現(xiàn)build文件夾,在其中可以找到如下類啸胧,意味著注解處理器配置成功赶站。
此時當(dāng)我們項目進(jìn)入編譯器時,會在APP工程中掃描引入該注解處理器module的組件中的類纺念,查看是否用使用了Route注解贝椿。下面就是在我們自定義的注解處理器內(nèi)添加相應(yīng) 的邏輯生成需要的java文件。
完整的項目地址: