本文為菜鳥窩作者 吳威龍 連載
菜鳥窩是專業(yè)的程序猿在線學(xué)習(xí)平臺旷太,提供最系統(tǒng)的 Android 項(xiàng)目實(shí)戰(zhàn)課程
如需轉(zhuǎn)載,請聯(lián)系菜鳥窩公眾號(cniao5),并注明出處洽洁。
前言
上一篇文章
RecycleView 綜合使用案例(結(jié)合 ButterKnife渴邦、Retrofit、Picasso) 分享了在使使用 RecycleView 的時(shí)候如何結(jié)合 ButterKnife意系、Retrofit、Picasso 等框架進(jìn)行使用〗刃冢現(xiàn)在來聊聊 MVC 以及 MVP 框架吧。
下面來看看 MVC 的介紹:
MVC簡介
MVC 全名是 Model View Controller痰催,是模型(model)-視圖(view)-控制器(controller)的縮寫兜辞,一種軟件設(shè)計(jì)典范,用一種業(yè)務(wù)邏輯夸溶、數(shù)據(jù)逸吵、界面顯示分離的方法組織代碼,在改進(jìn)和個性化定制界面及用戶交互的同時(shí)缝裁,不需要重新編寫業(yè)務(wù)邏輯扫皱。
其中 M 層處理數(shù)據(jù),業(yè)務(wù)邏輯等;V 層處理界面的顯示結(jié)果韩脑;C 層起到橋梁的作用氢妈,來控制 V 層和 M 層通信以此來達(dá)到分離視圖顯示和業(yè)務(wù)邏輯層。
Android 中的 MVC
- 視圖層(View)
一般采用 XML 文件進(jìn)行界面的描述段多,這些 XML 可以理解為 AndroidApp 的 View首量。使用的時(shí)候可以非常方便的引入。同時(shí)便于后期界面的修改进苍。邏輯中與界面對應(yīng)的 id 不變化則代碼不用修改加缘,大大增強(qiáng)了代碼的可維護(hù)性。
- 控制層(Controller)
Android 的控制層的重任通常落在了眾多的 Activity 的肩上觉啊。這句話也就暗含了不要在 Activity 中寫代碼拣宏,要通過 Activity 交割 Model 業(yè)務(wù)邏輯層處理,這樣做的另外一個原因是 Android 中的 Activity 的響應(yīng)時(shí)間是 5s杠人,如果耗時(shí)的操作放在這里勋乾,程序就很容易被回收掉。
- 模型層(Model)
我們針對業(yè)務(wù)模型搜吧,建立的數(shù)據(jù)結(jié)構(gòu)和相關(guān)的類市俊,就可以理解為 AndroidApp 的 Model,Model 是與 View 無關(guān)滤奈,而與業(yè)務(wù)相關(guān)的摆昧。對數(shù)據(jù)庫的操作、對網(wǎng)絡(luò)等的操作都應(yīng)該在 Model 里面處理蜒程,當(dāng)然對業(yè)務(wù)計(jì)算等操作也是必須放在的該層的绅你。就是應(yīng)用程序中二進(jìn)制的數(shù)據(jù)。
MVC MVP 對比
通過分析上圖昭躺,只需知道 MVC 傳統(tǒng)模式是沒有把 View 和 Model 層隔離開的忌锯,MVP 模式則是 View 層和 Model 完全解耦開,通過 Presenter 這個中間人進(jìn)行傳遞信息领炫。
下面看看 MVP 的介紹:
MVP
View:負(fù)責(zé)繪制 UI 元素偶垮、與用戶進(jìn)行交互(在 Android 中體現(xiàn)為 Activity)
Model:負(fù)責(zé)存儲、檢索帝洪、操縱數(shù)據(jù)(有時(shí)也實(shí)現(xiàn)一個 Model interface 用來降低耦合)
Presenter:作為 View 與 Model 交互的中間紐帶似舵,處理與用戶交互的負(fù)責(zé)邏輯。
View interface:需要 View 實(shí)現(xiàn)的接口葱峡,View 通過 View interface 與 Presenter 進(jìn)行交互砚哗,降低耦合,方便進(jìn)行單元測試
一句話解釋就是:Presenter 是 View 和 Model 之間的代理砰奕。
MVP 優(yōu)點(diǎn)
- 降低耦合度蛛芥,實(shí)現(xiàn)了 Model 和 View 真正的完全分離提鸟,可以修改 View 而不影響 Modle
- 模塊職責(zé)劃分明顯,層次清晰
- 隱藏?cái)?shù)據(jù)
- Presenter 可以復(fù)用
- 利于測試驅(qū)動開發(fā)
- View 可以進(jìn)行組件化
- 代碼靈活性
MVP缺點(diǎn)
Presenter 中除了應(yīng)用邏輯以外仅淑,還有大量的 View->Model称勋,Model->View 的手動同步邏輯,造成 Presenter 比較笨重漓糙,維護(hù)起來會比較困難铣缠。
由于對視圖的渲染放在了 Presenter 中,所以視圖和 Presenter 的交互會過于頻繁昆禽。
如果 Presenter 過多地渲染了視圖蝗蛙,往往會使得它與特定的視圖的聯(lián)系過于緊密。一旦視圖需要變更醉鳖,那么 Presenter 也需要變更了捡硅。
額外的代碼復(fù)雜度及學(xué)習(xí)成本。
代碼實(shí)現(xiàn):
MVP 是一種思想盗棵,每個人的理解不一樣壮韭,所以每個人的實(shí)現(xiàn)都是大同小異的。谷歌推出官方的 MVP Demo纹因,我們可以參考谷歌給出的進(jìn)行稍微修改一點(diǎn)點(diǎn)喷屋,形成自己風(fēng)格的 mvp 模式。注意瞭恰,模式的實(shí)現(xiàn)是沒有所謂的標(biāo)準(zhǔn)的屯曹,只要達(dá)到這種解耦效果就可以了。
這里以 菜鳥手機(jī)助手 的【推薦】欄目舉例說明
模塊解析:
Contract 接口:里面定義 presenter 接口 和 view 接口
presenter :負(fù)責(zé)和 view惊畏,module 交互
view :基本都是對控件進(jìn)行更新即可
BaseView
public interface BaseView {
//聲明公共的一些方法
void showLodading();//顯示加載進(jìn)度條
void dimissLoading();//關(guān)閉加載進(jìn)度條
}
RecommendContract
該類存放兩個接口恶耽,View 接口和 Presenter 接口。
以前兩個接口都是分開寫的颜启,現(xiàn)在合起來放在一個接口類里面偷俭,顯得不那么凌亂了。接口 View 給具體視圖層實(shí)現(xiàn)缰盏,譬如本例中的 RecommendFragment涌萤。
接口 Presenter 給具體的 Presenter 層實(shí)現(xiàn),譬如本例的 RecommendPresenter口猜。
public interface RecommendContract {
//接口與接口之間的繼承是用 extends
interface View extends BaseView{
void showResult(List<AppInfo> datas); //顯示數(shù)據(jù)
void showNodata(); //提示沒數(shù)據(jù)
void showError(String msg); //提示錯誤
}
// BasePresenter 暫時(shí)為空形葬,就不列代碼出來了,以后可以增加
interface Presenter extends BasePresenter{
public void requestDatas();//請求數(shù)據(jù)
}
}
Model 層
RecommendModel 類里面調(diào)用到的 HttpManager暮的、ApiService 類的代碼就不貼出來了,因?yàn)楸纠饕v MVP 模式淌实。
public class RecommendModel {
// presenter 層調(diào)用該方法冻辩,執(zhí)行完畢有回調(diào)方法
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);
}
}
Presenter 層
實(shí)現(xiàn) Contract 層接口 RecommendContract.Presenter猖腕,實(shí)現(xiàn)該接口下的抽象方法。
實(shí)現(xiàn)抽象方法:requestDatas()恨闪,請求數(shù)據(jù)
代碼中 引用 model 層的對象RecommendModel mModel倘感,
通過 mModel.getApps() 調(diào)用 Model 層的具體方法實(shí)現(xiàn)需求。引用 view 接口實(shí)例對象咙咽,接口引用指向一個對象 RecommendContract.View mView老玛,
通過 mView.showLodading()、mView.showNodata() 等調(diào)用 View 層具體方法實(shí)現(xiàn)需求钧敞。
public class RecommendPresenter implements RecommendContract.Presenter {
//引用 model 層的對象
private RecommendModel mModel;
//引用 view 接口實(shí)例對象蜡豹,接口引用指向一個對象
private RecommendContract.View mView;
//構(gòu)造方法中傳過來 view 對象
public RecommendPresenter(RecommendContract.View view){
this.mView = view;
mModel = new RecommendModel();
}
//實(shí)現(xiàn) RecommendContract.Presenter 接口,重寫接口的抽象方法
@Override
public void requestDatas() {
//調(diào)用 實(shí)現(xiàn)了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());
}
});
}
}
View 層
實(shí)現(xiàn) Contract 層接口 RecommendContract.View溉苛,實(shí)現(xiàn)該接口下的幾個抽象方法:
showLodading(),
dimissLoading(), showNodata(), showError(), showResult()代碼中使用 RecommendContract.Presenter mPresenter 接口實(shí)例對象镜廉,接口引用指向一個對象,
通過 mPresenter.requestDatas() 調(diào)用 Presenter 的 requestDatas()方法愚战。
以此達(dá)到解耦的目的娇唯。
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接口實(shí)例對象,接口引用指向一個對象
@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());
//實(shí)例化 Presenter
mPresenter = new RecommendPresenter(this);
initData();
return view;
}
private void initData(){
//調(diào)用 presenter 去請求數(shù)據(jù)寂玲,實(shí)際上塔插,presenter 是指揮 Model 去做實(shí)際操作
mPresenter.requestDatas();
}
//數(shù)據(jù)顯示
private void initRecycleView(List<AppInfo> datas){
//為RecyclerView設(shè)置布局管理器
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
//為RecyclerView設(shè)置分割線(這個可以對DividerItemDecoration進(jìn)行修改,自定義)
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));
//動畫
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdatper = new RecomendAppAdatper(getActivity(),datas);
mRecyclerView.setAdapter(mAdatper);
}
// 實(shí)現(xiàn) RecommendContract.View 接口后 重寫的抽象方法
@Override
public void showResult(List<AppInfo> datas) {
initRecycleView( datas);
}
@Override
public void showNodata() {
Toast.makeText(getActivity(),"暫時(shí)無數(shù)據(jù)拓哟,請吃完飯?jiān)賮?,Toast.LENGTH_LONG).show();
}
@Override
public void showError(String msg) {
Toast.makeText(getActivity(),"服務(wù)器開小差了:"+msg,Toast.LENGTH_LONG).show();
}
@Override
public void showLodading() {
mProgressDialog.show();
}
@Override
public void dimissLoading() {
if(mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
}
}
總結(jié)
通過上面代碼分析可以很清晰的明白 MVP 框架模式是怎么進(jìn)行代碼解耦的想许。下面再次梳理一下 MVP 實(shí)現(xiàn)步驟:
定義 Presenter、View 接口(可以向上面例子彰檬,放在 Contract 接口里面):接口里面定義 presenter 層伸刃、view 層的抽象方法。簡單的說就具體實(shí)現(xiàn)類所要實(shí)現(xiàn)的方法逢倍。
具體 Presenter 層實(shí)現(xiàn)類實(shí)現(xiàn)定義的 Presenter 接口捧颅,實(shí)現(xiàn)該接口的抽象方法。
比如 RecommendPresenter 類實(shí)現(xiàn)了 RecommendContract.Presenter 接口较雕,實(shí)現(xiàn) requestDatas()具體 View 層實(shí)現(xiàn)類實(shí)現(xiàn)定義的 View 接口碉哑,實(shí)現(xiàn)該接口下的抽象方法。
比如 RecommendFragment.RecommendFragment 通過類中的 RecommendPresenter 對象調(diào)用 Presenter 層里面的方法:requestDatas() 方法亮蒋。在 Presenter 層中通過 Model 對象調(diào)用 Model 層里面的具體方法:getApps(). Model 層的方法執(zhí)行完后通過回調(diào)把結(jié)果回調(diào)給 Presenter 層扣典,Presenter 層再通過 View 層的接口實(shí)例對象,調(diào)用相關(guān)方法把結(jié)果回調(diào)到具體實(shí)現(xiàn)了該 View 接口的 View 層慎玖。
擼這個項(xiàng)目的一半贮尖,你就是大神 , 戳http://mp.weixin.qq.com/s/ZagocTlDfxZpC2IjUSFhHg