MVP 架構(gòu)實(shí)戰(zhàn)深入淺出

本文為菜鳥窩作者 吳威龍 連載

菜鳥窩是專業(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 對比

image

通過分析上圖昭躺,只需知道 MVC 傳統(tǒng)模式是沒有把 View 和 Model 層隔離開的忌锯,MVP 模式則是 View 層和 Model 完全解耦開,通過 Presenter 這個中間人進(jìn)行傳遞信息领炫。

下面看看 MVP 的介紹:

MVP

image

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)

  1. 降低耦合度蛛芥,實(shí)現(xiàn)了 Model 和 View 真正的完全分離提鸟,可以修改 View 而不影響 Modle
  2. 模塊職責(zé)劃分明顯,層次清晰
  3. 隱藏?cái)?shù)據(jù)
  4. Presenter 可以復(fù)用
  5. 利于測試驅(qū)動開發(fā)
  6. View 可以進(jìn)行組件化
  7. 代碼靈活性

MVP缺點(diǎn)

  1. Presenter 中除了應(yīng)用邏輯以外仅淑,還有大量的 View->Model称勋,Model->View 的手動同步邏輯,造成 Presenter 比較笨重漓糙,維護(hù)起來會比較困難铣缠。

  2. 由于對視圖的渲染放在了 Presenter 中,所以視圖和 Presenter 的交互會過于頻繁昆禽。

  3. 如果 Presenter 過多地渲染了視圖蝗蛙,往往會使得它與特定的視圖的聯(lián)系過于緊密。一旦視圖需要變更醉鳖,那么 Presenter 也需要變更了捡硅。

  4. 額外的代碼復(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市趁怔,隨后出現(xiàn)的幾起案子湿硝,更是在濱河造成了極大的恐慌薪前,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件关斜,死亡現(xiàn)場離奇詭異示括,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)痢畜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門垛膝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丁稀,你說我怎么就攤上這事吼拥。” “怎么了二驰?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵扔罪,是天一觀的道長。 經(jīng)常有香客問我桶雀,道長矿酵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任矗积,我火速辦了婚禮全肮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘棘捣。我一直安慰自己辜腺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布乍恐。 她就那樣靜靜地躺著评疗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪茵烈。 梳的紋絲不亂的頭發(fā)上百匆,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機(jī)與錄音呜投,去河邊找鬼加匈。 笑死,一個胖子當(dāng)著我的面吹牛仑荐,可吹牛的內(nèi)容都是我干的雕拼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼粘招,長吁一口氣:“原來是場噩夢啊……” “哼啥寇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤示姿,失蹤者是張志新(化名)和其女友劉穎甜橱,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體栈戳,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年难裆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了子檀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡乃戈,死狀恐怖褂痰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情症虑,我是刑警寧澤缩歪,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站谍憔,受9級特大地震影響匪蝙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜习贫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一逛球、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苫昌,春花似錦颤绕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至袜硫,卻和暖如春氯葬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背父款。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工溢谤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人憨攒。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓世杀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肝集。 傳聞我的和親對象是個殘疾皇子瞻坝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容