Android MVP 模式 理解與淺析

吾日三省吾身,爲人謀而不忠乎?與朋友交而不信乎?傳不習乎?

問題

上一個桌面項目MVC模式,在主activity中的代碼超過了2000行,完成後感覺自己的項目很爛。雖然邏輯還算清晰,但是整個View層和Controller層感覺太臃腫了。

是時候該從MVC轉到MVP模式了。下面就有我來簡單的總結一點心得,介紹一下MVP模式,希望能給想用MVP的人一點幫助。

MVP總結和介紹

在MVP模式裏通常包含4個要素:

  • (1)View:負責繪製UI元素、與用戶進行交互(在Android中體現爲Activity);

  • (2)View interface:需要View實現的接口,View通過View interface與Presenter進行交互,降低耦合,方便進行單元測試;

  • (3)Model:負責存儲、檢索、操縱數據(有時也實現一個Model interface用來降低耦合);

  • (4)Presenter:作爲View與Model交互的中間紐帶,處理與用戶交互的負責邏輯。

MVC與MVP模式比較

MVC模式

  • M : 業務層和模型層,相當與javabean和我們的業務請求代碼
  • V : 視圖層,對應Android的layout.xml佈局文件
  • C : 控制層,對應於Activity中對於UI 的各種操作

    MVP模式

  • M :還是業務層和模型層

  • V : 視圖層的責任由Activity來擔當
  • P : 新成員Prensenter 用來代理 C(control) 控制層

MVPDemo講解

下面我將用一個比較簡單的Demo來向大家展示一下MVP,感受一下朵密的力量吧!

這裏先來看一下biz,也就是業務層,業務層獨立出去,該在哪兒調用就在哪兒調用。

RequestForDataBiz代碼如下:

/**
 * Created by Administrator on 2017/2/25 0025.
 * 一個請求數據的biz
 * biz就是業務層的意思
 */

public interface RequestForDataBiz {

    //請求數據業務
    void requestForData(OnRequestListener listener);
}

數據請求的回掉接口,聲明瞭成功和失敗的方法 。OnRequestListener代碼如下:

    /**
         * Created by Administrator on 2017/2/25 0025.
    */

    /*請求成功或者失敗的回調接口,就和網絡請求一樣,一個網絡請求的回調大致
    有四個success,finish error cancel.這裏就簡單用兩個,請求返回的數據爲一個String集合*/

public interface OnRequestListener {

    void onSuccess(List<String> data);

    void onFailed();


}

RequestForDataBizIml代碼如下:

請求的實現類爲了模擬網絡請求,開啓了一個會sleep1秒的線程,然後裝填請求的數據,通過OnRequestListener 接口回調出去,與我們平時開發的方式一致。

/**
 * Created by Administrator on 2017/2/25 0025.
 * 底下這是一個完整的網絡請求,就是模擬網絡數據太麻煩了,弄個假的簡單說明
 */

public class RequestForDataBizIml implements RequestForDataBiz {

    @Override
    public void requestForData(final OnRequestListener listener) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    ArrayList<String> data = new ArrayList<String>();
                    for (int i = 1; i < 12; i++) {
                        data.add("item" + i);
                    }
                    if (null != listener) {
                        listener.onSuccess(data);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }


//    public static String doGet(Context context, String url, String params) //
//            throws IOException {
//        InputStream in = null;
//        URL realUrl = new URL(url.replace(" ", "%20"));
//        Log.e("infodoGet", "HttpUtil:doGet realUrl=" + realUrl.toString());
//        URLConnection conn = realUrl.openConnection();
//        conn.setConnectTimeout(5000);
//        conn.setReadTimeout(5000);
//        conn.setRequestProperty("accept", "*/*");
//        conn.setRequestProperty("connection", "Keep-Alive");
//        conn.setRequestProperty("user-agent",
//                "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)") ;
//        //    conn.setRequestProperty("X-Bsl-Client" , Configure.PACKAGE_NAME + "^" + Configure.has_secure) ;
//
//        try {
//            conn.connect();
//            in = conn.getInputStream();
//            BufferedReader br = new BufferedReader(new InputStreamReader(in));
//            String line = "";
//            result = new StringBuffer();
//            while (null != (line = br.readLine())) {
//                result.append(line);
//            }
//        } catch (SocketException e) {
//            return new String("SocketException");
//        } catch (SocketTimeoutException e) {
//            return new String("SocketException");
//        }
//
//        try {
//            if (in != null) {
//                in.close();
//            }
//        } catch (IOException ex) {
//            ex.printStackTrace();
//        }
//        Log.e("infodoGet", "Httpresult.toString()"+ result.toString());
//        return result.toString();
//    }
//
}

業務層的業務在此處完成。

MVP的凝視與思考

由於Activity變成了view層不再去控制界面,但是具體的界面的改變api其實還是由Activity來提供的,所以在寫MVP之前需要思考,View層需要哪些方法,要做哪些事情。

  • 1,顯示loading
  • 2,隱藏loading
  • 3,listview的初始化,展現頁面
  • 4,彈出Toast消息

MVPView接口代碼如下


/**
 * Created by Administrator on 2017/2/25 0025.
 * view層需要哪些方法,涉及到的UI展示。
 */

public interface MVPView {

    //顯示loading progressBar
    void showLoading();

    //隱藏loading progressBar
    void hideLoading();

    //ListView的初始化,展示界面
    void initListView(List<String> data);

    //Toast message
    void showMessage(String message);


}

我們總結出View層需要的接口。我們的Activty就是View層,所以直接用Activity來實現上面的方法。將View視圖層就完成。


public class MainActivity extends AppCompatActivity implements MVPView {

    private ProgressBar mMvpLoadingbarProgressBar;
    private ListView mMvpListviewListView;
    private RelativeLayout mActivityMainRelativeLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMvpLoadingbarProgressBar = (ProgressBar) findViewById(R.id.mvp_loadingbar);
        mMvpListviewListView = (ListView) findViewById(R.id.mvp_listview);
        mActivityMainRelativeLayout = (RelativeLayout) findViewById(R.id.activity_main);


    }

    @Override
    public void showLoading() {
        mMvpLoadingbarProgressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
        mMvpLoadingbarProgressBar.setVisibility(View.GONE);
    }

    @Override
    public void initListView(List<String> data) {
        ArrayAdapter adapter = new ArrayAdapter(MainActivity.this,android.R.layout.simple_list_item_1,data);
        mMvpListviewListView.setAdapter(adapter);
    }

    @Override
    public void showMessage(String message) {
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
    }
}

View視圖層完成了。接下來開始寫presenter層, 同樣在寫presenter之前想想控制層需要哪些方法?

  • (1)網絡請求數據
  • (2)點擊事件的響應

MVPresenter代碼如下

/**
 * Created by Administrator on 2017/2/25 0025.
 * - (1)網絡請求數據
 - (2)點擊事件的響應

 */

public class MVPresenter {


    private MVPView mvpView;
    RequestForDataBiz requestBiz;
    private Handler mHandler;

    public MVPresenter(MVPView mvpView) {
        this.mvpView = mvpView;
        requestBiz = new RequestForDataBizIml();
        mHandler = new Handler(Looper.getMainLooper());
    }

    public void onResume(){
        mvpView.showLoading();
        requestBiz.requestForData(new OnRequestListener() {
            @Override
            public void onSuccess(final List<String> data) {
                //由於請求開啓了新線程,所以用handler去更新界面
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mvpView.hideLoading();
                        mvpView.initListView(data);
                    }
                });

            }

            @Override
            public void onFailed() {
                mvpView.showMessage("請求失敗");
            }
        });
    }

    public void onItemClick(int position){
        mvpView.showMessage("點擊了item"+position);
    }


    public void onDestroy(){
        mvpView = null;
    }
}

Presenter完成(Presenter裏面像不像接管了MVC模式中的C層?理解纔是硬道理啊!),現在就剩下一件事,Activity中使用Presenter。

下面放大招:完整版的MainActivity:

/*
* MVP實現了,是不是很簡潔?
* 就是這麼清爽,沒有亂七八糟的業務。
* 沒有各種點擊處理邏輯,Activity只需要提供View層的方法就可以了。
* 你看現在activity的生命週期都託管給了MVPresenter。
* 你要做的就是加深理解,把你的業務邏輯也帶入到prenter層去處理。
* */
public class MainActivity extends AppCompatActivity implements MVPView ,AdapterView.OnItemClickListener{

    private ProgressBar mMvpLoadingbarProgressBar;
    private ListView mMvpListviewListView;
    private RelativeLayout mActivityMainRelativeLayout;
    MVPresenter mMVPresenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMvpLoadingbarProgressBar = (ProgressBar) findViewById(R.id.mvp_loadingbar);
        mMvpListviewListView = (ListView) findViewById(R.id.mvp_listview);
        mActivityMainRelativeLayout = (RelativeLayout) findViewById(R.id.activity_main);
        mMVPresenter=new MVPresenter(this);

    }

    @Override
    public void showLoading() {
        mMvpLoadingbarProgressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
        mMvpLoadingbarProgressBar.setVisibility(View.GONE);
    }

    @Override
    public void initListView(List<String> data) {
        ArrayAdapter adapter = new ArrayAdapter(MainActivity.this,android.R.layout.simple_list_item_1,data);
        mMvpListviewListView.setAdapter(adapter);
    }

    @Override
    public void showMessage(String message) {
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        mMVPresenter.onItemClick(position);
    }


    //將生命週期託付給MVPresenter,你的邏輯,業務,在這裏面做的事都可以轉移到MVPresenter
    @Override
    protected void onResume() {
        super.onResume();
        mMVPresenter.onResume();

    }

    @Override
    protected void onDestroy() {
        mMVPresenter.onDestroy();
        super.onDestroy();
    }
}

一點體會和小小的心得

可以看到,View只負責處理與用戶進行交互,並把數據相關的邏輯操作都扔給了Presenter去做。視圖層與控制層完全分離,可以讓我們在界面還是很粗糙的情況下,先進行控制層的開發,甚至可以先讓View層先提供方法出來,這樣可以節省很多時間。

Github源碼下載地址

發佈了40 篇原創文章 · 獲贊 234 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章