Android MVP的簡單使用

1.前言

你是否遇到過Activity/Fragment中成百上千行代碼,完全無法維護,看著頭疼?
你是否遇到過因后臺接口還未寫而你不能先寫代碼邏輯的情況?
你是否遇到過用MVC架構(gòu)寫的項目進行單元測試時的深深無奈?
如果你現(xiàn)在還是用MVC架構(gòu)模式在寫項目,請先轉(zhuǎn)到MVP模式!

2.MVC架構(gòu)

MVC架構(gòu)模式最初生根于服務器端的Web開發(fā),后來漸漸能夠勝任客戶端
Web開發(fā),再后來因Android項目由XML和Activity/Fragment組成,慢慢的Android
開發(fā)者開始使用類似MVC的架構(gòu)模式開發(fā)應用.

M層:模型層(model),主要是實體類,數(shù)據(jù)庫,網(wǎng)絡(luò)等存在的層面,model將新的數(shù)
據(jù)發(fā)送到view層,用戶得到數(shù)據(jù)響應.

V層:視圖層(view),一般指XML為代表的視圖界面.顯示來源于model層的數(shù)據(jù).  
用戶的點擊操作等事件從view層傳遞到controller層.

C層:控制層(controller),一般以Activity/Fragment為代表.C層主要是連接V層和
M層的,C層收到V層發(fā)送過來的事件請求,從M層獲取數(shù)據(jù),展示給V層.

從上圖可以看出M層和V層有連接關(guān)系,而Activity有時候既充當了控制層又充
當了視圖層,導致項目維護比較麻煩.

1.MVC架構(gòu)優(yōu)缺點

A. 缺點
    M層和V層有連接關(guān)系,沒有解耦,導致維護困難.
    Activity/Fragment中的代碼過多,難以維護.
    Activity中有很多關(guān)于視圖UI的顯示代碼,因此View視圖和Activity控制器    
并不是完全分離的,當Activity類業(yè)務過多的時候,會變得難以管理和維護.尤其
是當UI的狀態(tài)數(shù)據(jù),跟持久化的數(shù)據(jù)混雜在一起,變得極為混亂.
B. 優(yōu)點
    控制層和View層都在Activity中進行操作,數(shù)據(jù)操作方便.
    模塊職責劃分明確.主要劃分層M,V,C三個模塊.

3.MVP架構(gòu)

MVP,即是Model,View,Presenter架構(gòu)模式.看起來類似MVC,其實不然.從上圖
能看到Model層和View層沒有相連接,完全解耦.

用戶觸碰界面觸發(fā)事件,View層把事件通知Presenter層,Presenter層通知
Model層處理這個事件,Model層處理后把結(jié)果發(fā)送到Presenter層,Presenter層再
通知View層,最后View層做出改變.這是一整套流程.

M層:模型層(Model),此層和MVC中的M層作用類似.

V層:視圖層(View),在MVC中V層只包含XML文件,而MVP中V層包含
XML,Activity和Fragment三者.理論上V層不涉及任何邏輯,只負責界面的改變,盡
量把邏輯處理放到M層.

P層:通知層(Presenter),P層的主要作用就是連接V層和M層,起到一個通知傳遞數(shù)據(jù)的作用.

1. MVP架構(gòu)優(yōu)缺點

A. 缺點
    MVP中接口過多.
    每一個功能,相比于MVC要多寫好幾個文件.
    如果某一個界面中需要請求多個服務器接口,這個界面文件中會實現(xiàn)很多的回調(diào)接口,導致代碼繁雜.
    如果更改了數(shù)據(jù)源和請求中參數(shù),會導致更多的代碼修改.
    額外的代碼復雜度及學習成本.
B. 優(yōu)點
    模塊職責劃分明顯,層次清晰,接口功能清晰.
    Model層和View層分離,解耦.修改View而不影響Model.
    功能復用度高,方便.一個Presenter可以復用于多個View,而不用更改Presenter的邏輯.
    有利于測試驅(qū)動開發(fā),以前的Android開發(fā)是難以進行單元測試.
    如果后臺接口還未寫好,但已知返回數(shù)據(jù)類型的情況下,完全可以寫出此接口完整的功能.

四.MVP架構(gòu)實戰(zhàn)

MVP簡單案例

用戶點擊按鈕后,Presenter層通知Model層請求處理網(wǎng)絡(luò)數(shù)據(jù),處理后Model層
把結(jié)果數(shù)據(jù)發(fā)送給Presenter層,Presenter層再通知View層,然后View層改變TextView顯示的內(nèi)容.

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".view.SingleInterfaceActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="點擊" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100px"
        android:text="請點擊上方按鈕獲取數(shù)據(jù)" />
</LinearLayout>

頁面:

public class SingleActivity extends AppCompatActivity {

    private Button button;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_single_interface);
        button = findViewById(R.id.button);
        textView = findViewById(R.id.textView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

    }
}

Api:

public interface MyServer {

    public String Url = "http://api.shujuzhihui.cn/api/news/";

    @GET("categories?")
    Observable<ReBean> getData(@Query("appKey") String appKey);
}

bean:

public class ReBean {
    private String ERRORCODE;
    private List<String> RESULT;

    public String getERRORCODE() {
        return ERRORCODE;
    }

    public void setERRORCODE(String ERRORCODE) {
        this.ERRORCODE = ERRORCODE;
    }

    public List<String> getRESULT() {
        return RESULT;
    }

    public void setRESULT(List<String> RESULT) {
        this.RESULT = RESULT;
    }
}

Model:

public interface MyModel {

    void getData(String appKey, CallBack<ReBean, String> callBack);
}

public class MyModelImpl implements MyModel {

    @Override
    public void getData(String appKey, final CallBack<ReBean, String> callBack) {

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(MyServer.Url)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        MyServer myServer = retrofit.create(MyServer.class);

        Observable<ReBean> data = myServer.getData(appKey);

        data.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ReBean>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(ReBean reBean) {

                        //成功
                        //txt.setText(reBean.getRESULT().toString());
                        if (reBean!=null){
                            if (reBean.getERRORCODE().equals("0")){
                                callBack.onSuccess(reBean);
                            }else{
                                callBack.onFail("失敗");
                            }
                        }else{
                            callBack.onFail("出現(xiàn)錯誤");
                        }
                    }

                    @Override
                    public void onError(Throwable e) {

                        //失敗
                        callBack.onFail("出現(xiàn)錯誤");
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

}

CallBack:

public interface CallBack<K, V> {
    void onSuccess(K data);

    void onFail(V data);
}

Presenter:

public interface MyPresenter {

    void getData(String appKey);
}

public class MyPresenterImpl implements  MyPresenter, CallBack<ReBean,String> {

    private MyModel myModel;
    private MyView myView;

    public MyPresenterImpl(MyModel myModel,MyView myView) {
        this.myModel = myModel;
        this.myView = myView;
    }

    @Override
    public void getData(String appKey) {
        if (myModel!=null){
            myModel.getData(appKey, this);
        }
    }

    @Override
    public void onSuccess(ReBean data) {
        //如果Model層請求數(shù)據(jù)成功,則此處應執(zhí)行通知View層的代碼   
        if(myView!=null){
            myView.onSuccess(data);
        }
    }

    @Override
    public void onFail(String data) {
        //如果Model層請求數(shù)據(jù)失敗,則此處應執(zhí)行通知View層的代碼
        if(myView!=null){
            myView.onFail(data);
        }
    }

}

IView:

public interface MyView<K,V> {

    void onSuccess(K data);

    void onFail(V data);
}


public class MainActivity extends AppCompatActivity implements View.OnClickListener, MyView<ReBean,String> {

    private Button btn;
    private TextView txt;
    private String appKey = "908ca46881994ffaa6ca20b31755b675";
    private MyPresenterImpl myPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myPresenter = new MyPresenterImpl(new MyModelImpl(), this);

        initView();
    }

    private void initView() {
        btn = (Button) findViewById(R.id.btn);
        txt = (TextView) findViewById(R.id.txt);

        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn:
                initData();
                break;
        }
    }

    private void initData() {
        myPresenter.getData(appKey);
    }

    @Override
    public void onSuccess(ReBean data) {
        txt.setText(data.getRESULT().toString());
    }

    @Override
    public void onFail(String data) {
        txt.setText(data);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容