一夜矗、表現(xiàn)層模式架構(gòu)的演變
三層架構(gòu)通常是指表現(xiàn)層(Presentation Layer)究西、業(yè)務(wù)邏輯層(Business Layer)和數(shù)據(jù)訪問層(Data Access Layer)俭缓。表現(xiàn)層是用戶和系統(tǒng)之間交流的橋梁,它一方面提供與用戶交互的界面箕憾,另一方面也提供了與數(shù)據(jù)交互的邏輯诬辈,便于協(xié)調(diào)用戶與系統(tǒng)的操作。我們所談?wù)摰?MVC趴乡、MVP对省、MVVM 等設(shè)計模式都屬于表現(xiàn)層的設(shè)計模式。
1. MVC(Model-View-Controller)模式
如果把 MVC 模式套用在 Android 中浙宜,那么:
- View 對應(yīng) xml 布局官辽,實現(xiàn)數(shù)據(jù)的展示
- Controller 對應(yīng) Activity / Fragment ,處理業(yè)務(wù)邏輯
- Model 對應(yīng)數(shù)據(jù)源粟瞬,包括網(wǎng)絡(luò)接口數(shù)據(jù)同仆、數(shù)據(jù)庫、緩存等
使用 MVC 模式看似分工明確裙品,但是應(yīng)用到 Android 中俗批,會帶不少的問題:
- Activity / Fragment 實現(xiàn)了多重職責(zé)俗或,即是 View,又是 Controller岁忘,導(dǎo)致代碼復(fù)雜臃腫辛慰,難以復(fù)用
- 把 Activity / Fragment 作為 Controller,無法對 Controller 進行單元測試
所以干像,在 Android 中帅腌,MVC 模式不太適用。
2. MVP(Model-View-Presenter) 模式
MVP 模式是 MVC 的進化版麻汰,它把 Controller 的職責(zé)從 Activity/Fragment 中拆分出來速客,作為 Presenter,這樣就實現(xiàn)了 Activity/Fragment 和業(yè)務(wù)邏輯的解耦五鲫,更好地解決了數(shù)據(jù)與界面的關(guān)系溺职。
二、細(xì)說 MVP
1. 職責(zé)劃分
MVP 各自的職責(zé)分別是:
-
View
- 對應(yīng) Activity / Fragment / Custom View
- 與用戶交互位喂,響應(yīng)用戶操作浪耘,分派事件行為給 Presenter 處理
- 響應(yīng) Presenter 回調(diào),對數(shù)據(jù)進行顯示
-
Presenter
- 是連接 VIew 和其它代碼的膠水
- 用于轉(zhuǎn)換 Model 的數(shù)據(jù)以便于 VIew 顯示
- Presenter 不做 UI 相關(guān)處理塑崖,也不包含上下文對象(Context)
-
Model
- 與數(shù)據(jù)進行交互七冲,對數(shù)據(jù)進行加工處理
- 通常是與 Android 無關(guān)的,不會用到 Android SDK
- 關(guān)注從哪里拿數(shù)據(jù)(Retrofit规婆、Sqlite etc.)
2. 架構(gòu)實現(xiàn)
谷歌官方已經(jīng)給出了一個 MVP 架構(gòu)的實踐示例[googlesamples/android-architecture](上面圖中的 REPOSITORIES 就是指 Model 層)癞埠。
接下來,我們以它為例來看一下具體的實現(xiàn)聋呢。我們簡化一下代碼,更直觀地看一下它們之間的關(guān)系颠区。
首先官方的例子定義了一個契約(Contract)類:
public interface TasksContract {
interface View extends BaseView<Presenter> {
void setLoadingIndicator(boolean active);
void showTasks(List<Task> tasks);''
void showLoadingTasksError();
// ....
}
interface Presenter extends BasePresenter {
void loadTasks();
void addNewTask();
// ....
}
}
契約類就是把 MVP 所需要定義的幾個接口都寫在一個類里面削锰。在項目實現(xiàn)中,我會把 Model 接口也寫到契約類里毕莱,定義契約類的好處除了可以少寫幾個 Java 文件外器贩,也比較直觀。比如先在 Presenter 定義一個 loadTasks 方法朋截,那么相應(yīng)地蛹稍,Model 就接口需要定義一個 getTasks 方法來為 Presenter 提供數(shù)據(jù),View 接口則需要定義一個 showTasks 來顯示獲取到 Tasks 數(shù)據(jù)部服,以及 setLoadingIndicator唆姐、showLoadingTasksError 等方法來更新 UI 狀態(tài)。
- Model 層的實現(xiàn)代碼:
public class TasksRepository implements TasksDataSource {
@Inject
TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
@Local TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = tasksRemoteDataSource;
mTasksLocalDataSource = tasksLocalDataSource;
}
@Override
public Observable<List<Task>> getTasks() {
// 從緩存或者網(wǎng)絡(luò)接口等數(shù)據(jù)源獲取數(shù)據(jù)
}
}
Model 層的代碼維護了兩個數(shù)據(jù)源(mTasksRemoteDataSource 和 mTasksLocalDataSource)廓八,用來為 Presenter 提供數(shù)據(jù)奉芦,Presenter 無需關(guān)心從哪里拿數(shù)據(jù)赵抢。代碼中的注解
@Inject
標(biāo)記了該構(gòu)造方法可以被注入,如果你使用了 Google 的 Dagger 框架声功,Dagger 可以提供 TasksRepository 所需的依賴烦却,并創(chuàng)建一個 TasksRepository 對象。
- Presenter 層的實現(xiàn)代碼:
public class TasksPresenter implements TasksContract.Presenter {
@Inject
TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) {
mTasksRepository = tasksRepository;
mTasksView = tasksView;
}
@Override
public void loadTasks() {
mTasksRepository.getTasks()
.observeOn(mSchedulerProvider.ui())
.subscribe(new Observer<List<Task>>() {
@Override
public void onCompleted() {
mTasksView.setLoadingIndicator(false);
}
@Override
public void onError(Throwable e) {
mTasksView.showLoadingTasksError();
}
@Override
public void onNext(List<Task> tasks) {
mTasksView.showTasks(tasks);
}
});
}
}
這里我們使用了 Dagger 和 RxJava(官方例子是分開兩個獨立的分支)先巴,Dagger 注入所需的依賴其爵,并創(chuàng)建 TasksPresenter 對象。View 通過調(diào)用 Presenter 的 loadTasks 方法來獲取便于 VIew 展示的數(shù)據(jù)伸蚯。Presenter 就是用于響應(yīng) View 分派的事件摩渺,校驗數(shù)據(jù)并提交給 Model 處理,最后把 Model 處理的結(jié)果轉(zhuǎn)交給 View朝卒。
另外证逻,Presenter 也承擔(dān)了一部分 Activity / Fragment 的業(yè)務(wù)邏輯,這樣也減輕了 Activity / Fragment 作為 View 的負(fù)擔(dān)抗斤。
- View 層的實現(xiàn)代碼
public class TasksFragment extends Fragment implements TasksContract.View {
private TasksContract.Presenter mPresenter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
swipeRefreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh() {
mPresenter.loadTasks();
}
});
}
@Override
public void showLoadingTasksError() {
showMessage(getString(R.string.loading_tasks_error));
}
@Override
public void showTasks(List<Task> tasks) {
mListAdapter.replaceData(tasks);
mTasksView.setVisibility(View.VISIBLE);
mNoTasksView.setVisibility(View.GONE);
}
}
View 就比較簡單了囚企,純粹做 UI 相關(guān)的處理,不關(guān)注業(yè)務(wù)處理瑞眼。它將用戶的行為傳遞給 Presenter龙宏,同時接收 Presenter 的調(diào)用來更新界面。在上面的代碼中伤疙,當(dāng) TasksFragment 初始化之后银酗,會調(diào)用 swipeRefreshLayout.setRefreshing 來觸發(fā) Presenter 的 loadTasks() 方法。之后徒像,當(dāng) Presenter 處理完后黍特,調(diào)用 View 的 showTasks 或者 showLoadingTasksError 方法,TasksFragment 只需要關(guān)注界面更新的具體實現(xiàn)锯蛀。
從上面的例子來看灭衷,Model-View-Presenter 三種角色之間分工明確,使得數(shù)據(jù)與界面之間的耦合更低旁涤,代碼復(fù)用性更高翔曲,也更方便于測試。
三劈愚、總結(jié)
使用 MVP 模式來開發(fā) Android 應(yīng)用給我們帶來了很多好處瞳遍,只是需要多定義 M-V-P 這三個接口(可以寫在一個 Contract 類中),正是因為這些接口菌羽,才使得類的組織結(jié)構(gòu)更加清晰掠械,每一層實現(xiàn)對應(yīng)的接口,只關(guān)注其本身單一的職責(zé)。這樣層與層的耦合底非常低份蝴,可維護性提高犁功。
一路走來,我們也在不斷地嘗試婚夫,持續(xù)地改進現(xiàn)有的架構(gòu)浸卦。我們結(jié)合了目前流行的框架來提高生產(chǎn)力;比如案糙,使用 Dagger 來為 Presenter 注入依賴(不需要再用new
關(guān)鍵字創(chuàng)建各種對象)限嫌,還使用了 Retrofit、RxJava时捌、Realm 等框架更好地去組織數(shù)據(jù)層的接口怒医,以及使用 DataBinding 來簡化 UI 界面與數(shù)據(jù)實體的綁定。
架構(gòu)的作用是為解決痛點奢讨,適合自己的才是最好的稚叹。希望本文對你找到適合自己項目的架構(gòu)組織方式有所幫助。
// 能力一般拿诸,水平有限扒袖。文中有不妥或謬誤之處在所難免,請大家批評指正亩码。