
MVC、MVP和MVVM是常見的三種架構(gòu)設(shè)計(jì)模式,當(dāng)前MVP和MVVM的使用相對比較廣泛,當(dāng)然MVC也并沒有過時(shí)之說。而所謂的組件化就是指將應(yīng)用根據(jù)業(yè)務(wù)需求劃分成各個(gè)模塊來進(jìn)行開發(fā),每個(gè)模塊又可以編譯成獨(dú)立的APP進(jìn)行開發(fā)。理論上講,組件化和前面三種架構(gòu)設(shè)計(jì)不是一個(gè)層次的。它們之間的關(guān)系是,組件化的各個(gè)組件可以使用前面三種架構(gòu)設(shè)計(jì)。我們只有了解了這些架構(gòu)設(shè)計(jì)的特點(diǎn)之后,才能在進(jìn)行開發(fā)的時(shí)候選擇適合自己項(xiàng)目的架構(gòu)模式,這也是本文的目的。
1、MVC
MVC (Model-View-Controller, 模型-視圖-控制器),標(biāo)準(zhǔn)的MVC是這個(gè)樣子的:
- 模型層 (Model):業(yè)務(wù)邏輯對應(yīng)的數(shù)據(jù)模型,無View無關(guān),而與業(yè)務(wù)相關(guān);
- 視圖層 (View):一般使用XML或者Java對界面進(jìn)行描述;
- 控制層 (Controllor):在Android中通常指Activity和Fragment,或者由其控制的業(yè)務(wù)類。
Activity并非標(biāo)準(zhǔn)的Controller,它一方面用來控制了布局,另一方面還要在Activity中寫業(yè)務(wù)代碼,造成了Activity既像View又像Controller。
在Android開發(fā)中,就是指直接使用Activity并在其中寫業(yè)務(wù)邏輯的開發(fā)方式。顯然,一方面Activity本身就是一個(gè)視圖,另一方面又要負(fù)責(zé)處理業(yè)務(wù)邏輯,因此邏輯會(huì)比較混亂。
這種開發(fā)方式不太適合Android開發(fā)。
2、MVP
2.1 概念梳理
MVP (Model-View-Presenter) 是MVC的演化版本,幾個(gè)主要部分如下:
- 模型層 (Model):主要提供數(shù)據(jù)存取功能。
- 視圖層 (View):處理用戶事件和視圖。在Android中,可能是指Activity、Fragment或者View。
- 展示層 (Presenter):負(fù)責(zé)通過Model存取書數(shù)據(jù),連接View和Model,從Model中取出數(shù)據(jù)交給View。
所以,對于MVP的架構(gòu)設(shè)計(jì),我們有以下幾點(diǎn)需要說明:
- 這里的Model是用來存取數(shù)據(jù)的,也就是用來從指定的數(shù)據(jù)源中獲取數(shù)據(jù),不要將其理解成MVC中的Model。在MVC中Model是數(shù)據(jù)模型,在MVP中,我們用Bean來表示數(shù)據(jù)模型。
- Model和View不會(huì)直接發(fā)生關(guān)系,它們需要通過Presenter來進(jìn)行交互。在實(shí)際的開發(fā)中,我們可以用接口來定義一些規(guī)范,然后讓我們的View和Model實(shí)現(xiàn)它們,并借助Presenter進(jìn)行交互即可。
為了說明MVP設(shè)計(jì)模式,我們給出一個(gè)示例程序。你可以在Github中獲取到它的源代碼。
2.2 示例程序
在該示例中,我們使用了:
- 開眼視頻的API作為數(shù)據(jù)源;
- Retrofit進(jìn)行數(shù)據(jù)訪問;
- 使用ARouter進(jìn)行路由;
- 使用MVP設(shè)計(jì)模式作為程序架構(gòu)。
下面是該模塊的基本的包結(jié)構(gòu):
這里核心的代碼是MVP部分。
這里我們首先定義了MVP模式中的最頂層的View和Presenter,在這里分別是BaseView和BasePresenter,它們在該項(xiàng)目中是兩個(gè)空的接口,在一些項(xiàng)目中,我們可以根據(jù)自己的需求在這兩個(gè)接口中添加自己需要的方法。
然后,我們定義了HomeContract。它是一個(gè)抽象的接口,相當(dāng)于一層協(xié)議,用來規(guī)定指定的功能的View和Presenter分別應(yīng)該具有哪些方法。通常,對于不同的功能,我們需要分別實(shí)現(xiàn)一個(gè)MVP,每個(gè)MVP都會(huì)又一個(gè)對應(yīng)的Contract。筆者認(rèn)為它的好處在于,將指定的View和Presenter的接口定義在一個(gè)接口中,更加集中。它們各自需要實(shí)現(xiàn)的方法也一目了然地展現(xiàn)在了我們面前。
這里根據(jù)我們的業(yè)務(wù)場景,該接口的定義如下:
public interface HomeContract {
interface IView extends BaseView {
void setFirstPage(List<HomeBean.IssueList.ItemList> itemLists);
void setNextPage(List<HomeBean.IssueList.ItemList> itemLists);
void onError(String msg);
}
interface IPresenter extends BasePresenter {
void requestFirstPage();
void requestNextPage();
}
}
HomeContract用來規(guī)定View和Presenter應(yīng)該具有的操作,在這里它用來指定主頁的View和Presenter的方法。從上面我們也可以看出,這里的IView和IPresenter分別實(shí)現(xiàn)了BaseView和BasePresenter。
上面,我們定義了V和P的規(guī)范,MVP中還有一項(xiàng)Model,它用來從網(wǎng)絡(luò)中獲取數(shù)據(jù)。這里我們省去網(wǎng)絡(luò)相關(guān)的具體的代碼,你只需要知道APIRetrofit.getEyepetizerService()是用來獲取Retrofit對應(yīng)的Service,而getMoreHomeData()和getFirstHomeData()是用來從指定的接口中獲取數(shù)據(jù)就行。下面是HomeModel的定義:
public class HomeModel {
public Observable<HomeBean> getFirstHomeData() {
return APIRetrofit.getEyepetizerService().getFirstHomeData(System.currentTimeMillis());
}
public Observable<HomeBean> getMoreHomeData(String url) {
return APIRetrofit.getEyepetizerService().getMoreHomeData(url);
}
}
OK,上面我們已經(jīng)完成了Model的定義和View及Presenter的規(guī)范的定義。下面,我們就需要具體去實(shí)現(xiàn)View和Presenter。
首先是Presenter,下面是我們的HomePresenter的定義。在下面的代碼中,為了更加清晰地展示其中的邏輯,我刪減了一部分無關(guān)代碼:
public class HomePresenter implements HomeContract.IPresenter {
private HomeContract.IView view;
private HomeModel homeModel;
private String nextPageUrl;
// 傳入View并實(shí)例化Model
public HomePresenter(HomeContract.IView view) {
this.view = view;
homeModel = new HomeModel();
}
// 使用Model請求數(shù)據(jù),并在得到請求結(jié)果的時(shí)候調(diào)用View的方法進(jìn)行回調(diào)
@Override
public void requestFirstPage() {
Disposable disposable = homeModel.getFirstHomeData()
// ....
.subscribe(itemLists -> { view.setFirstPage(itemLists); },
throwable -> { view.onError(throwable.toString()); });
}
// 使用Model請求數(shù)據(jù),并在得到請求結(jié)果的時(shí)候調(diào)用View的方法進(jìn)行回調(diào)
@Override
public void requestNextPage() {
Disposable disposable = homeModel.getMoreHomeData(nextPageUrl)
// ....
.subscribe(itemLists -> { view.setFirstPage(itemLists); },
throwable -> { view.onError(throwable.toString()); });
}
}
從上面我們可以看出,在Presenter需要將View和Model建立聯(lián)系。我們需要在初始化的時(shí)候傳入View,并實(shí)例化一個(gè)Model。Presenter通過Model獲取數(shù)據(jù),并在拿到數(shù)據(jù)的時(shí)候,通過View的方法通知給View層。
然后,就是我們的View層的代碼,同樣,我對代碼做了刪減:
@Route(path = BaseConstants.EYEPETIZER_MENU)
public class HomeActivity extends CommonActivity<ActivityEyepetizerMenuBinding> implements HomeContract.IView {
// 實(shí)例化Presenter
private HomeContract.IPresenter presenter;
{
presenter = new HomePresenter(this);
}
@Override
protected int getLayoutResId() {
return R.layout.activity_eyepetizer_menu;
}
@Override
protected void doCreateView(Bundle savedInstanceState) {
// ...
// 使用Presenter請求數(shù)據(jù)
presenter.requestFirstPage();
loading = true;
}
private void configList() {
// ...
getBinding().rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// 請求下一頁的數(shù)據(jù)
presenter.requestNextPage();
}
}
});
}
// 當(dāng)請求到結(jié)果的時(shí)候在頁面上做處理,展示到頁面上
@Override
public void setFirstPage(List<HomeBean.IssueList.ItemList> itemLists) {
loading = false;
homeAdapter.addData(itemLists);
}
// 當(dāng)請求到結(jié)果的時(shí)候在頁面上做處理,展示到頁面上
@Override
public void setNextPage(List<HomeBean.IssueList.ItemList> itemLists) {
loading = false;
homeAdapter.addData(itemLists);
}
@Override
public void onError(String msg) {
ToastUtils.makeToast(msg);
}
// ...
}
從上面的代碼中我們可以看出實(shí)際在View中也要維護(hù)一個(gè)Presenter的實(shí)例。
當(dāng)需要請求數(shù)據(jù)的時(shí)候會(huì)使用該實(shí)例的方法來請求數(shù)據(jù),所以,在開發(fā)的時(shí)候,我們需要根據(jù)請求數(shù)據(jù)的情況,在Presenter中定義接口方法。
實(shí)際上,MVP的原理就是View通過Presenter獲取數(shù)據(jù),獲取到數(shù)據(jù)之后再回調(diào)View的方法來展示數(shù)據(jù)。
2.3 MVC 和 MVP 的區(qū)別
- MVC 中是允許 Model 和 View 進(jìn)行交互的,而MVP中,Model 與 View 之間的交互由Presenter完成;
- MVP 模式就是將 P 定義成一個(gè)接口,然后在每個(gè)觸發(fā)的事件中調(diào)用接口的方法來處理,也就是將邏輯放進(jìn)了 P 中,需要執(zhí)行某些操作的時(shí)候調(diào)用 P 的方法就行了。
2.4 MVP的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 降低耦合度,實(shí)現(xiàn)了 Model 和 View 真正的完全分離,可以修改 View 而不影響 Modle;
- 模塊職責(zé)劃分明顯,層次清晰;
- 隱藏?cái)?shù)據(jù);
- Presenter 可以復(fù)用,一個(gè) Presenter 可以用于多個(gè) View,而不需要更改 Presenter 的邏輯;
- 利于測試驅(qū)動(dòng)開發(fā),以前的Android開發(fā)是難以進(jìn)行單元測試的;
- View 可以進(jìn)行組件化,在MVP當(dāng)中,View 不依賴 Model。
缺點(diǎn):
- Presenter 中除了應(yīng)用邏輯以外,還有大量的 View->Model,Model->View 的手動(dòng)同步邏輯,造成 Presenter 比較笨重,維護(hù)起來會(huì)比較困難;
- 由于對視圖的渲染放在了 Presenter 中,所以視圖和 Presenter 的交互會(huì)過于頻繁;
- 如果 Presenter 過多地渲染了視圖,往往會(huì)使得它與特定的視圖的聯(lián)系過于緊密,一旦視圖需要變更,那么Presenter也需要變更了。
3、MVVM (分手大師)
3.1 基礎(chǔ)概念
MVVM 是 Model-View-ViewModel 的簡寫。它本質(zhì)上就是 MVC 的改進(jìn)版。MVVM 就是將其中的 View 的狀態(tài)和行為抽象化,讓我們將視圖 UI 和業(yè)務(wù)邏輯分開。
- 模型層 (Model):負(fù)責(zé)從各種數(shù)據(jù)源中獲取數(shù)據(jù);
- 視圖層 (View):在 Android 中對應(yīng)于 Activity 和 Fragment,用于展示給用戶和處理用戶交互,會(huì)驅(qū)動(dòng) ViewModel 從 Model 中獲取數(shù)據(jù);
- ViewModel 層:用于將 Model 和 View 進(jìn)行關(guān)聯(lián),我們可以在 View 中通過 ViewModel 從 Model 中獲取數(shù)據(jù);當(dāng)獲取到了數(shù)據(jù)之后,會(huì)通過自動(dòng)綁定,比如 DataBinding,來將結(jié)果自動(dòng)刷新到界面上。
使用 Google 官方的 Android Architecture Components ,我們可以很容易地將 MVVM 應(yīng)用到我們的應(yīng)用中。下面,我們就使用它來展示一下 MVVM 的實(shí)際的應(yīng)用。你可以在Github中獲取到它的源代碼。
3.2 示例程序
在該項(xiàng)目中,我們使用了:
- 果殼網(wǎng)的 API 作為數(shù)據(jù)源;
- 使用 Retrofit 進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)訪問;
- 使用 ViewMdeol 作為整體的架構(gòu)設(shè)計(jì)。
該項(xiàng)目的包結(jié)構(gòu)如下圖所示:
這里的model.data下面的類是對應(yīng)于網(wǎng)絡(luò)的數(shù)據(jù)實(shí)體的,由JSON自動(dòng)生成,這里我們不進(jìn)行詳細(xì)描述。這里的model.repository下面的兩個(gè)類是用來從網(wǎng)絡(luò)中獲取數(shù)據(jù)信息的,我們也忽略它的定義。
上面就是我們的 Model 的定義,并沒有太多的內(nèi)容,基本與 MVP 一致。
下面的是 ViewModel 的代碼,我們選擇了其中的一個(gè)方法來進(jìn)行說明。當(dāng)我們定義 ViewModel 的時(shí)候,需要繼承 ViewModel 類。
public class GuokrViewModel extends ViewModel {
public LiveData<Resource<GuokrNews>> getGuokrNews(int offset, int limit) {
MutableLiveData<Resource<GuokrNews>> result = new MutableLiveData<>();
GuokrRetrofit.getGuokrService().getNews(offset, limit)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<GuokrNews>() {
@Override
public void onError(Throwable e) {
result.setValue(Resource.error(e.getMessage(), null));
}
@Override
public void onComplete() { }
@Override
public void onSubscribe(Disposable d) { }
@Override
public void onNext(GuokrNews guokrNews) {
result.setValue(Resource.success(guokrNews));
}
});
return result;
}
}
這里的 ViewModel 來自 android.arch.lifecycle.ViewModel,所以,為了使用它,我們還需要加入下面的依賴:
api "android.arch.lifecycle:runtime:$archVersion"
api "android.arch.lifecycle:extensions:$archVersion"
annotationProcessor "android.arch.lifecycle:compiler:$archVersion"
在 ViewModel 的定義中,我們直接使用 Retrofit 來從網(wǎng)絡(luò)中獲取數(shù)據(jù)。然后當(dāng)獲取到數(shù)據(jù)的時(shí)候,我們使用 LiveData 的方法把數(shù)據(jù)封裝成一個(gè)對象返回給 View 層。在 View 層,我們只需要調(diào)用該方法,并對返回的 LiveData 進(jìn)行"監(jiān)聽"即可。這里,我們將錯(cuò)誤信息和返回的數(shù)據(jù)信息進(jìn)行了封裝,并且封裝了一個(gè)代表當(dāng)前狀態(tài)的枚舉信息,你可以參考源代碼來詳細(xì)了解下這些內(nèi)容。
上面我們定義完了 Model 和 ViewModel,下面我們看下 View 層的定義,以及在 View 層中該如何使用 ViewModel。
@Route(path = BaseConstants.GUOKR_NEWS_LIST)
public class NewsListFragment extends CommonFragment<FragmentNewsListBinding> {
private GuokrViewModel guokrViewModel;
private int offset = 0;
private final int limit = 20;
private GuokrNewsAdapter adapter;
@Override
protected int getLayoutResId() {
return R.layout.fragment_news_list;
}
@Override
protected void doCreateView(Bundle savedInstanceState) {
// ...
guokrViewModel = ViewModelProviders.of(this).get(GuokrViewModel.class);
fetchNews();
}
private void fetchNews() {
guokrViewModel.getGuokrNews(offset, limit).observe(this, guokrNewsResource -> {
if (guokrNewsResource == null) {
return;
}
switch (guokrNewsResource.status) {
case FAILED:
ToastUtils.makeToast(guokrNewsResource.message);
break;
case SUCCESS:
adapter.addData(guokrNewsResource.data.getResult());
adapter.notifyDataSetChanged();
break;
}
});
}
}
以上就是我們的 View 層的定義,這里我們先使用了
這里的view.fragment包下面的類對應(yīng)于實(shí)際的頁面,這里我們 ViewModelProviders 的方法來獲取我們需要使用的 ViewModel,然后,我們直接使用該 ViewModel 的方法獲取數(shù)據(jù),并對返回的結(jié)果進(jìn)行“監(jiān)聽”即可。
以上就是 MVVM 的基本使用,當(dāng)然,這里我們并沒有使用 DataBinding 直接與返回的列表信息進(jìn)行綁定,它被更多的用在了整個(gè) Fragment 的布局中。
3.3 MVVM 的優(yōu)點(diǎn)和缺點(diǎn)
MVVM模式和MVC模式一樣,主要目的是分離視圖(View)和模型(Model),有幾大優(yōu)點(diǎn):
- 低耦合:視圖(View)可以獨(dú)立于Model變化和修改,一個(gè) ViewModel 可以綁定到不同的 View 上,當(dāng) View 變化的時(shí)候 Model 可以不變,當(dāng) Model 變化的時(shí)候 View 也可以不變。
- 可重用性:你可以把一些視圖邏輯放在一個(gè) ViewModel 里面,讓很多 view 重用這段視圖邏輯。
- 獨(dú)立開發(fā):開發(fā)人員可以專注于業(yè)務(wù)邏輯和數(shù)據(jù)的開發(fā)(ViewModel),設(shè)計(jì)人員可以專注于頁面設(shè)計(jì)。
- 可測試:界面素來是比較難于測試的,而現(xiàn)在測試可以針對 ViewModel 來寫。
4、組件化
4.1 基礎(chǔ)概念
所謂的組件化,通俗理解就是將一個(gè)工程分成各個(gè)模塊,各個(gè)模塊之間相互解耦,可以獨(dú)立開發(fā)并編譯成一個(gè)獨(dú)立的 APP 進(jìn)行調(diào)試,然后又可以將各個(gè)模塊組合起來整體構(gòu)成一個(gè)完整的 APP。它的好處是當(dāng)工程比較大的時(shí)候,便于各個(gè)開發(fā)者之間分工協(xié)作、同步開發(fā);被分割出來的模塊又可以在項(xiàng)目之間共享,從而達(dá)到復(fù)用的目的。組件化有諸多好處,尤其適用于比較大型的項(xiàng)目。
簡單了解了組件化之后,讓我們來看一下如何實(shí)現(xiàn)組件化開發(fā)。你可能之前聽說過組件化開發(fā),或者被其高大上的稱謂嚇到了,但它實(shí)際應(yīng)用起來并不復(fù)雜,至少借助了現(xiàn)成的框架之后并不復(fù)雜。這里我們先梳理一下,在應(yīng)用組件化的時(shí)候需要解決哪些問題:
- 如何分成各個(gè)模塊?我們可以根據(jù)業(yè)務(wù)來進(jìn)行拆分,對于比較大的功能模塊可以作為應(yīng)用的一個(gè)模塊來使用,但是也應(yīng)該注意,劃分出來的模塊不要過多,否則可能會(huì)降低編譯的速度并且增加維護(hù)的難度。
- 各個(gè)模塊之間如何進(jìn)行數(shù)據(jù)共享和數(shù)據(jù)通信?我們可以把需要共享的數(shù)據(jù)劃分成一個(gè)單獨(dú)的模塊來放置公共數(shù)據(jù)。各個(gè)模塊之間的數(shù)據(jù)通信,我們可以使用阿里的 ARouter 進(jìn)行頁面的跳轉(zhuǎn),使用封裝之后的 RxJava 作為 EventBus 進(jìn)行全局的數(shù)據(jù)通信。
- 如何將各個(gè)模塊打包成一個(gè)獨(dú)立的 APP 進(jìn)行調(diào)試?首先這個(gè)要建立在2的基礎(chǔ)上,然后,我們可以在各個(gè)模塊的 gradle 文件里面配置需要加載的 AndroidManifest.xml 文件,并可以為每個(gè)應(yīng)用配置一個(gè)獨(dú)立的 Application 和啟動(dòng)類。
- 如何防止資源名沖突問題?遵守命名規(guī)約就能規(guī)避資源名沖突問題。
- 如何解決 library 重復(fù)依賴以及 sdk 和依賴的第三方版本號控制問題?可以將各個(gè)模塊公用的依賴的版本配置到 settings.gradle 里面,并且可以建立一個(gè)公共的模塊來配置所需要的各種依賴。
Talk is cheap,下面讓我們動(dòng)手實(shí)踐來應(yīng)用組件化進(jìn)行開發(fā)。你可以在Github中獲取到它的源代碼。
4.2 組件化實(shí)踐
包結(jié)構(gòu)
首先,我們先來看整個(gè)應(yīng)用的包的結(jié)構(gòu)。如下圖所示,該模塊的劃分是根據(jù)各個(gè)模塊的功能來決定的。圖的右側(cè)白色的部分是各個(gè)模塊的文件路徑,我推薦使用這種方式,而不是將各個(gè)模塊放置在 app 下面,因?yàn)檫@樣看起來更加的清晰。為了達(dá)到這個(gè)目的,你只需要按照下面的方式在 settings.gralde 里面配置一下各個(gè)模塊的路徑即可。注意在實(shí)際應(yīng)用的時(shí)候模塊的路徑的關(guān)系,不要搞錯(cuò)了。
然后,我們介紹一下這里的 commons 模塊。它用來存放公共的資源和一些依賴,這里我們將兩者放在了一個(gè)模塊中以減少模塊的數(shù)量。下面是它的 gradle 的部分配置。這里我們使用了 api 來引入各個(gè)依賴,以便在其他的模塊中也能使用這些依賴。
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
// ...
// router
api 'com.alibaba:arouter-api:1.3.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
// walle
api 'com.meituan.android.walle:library:1.1.6'
// umeng
api 'com.umeng.sdk:common:1.5.3'
api 'com.umeng.sdk:analytics:7.5.3'
api files('libs/pldroid-player-1.5.0.jar')
}
路由
接著,我們來看一下路由框架的配置。這里,我們使用阿里的 ARouter 來進(jìn)行頁面之間的跳轉(zhuǎn),你可以在Github上面了解該框架的配置和使用方式。這里我們只講解一下在組件化開發(fā)的時(shí)候需要注意的地方。注意到 ARouter 是通過注解來進(jìn)行頁面配置的,并且它的注解是在編譯的時(shí)候進(jìn)行處理的。所以,我們需要引入arouter-compiler來使用它的編譯時(shí)處理功能。需要注意的地方是,我們只要在公共的模塊中加入arouter-api就可以使用ARouter的API了,但是需要在每個(gè)模塊中引入arouter-compiler才能使用編譯時(shí)注解。也就是說,我們需要在每個(gè)模塊中都加入arouter-compiler依賴。
模塊獨(dú)立
為了能夠?qū)⒏鱾€(gè)模塊編譯成一個(gè)獨(dú)立的 APP,我們需要在 Gradle 里面做一些配置。
首先,我們需要在gradle.properties定義一些布爾類型的變量用來判斷各個(gè)模塊是作為一個(gè) library 還是 application 進(jìn)行編譯。這里我的配置如下面的代碼所示。也就是,我為每個(gè)模塊都定義了這么一個(gè)布爾類型的變量,當(dāng)然,你也可以只定義一個(gè)變量,然后在各個(gè)模塊中使用同一個(gè)變量來進(jìn)行判斷。
isGuokrModuleApp=false
isLiveModuleApp=false
isLayoutModuleApp=false
isLibraryModuleApp=false
isEyepetizerModuleApp=false
然后,我們來看一下各個(gè)模塊中的 gradle 該如何配置,這里我們以開眼視頻的功能模塊作為例子來進(jìn)行講解。首先,一個(gè)模塊作為 library 還是 application 是根據(jù)引用的 plugin 來決定的,所以,我們要根據(jù)之前定義的布爾變量來決定使用的 plugin:
if (isEyepetizerModuleApp.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
假如我們要將某個(gè)模塊作為一個(gè)獨(dú)立的 APP,那么啟動(dòng)類你肯定需要配置。這就意味著你需要兩個(gè) AndroidManifest.xml 文件,一個(gè)用于 library 狀態(tài),一個(gè)用于 application 狀態(tài)。所以,我們可以在 main 目錄下面再定義一個(gè) AndroidManifest.xml,然后,我們在該配置文件中不只指定啟動(dòng)類,還使用我們定義的 Application。指定 Application 有時(shí)候是必須的,比如你需要在各個(gè)模塊里面初始化 ARouter 等等。這部分代碼就不給出了,可以參考源碼,這里我們給出一下在 Gradle 里面指定 AndroidManifest.xml 的方式。
如下所示,我們可以根據(jù)之前定義的布爾值來決定使用哪一個(gè)配置文件:
sourceSets {
main {
jniLibs.srcDirs = ['libs']
if (isEyepetizerModuleApp.toBoolean()) {
manifest.srcFile "src/main/debug/AndroidManifest.xml"
} else {
manifest.srcFile "src/main/AndroidManifest.xml"
}
}
}
此外,還需要注意的是,如果我們希望在每個(gè)模塊中都能應(yīng)用 DataBinding 和 Java 8 的一些特性,那么你需要在每個(gè)模塊里面都加入下面的配置:
// use data binding
dataBinding {
enabled = true
}
// use java 8 language
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
對于編譯時(shí)注解之類的配置,我們也需要在每個(gè)模塊里面都進(jìn)行聲明。
完成了以上的配置,我們只要根據(jù)需要編譯的類型,修改之前定義的布爾值,來決定是將該模塊編譯成 APP 還是作為類庫來使用即可。
以上就是組件化在 Android 開發(fā)當(dāng)中的應(yīng)用。
總結(jié)
MVC、MVP和MVVM各有各自的特點(diǎn),可以根據(jù)應(yīng)用開發(fā)的需要選擇適合自己的架構(gòu)模式。組件化的目的就在于保持各個(gè)模塊之間的獨(dú)立從而便于分工協(xié)作。它們之間的關(guān)系就是,你可以在組件化的各個(gè)模塊中應(yīng)用前面三種架構(gòu)模式的一種或者幾種。
獲取更多技術(shù)文章可以直接關(guān)注我的公眾號「Hello 開發(fā)者」,另外感興趣的可以加入技術(shù) QQ 交流群:1018235573.
以上,感謝閱讀~