前言
上一篇文章
【Android 進階】ButterKnife+Retrofit+Picasso+RecycleView 實現一個小案例) 分享了在使使用 RecycleView 的時候如何結合 ButterKnife、Retrofit、Picasso 等框架進行使用。現在來聊聊 MVC 以及 MVP 框架吧。
下面來看看 MVC 的介紹:
MVC簡介
MVC 全名是 Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設計典範,用一種業務邏輯、數據、界面顯示分離的方法組織代碼,在改進和個性化定製界面及用戶交互的同時,不需要重新編寫業務邏輯。
其中 M 層處理數據,業務邏輯等;V 層處理界面的顯示結果;C 層起到橋樑的作用,來控制 V 層和 M 層通信以此來達到分離視圖顯示和業務邏輯層。
Android 中的 MVC
- 視圖層(View)
一般採用 XML 文件進行界面的描述,這些 XML 可以理解爲 AndroidApp 的 View。使用的時候可以非常方便的引入。同時便於後期界面的修改。邏輯中與界面對應的 id 不變化則代碼不用修改,大大增強了代碼的可維護性。
- 控制層(Controller)
Android 的控制層的重任通常落在了衆多的 Activity 的肩上。這句話也就暗含了不要在 Activity 中寫代碼,要通過 Activity 交割 Model 業務邏輯層處理,這樣做的另外一個原因是 Android 中的 Activity 的響應時間是 5s,如果耗時的操作放在這裏,程序就很容易被回收掉。
- 模型層(Model)
我們針對業務模型,建立的數據結構和相關的類,就可以理解爲 AndroidApp 的 Model,Model 是與 View 無關,而與業務相關的。對數據庫的操作、對網絡等的操作都應該在 Model 裏面處理,當然對業務計算等操作也是必須放在的該層的。就是應用程序中二進制的數據。
MVC MVP 對比
通過分析上圖,只需知道 MVC 傳統模式是沒有把 View 和 Model 層隔離開的,MVP 模式則是 View 層和 Model 完全解耦開,通過 Presenter 這個中間人進行傳遞信息。
下面看看 MVP 的介紹:
MVP
View:負責繪製 UI 元素、與用戶進行交互(在 Android 中體現爲 Activity)
Model:負責存儲、檢索、操縱數據(有時也實現一個 Model interface 用來降低耦合)
Presenter:作爲 View 與 Model 交互的中間紐帶,處理與用戶交互的負責邏輯。
View interface:需要 View 實現的接口,View 通過 View interface 與 Presenter 進行交互,降低耦合,方便進行單元測試
一句話解釋就是:Presenter 是 View 和 Model 之間的代理。
MVP 優點
- 降低耦合度,實現了 Model 和 View 真正的完全分離,可以修改 View 而不影響 Modle
- 模塊職責劃分明顯,層次清晰
- 隱藏數據
- Presenter 可以複用
- 利於測試驅動開發
- View 可以進行組件化
- 代碼靈活性
MVP缺點
Presenter 中除了應用邏輯以外,還有大量的 View->Model,Model->View 的手動同步邏輯,造成 Presenter 比較笨重,維護起來會比較困難。
由於對視圖的渲染放在了 Presenter 中,所以視圖和 Presenter 的交互會過於頻繁。
如果 Presenter 過多地渲染了視圖,往往會使得它與特定的視圖的聯繫過於緊密。一旦視圖需要變更,那麼 Presenter 也需要變更了。
額外的代碼複雜度及學習成本。
代碼實現:
MVP 是一種思想,每個人的理解不一樣,所以每個人的實現都是大同小異的。谷歌推出官方的 MVP Demo,我們可以參考谷歌給出的進行稍微修改一點點,形成自己風格的 mvp 模式。注意,模式的實現是沒有所謂的標準的,只要達到這種解耦效果就可以了。
這裏以 手機助手 的【推薦】欄目舉例說明
模塊解析:
Contract 接口:裏面定義 presenter 接口 和 view 接口
presenter :負責和 view,module 交互
view :基本都是對控件進行更新即可
BaseView
public interface BaseView {
//聲明公共的一些方法
void showLodading();//顯示加載進度條
void dimissLoading();//關閉加載進度條
}
- 1
- 2
- 3
- 4
- 5
- 6
RecommendContract
該類存放兩個接口,View 接口和 Presenter 接口。
以前兩個接口都是分開寫的,現在合起來放在一個接口類裏面,顯得不那麼凌亂了。
接口 View 給具體視圖層實現,譬如本例中的 RecommendFragment
接口 Presenter 給具體的 Presenter 層實現,譬如本例的 RecommendPresenter
public interface RecommendContract {
//接口與接口之間的繼承是用 extends
interface View extends BaseView{
void showResult(List<AppInfo> datas); //顯示數據
void showNodata(); //提示沒數據
void showError(String msg); //提示錯誤
}
// BasePresenter 暫時爲空,就不列代碼出來了,以後可以增加
interface Presenter extends BasePresenter{
public void requestDatas();//請求數據
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
Model 層
RecommendModel 類裏面調用到的 HttpManager、ApiService 類的代碼就不貼出來了,因爲本例主要講 MVP 模式。
public class RecommendModel {
// presenter 層調用該方法,執行完畢有回調方法
public void getApps(Callback<PageBean<AppInfo>> callback){
HttpManager manager = new HttpManager();
ApiService apiService =manager.getRetrofit(manager.getOkHttpClient()).create(ApiService.class);
apiService.getApps("{'page':0}").enqueue(callback);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
Presenter 層
實現 Contract 層接口 RecommendContract.Presenter,實現該接口下的抽象方法。
實現抽象方法:requestDatas(),請求數據
代碼中 引用 model 層的對象RecommendModel mModel,
通過 mModel.getApps() 調用 Model 層的具體方法實現需求。
引用 view 接口實例對象,接口引用指向一個對象 RecommendContract.View mView,
通過 mView.showLodading()、mView.showNodata() 等調用 View 層具體方法實現需求。
public class RecommendPresenter implements RecommendContract.Presenter {
//引用 model 層的對象
private RecommendModel mModel;
//引用 view 接口實例對象,接口引用指向一個對象
private RecommendContract.View mView;
//構造方法中傳過來 view 對象
public RecommendPresenter(RecommendContract.View view){
this.mView = view;
mModel = new RecommendModel();
}
//實現 RecommendContract.Presenter 接口,重寫接口的抽象方法
@Override
public void requestDatas() {
//調用 實現了RecommendContract.View 接口的 fragment 裏面重寫的 showLodading()方法
mView.showLodading();
mModel.getApps(new Callback<PageBean<AppInfo>>() {
@Override
public void onResponse(Call<PageBean<AppInfo>> call, Response<PageBean<AppInfo>> response) {
if(response !=null){
mView.showResult(response.body().getDatas());
}
else{
mView.showNodata();
}
mView.dimissLoading();
}
@Override
public void onFailure(Call<PageBean<AppInfo>> call, Throwable t) {
mView.dimissLoading();
mView.showError(t.getMessage());
}
});
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
View 層
實現 Contract 層接口 RecommendContract.View,實現該接口下的幾個抽象方法:
showLodading(),
dimissLoading(), showNodata(), showError(), showResult()
代碼中使用 RecommendContract.Presenter mPresenter 接口實例對象,接口引用指向一個對象,
通過 mPresenter.requestDatas() 調用 Presenter 的 requestDatas()方法。
以此達到解耦的目的。
public class RecommendFragment extends Fragment implements RecommendContract.View {
@BindView(R.id.recycle_view)
RecyclerView mRecyclerView;
private RecomendAppAdatper mAdatper;
private ProgressDialog mProgressDialog;
private RecommendContract.Presenter mPresenter;//presenter接口實例對象,接口引用指向一個對象
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recomend, container, false);
ButterKnife.bind(this, view);
mProgressDialog = new ProgressDialog(getActivity());
//實例化 Presenter
mPresenter = new RecommendPresenter(this);
initData();
return view;
}
private void initData(){
//調用 presenter 去請求數據,實際上,presenter 是指揮 Model 去做實際操作
mPresenter.requestDatas();
}
//數據顯示
private void initRecycleView(List<AppInfo> datas){
//爲RecyclerView設置佈局管理器
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
//爲RecyclerView設置分割線(這個可以對DividerItemDecoration進行修改,自定義)
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));
//動畫
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdatper = new RecomendAppAdatper(getActivity(),datas);
mRecyclerView.setAdapter(mAdatper);
}
// 實現 RecommendContract.View 接口後 重寫的抽象方法
@Override
public void showResult(List<AppInfo> datas) {
initRecycleView( datas);
}
@Override
public void showNodata() {
Toast.makeText(getActivity(),"暫時無數據,請吃完飯再來",Toast.LENGTH_LONG).show();
}
@Override
public void showError(String msg) {
Toast.makeText(getActivity(),"服務器開小差了:"+msg,Toast.LENGTH_LONG).show();
}
@Override
public void showLodading() {
mProgressDialog.show();
}
@Override
public void dimissLoading() {
if(mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
總結
通過上面代碼分析可以很清晰的明白 MVP 框架模式是怎麼進行代碼解耦的。下面再次梳理一下 MVP 實現步驟:
定義 Presenter、View 接口(可以向上面例子,放在 Contract 接口裏面):接口裏面定義 presenter 層、view 層的抽象方法。簡單的說就具體實現類所要實現的方法。
具體 Presenter 層實現類實現定義的 Presenter 接口,實現該接口的抽象方法。
比如 RecommendPresenter 類實現了 RecommendContract.Presenter 接口,實現 requestDatas() 方法。具體 View 層實現類實現定義的 View 接口,實現該接口下的抽象方法。
比如 RecommendFragment 類實現了 RecommendContract.View 接口,實現了 dimissLoading(), showNodata(), showError(), showResult() 等方法。RecommendFragment 通過類中的 RecommendPresenter 對象調用 Presenter 層裏面的方法:requestDatas() 方法。在 Presenter 層中通過 Model 對象調用 Model 層裏面的具體方法:getApps(). Model 層的方法執行完後通過回調把結果回調給 Presenter 層,Presenter 層再通過 View 層的接口實例對象,調用相關方法把結果回調到具體實現了該 View 接口的 View 層。
歡迎關注我的微信公衆號: