Android MVP

Android MVP架構和MVC架構比較 
代碼示例請點擊點擊下載demo 
1.概述 如題,本文想要討論的是MVP與MVC之間的比較,那麼在這之前,我們首先來回顧一下MVC的概念.MVC我們再熟悉不過,即Model-View-Controllor,對應於Android項目結構如下

             - Model對應於業務邏輯和實體類
             - View對應於xml佈局文件
             - Controller對應於Activity
  • 1
  • 2
  • 3
  • 4

這裏我們的View佈局文件只做了界面的展示工作,做的工作其實並不多,當然現在Android有了MVVM來解決這個問題,讓View變的更加強大,我們這裏暫且不做討論.而界面的大部分顯示邏輯和數據處理邏輯都是在Activity中完成的(類似於ios中的ViewControllor類),這就使我們的Activity即像Controllor又像View,導致Activity中的代碼變的異常複雜,我本人以前接手的一個項目MainActivity中有超過3000行的代碼,導致後期根本無法維護,只能重寫來解決.

而隨着MVP架構的出現,我們可以將以往在Activity中完成的數據展示邏輯到Presenter中完成,對應的架構就變成了下邊這樣

             - Model不變對應於業務邏輯和實體類
             - View對應於Activity,負責界面的展示
             - Presenter,負責業務邏輯處理,來連接Model和View
  • 1
  • 2
  • 3
  • 4

2.MVP與MVC對比 
下邊用一張圖來更清晰的對比兩種邏輯 
MVP與MVC對比 
可以看到,二者最明顯的區別就是,MVC允許View和Model進行數據交互,而MVP則是把View和Model的交互交給Presenter來完成,這樣就使得Activity中的變的更輕,代碼更加清爽簡潔,耦合度也更低,便於後期的維護. 
通過代碼來展示二者在設計上的一些區別 
3.代碼設計 
1.包結構 這裏通過一個模擬登錄的例子來演示二者在設計上的異同,首先我們設計一下程序的包結構,一個好的包結構是程序的基石.貼出一張圖來展示一下我的Demo程序包結構 
MVP Demo包結構 
是的,看上去很簡單,就是model-view-presenter,讓人一目瞭然.接下來我們來分析一下具體的業務邏輯,並且來編寫代碼. 
1.Action層 首先是實體類,這個必不可少,不做討論

/**
 * 登錄返回用戶信息
 * Created by shidong on 15/11/26.
 */
public class UserInfo implements Serializable {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 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

2.Action中的登錄操作,我們就叫login()方法

/**
 * 具體實現登錄操作(項目中一般是網絡請求返回數據)
 * Created by shidong on 15/11/26.
 */
public class LoginAction {
    public UserInfo login() {
        UserInfo userInfo = new UserInfo();
        userInfo.setName("dong");
        userInfo.setAge(20);
        return userInfo;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

好了,到這裏我們的action層已經編寫完了,跟以往的寫法沒有半點區別. 
2.Presenter層 
可以試想一下,我們的登錄操作通常會有什麼呢,無非就是在異步線程中請求登錄操作,即上邊的LoginAction,然後處理登錄成功和失敗的邏輯.那麼好,我們的登錄功能接口定義大概就是下邊這樣子

/**
 * 登錄接口
 * Created by shidong on 15/11/26.
 */
public interface OnLoginListener {
    //登錄成功
    public void loginSuccess(UserInfo userInfo);
    //登錄失敗
    public void loginFailure();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

爲了演示方便,暫且只定義這兩個方法. 
現在應該設計我們的Presenter類了,嗯,很顯然,我們需要有一個login()方法,這個login調用LoginAction中的方法完成登錄請求.應該是這樣的

 public void login() {
        request(REQ_LOGIN);
    }
  • 1
  • 2
  • 3

嗯?request()方法是什麼東東,好了,接下來繼續分析.別忘了,這些請求一定是在異步線程中實現的,耗時操作都是需要這個異步請求操作的,Android已經爲我們提供了AsyncTask,我們來對其進一步封裝.既然是公用的,我們需要定義一個接口來完成上述操作,接口中方法可以仿照AsyncTask類設計如下

/**
 * [異步請求監聽]
 * 
 * @author mashidong
 * @version V3.6.0
 * @date 2015-11-25
 * 
 **/
public interface OnDataListener {

    /**
     * 耗時操作將在該方法中實現,比如髮網絡請求等
     * @param requestCode 請求code
     * @return
     * @throws HttpException
     */
    public Object doInBackground(int requestCode) throws HttpException;

    /**
     * 請求成功回調方法
     * @param requestCode 請求code
     * @param result 結果
     */
    public void onSuccess(int requestCode, Object result);

    /**
     * 請求失敗回調方法
     * @param requestCode 請求code
     * @param state 狀態
     * @param result 結果
     */
    public void onFailure(int requestCode, int state, Object result);
}
  • 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

之後我們的每個Presenter都需要實現這個接口來完成異步請求任務,誰都不想每次都去寫重複的代碼,顯然我們需要定義一個BasePresenter來完成這些操作.

package com.example.shidong.androidmvp.presenter;

import com.example.shidong.DemoApplication;
import com.example.shidong.network.async.AsyncTaskManager;
import com.example.shidong.network.async.OnDataListener;
import com.example.shidong.network.http.HttpException;
import com.example.shidong.network.utils.NToast;

/**
 * Presenter 基礎類,提供異步請求,設置監聽器方法等
 * <p>
 * Created by shidong on 15/11/26.
 */
public abstract class BasePresenter<T> implements OnDataListener {
    private AsyncTaskManager mAsyncTaskManager;

    public BasePresenter() {
        //構造方法中初始化AsyncTaskManager
        this.mAsyncTaskManager = AsyncTaskManager.getInstance(DemoApplication.application);
    }

    /**
     * 發送請求,默認是需要檢查網絡的
     *
     * @param requsetCode 請求code
     */
    public void request(int requsetCode) {
        request(requsetCode, true);
    }

    /**
     * 發送請求
     *
     * @param requsetCode    請求code
     * @param isCheckNetwork 是否需要檢查網絡, true需要,false不需要
     */
    public void request(int requsetCode, boolean isCheckNetwork) {
        mAsyncTaskManager.request(requsetCode, isCheckNetwork, this);
    }

    /**
     * 取消請求
     *
     * @param requsetCode
     */
    public void cancelRequest(int requsetCode) {
        mAsyncTaskManager.cancelRequest(requsetCode);
    }

    /**
     * 取消所有請求
     */
    public void cancelRequest() {
        mAsyncTaskManager.cancelRequest();
    }

    /**
     * 設置listener
     *
     * @param listener
     */
    public abstract void setListener(T listener);


    @Override
    public Object doInBackground(int requestCode) throws HttpException {
        return null;
    }

    @Override
    public void onSuccess(int requestCode, Object result) {

    }

    @Override
    public void onFailure(int requestCode, int state, Object result) {
        switch (state) {
            // 網絡不可用給出提示
            case AsyncTaskManager.HTTP_NULL_CODE:
                NToast.shortToast(DemoApplication.application, "網絡不可用");
                break;
            // 網絡有問題給出提示
            case AsyncTaskManager.REQUEST_ERROR_CODE:
                break;
            case AsyncTaskManager.HTTP_ERROR_CODE:
                NToast.longToast(DemoApplication.application, "網絡連接錯誤");
                break;
        }
    }
}
  • 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

其中我們定義一個AsyncTaskManager來對異步請求進行管理.具體見代碼


package com.example.shidong.network.async;

import android.content.Context;
import android.os.Build;

import com.example.shidong.network.utils.NLog;

import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * 異步框架類,該類主要實現將耗時操作放在異步線程裏面操作,如頁面發送請求等。
 * <p/>
 * 發生請求成功: REQUEST_SUCCESS_CODE = 200
 * 發生請求失敗: REQUEST_ERROR_CODE = -999
 * 網絡有問題: HTTP_ERROR_CODE = -200
 * 網絡不可用: HTTP_NULL_CODE = -400
 *
 * @author mashidong
 * @version V3.6.0
 * @date 2015-11-25
 **/
public class AsyncTaskManager {

    /**
     * 日誌對象
     **/
    private final String tag = AsyncTaskManager.class.getSimpleName();

    /**
     * 發生請求成功
     **/
    public static final int REQUEST_SUCCESS_CODE = 200;
    /**
     * 發生請求失敗
     **/
    public static final int REQUEST_ERROR_CODE = -999;
    /**
     * 網絡有問題
     **/
    public static final int HTTP_ERROR_CODE = -200;
    /**
     * 網絡不可用
     **/
    public static final int HTTP_NULL_CODE = -400;

    /**
     * 默認下載請求碼
     **/
    public static final int DEFAULT_DOWNLOAD_CODE = 10000;

    /**
     * 線程池最多線程數
     **/
    public final int MAX_CONNECTIONS_NUM = 10;

    private Context mContext;
    private static AsyncTaskManager instance;
    private static ExecutorService mExecutorService;
    private static Map<Integer, WeakReference<BaseAsyncTask>> requestMap;


    /**
     * 構造方法
     *
     * @param context
     */
    private AsyncTaskManager(Context context) {
        mContext = context;
        mExecutorService = Executors.newFixedThreadPool(MAX_CONNECTIONS_NUM);
        requestMap = new WeakHashMap<Integer, WeakReference<BaseAsyncTask>>();
    }

    /**
     * 單例模式得到AsyncTaskManager實例對象
     *
     * @param context
     * @return
     */
    public static AsyncTaskManager getInstance(Context context) {
        if (instance == null) {
            synchronized (AsyncTaskManager.class) {
                if (instance == null) {
                    instance = new AsyncTaskManager(context);
                }
            }
        }
        return instance;
    }

    /**
     * 發送請求, 默認是需要檢查網絡的
     *
     * @param requestCode 請求code
     * @param listener回調
     */
    public void request(int requestCode, OnDataListener listener) {
        request(requestCode, true, listener);
    }

    /**
     * 發送請求
     *
     * @param requestCode    請求code
     * @param isCheckNetwork 是否需要檢查網絡
     * @param listener       回調
     */
    public void request(int requestCode, boolean isCheckNetwork, OnDataListener listener) {
        DownLoad bean = new DownLoad(requestCode, isCheckNetwork, listener);
        if (requestCode > 0) {
            BaseAsyncTask mAsynctask = new BaseAsyncTask(bean, mContext);
            //after version 2.3 added executeOnExecutor method.
            //before 2.3 only run five asyntask, more than five must wait
            if (Build.VERSION.SDK_INT >= 11) {
                mAsynctask.executeOnExecutor(mExecutorService);
            } else {
                mAsynctask.execute();
            }
            requestMap.put(requestCode, new WeakReference<BaseAsyncTask>(mAsynctask));
        } else {
            NLog.e(tag, "the error is requestCode < 0");
        }
    }

    /**
     * 根據requestCode取消請求
     *
     * @param requestCode
     */
    public void cancelRequest(int requestCode) {
        WeakReference<BaseAsyncTask> requestTask = requestMap.get(requestCode);
        if (requestTask != null) {
            BaseAsyncTask request = requestTask.get();
            if (request != null) {
                request.cancel(true);
                request = null;
            }
        }
        requestMap.remove(requestCode);
    }


    /**
     * 取消所有請求
     */
    public void cancelRequest() {
        if (requestMap != null) {
            Iterator<Entry<Integer, WeakReference<BaseAsyncTask>>> it = requestMap.entrySet().iterator();
            while (it.hasNext()) {
                Entry<Integer, WeakReference<BaseAsyncTask>> entry = (Entry<Integer, WeakReference<BaseAsyncTask>>) it.next();
                Integer requestCode = entry.getKey();
                cancelRequest(requestCode);
            }
            requestMap.clear();
        }
    }

}
  • 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
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165

這回好了,異步請求方法有了,我們就大膽的在Presenter中完成我們的操作吧.

package com.example.shidong.androidmvp.presenter;

import com.example.shidong.androidmvp.presenter.impl.OnLoginListener;
import com.example.shidong.androidmvp.module.action.LoginAction;
import com.example.shidong.androidmvp.module.model.UserInfo;
import com.example.shidong.network.http.HttpException;

/**
 * 登錄Presenter 實現登錄邏輯
 * Created by shidong on 15/11/26.
 */
public class LoginPresenter extends BasePresenter<OnLoginListener> {

    private static final int REQ_LOGIN = 0x0a;
    private OnLoginListener loginListener;

    @Override
    public void setListener(OnLoginListener listener) {
        this.loginListener = listener;
    }

    public void login() {
        request(REQ_LOGIN);
    }

    @Override
    public Object doInBackground(int requestCode) throws HttpException {
        switch (requestCode) {
            case REQ_LOGIN:
                LoginAction action = new LoginAction();
                return action.login();
            default:
                return null;
        }
    }

    @Override
    public void onSuccess(int requestCode, Object result) {
        super.onSuccess(requestCode, result);
        switch (requestCode) {
            case REQ_LOGIN:
                if (result != null) {
                    UserInfo userInfo = (UserInfo) result;
                    if (loginListener != null) {
                        loginListener.loginSuccess(userInfo);
                    }
                }
            default:
                break;
        }
    }

    @Override
    public void onFailure(int requestCode, int state, Object result) {
        super.onFailure(requestCode, state, result);
    }
}
  • 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

ok,到這裏,我們所有的異步請求邏輯已經完成的,好像還少點什麼,不錯,最後要在我們的View中調用請求方法來完成顯示 
3.Activity設計

package com.example.shidong.androidmvp.view;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;

import com.example.shidong.androidmvp.R;
import com.example.shidong.androidmvp.presenter.impl.OnLoginListener;
import com.example.shidong.androidmvp.module.model.UserInfo;
import com.example.shidong.androidmvp.presenter.LoginPresenter;
import com.example.shidong.network.utils.NToast;

public class LoginActivity extends AppCompatActivity implements OnLoginListener {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_mvp);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        //MVP模式
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //登錄請求
                LoginPresenter presenter = new LoginPresenter();
                presenter.setListener(LoginActivity.this);
                presenter.login();
            }
        });

        //MVC模式
        findViewById(R.id.btn_test_mvc).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LoginActivity.this.startActivity(new Intent(LoginActivity.this, MVCLoginActivity.class));
            }
        });
    }

    //登錄成功
    @Override
    public void loginSuccess(UserInfo userInfo) {
        NToast.longToast(this, "登錄成功" + userInfo.toString());
    }

    @Override
    public void loginFailure() {
        NToast.longToast(this, "登錄失敗");
    }
}
  • 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

至此,我們的整個流程已經完成,Activity中只有區區幾十行代碼,處理幾個回調方法即可,是不是很清爽,而且所有的邏輯都做了很好的分層,便於代碼閱讀和後期維護.當然這種寫法帶來的代價就是會增加很多類和接口的定義,所以我本人建議適當使用,即能用則用,簡單的邏輯可以不用.比如上述登錄請求,直接用MVC來完成是下邊這樣

package com.example.shidong.androidmvp.view;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;

import com.example.shidong.androidmvp.R;
import com.example.shidong.androidmvp.module.action.LoginAction;
import com.example.shidong.androidmvp.module.model.UserInfo;
import com.example.shidong.network.async.AsyncTaskManager;
import com.example.shidong.network.async.OnDataListener;
import com.example.shidong.network.http.HttpException;
import com.example.shidong.network.utils.NToast;

public class MVCLoginActivity extends AppCompatActivity implements OnDataListener {

    private static final int REQ_MVCLOGIN = 0x0b;
    private AsyncTaskManager mAsyncTaskManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvclogin);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mAsyncTaskManager = AsyncTaskManager.getInstance(this);
        findViewById(R.id.btn_mvclogin).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                request(REQ_MVCLOGIN);
            }
        });

    }

    /**
     * 發送請求,默認是需要檢查網絡的
     *
     * @param requsetCode 請求code
     */
    public void request(int requsetCode) {
        request(requsetCode, true);
    }

    /**
     * 發送請求
     *
     * @param requsetCode    請求code
     * @param isCheckNetwork 是否需要檢查網絡, true需要,false不需要
     */
    public void request(int requsetCode, boolean isCheckNetwork) {
        mAsyncTaskManager.request(requsetCode, isCheckNetwork, this);
    }

    /**
     * 取消請求
     *
     * @param requsetCode
     */
    public void cancelRequest(int requsetCode) {
        mAsyncTaskManager.cancelRequest(requsetCode);
    }

    /**
     * 取消所有請求
     */
    public void cancelRequest() {
        mAsyncTaskManager.cancelRequest();
    }


    @Override
    public Object doInBackground(int requestCode) throws HttpException {
        LoginAction action = new LoginAction();
        return action.login();
    }

    @Override
    public void onSuccess(int requestCode, Object result) {
        UserInfo userInfo = (UserInfo) result;
        NToast.longToast(this, userInfo.toString());
    }

    @Override
    public void onFailure(int requestCode, int state, Object result) {

    }
}
  • 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

所有的請求和顯示邏輯在Activity中完成,目測比MVP寫法多了一倍的代碼,還好我們已經將異步請求邏輯封裝,否則可能會更多; 
這樣兩種方法我們都可以運用自如, 在適當的時候選擇適當的方法來完成.

好了,到這裏已經寫完我想要寫的東西,個人水平有限,可能理解不夠深刻,望多多討論和提出更好建議.最後感謝hongyang大神的文章,讓我進一步完善了項目總的請求封裝邏輯.

參考文章 http://blog.csdn.net/lmj623565791/article/details/46596109 
完整Demo代碼請點擊 http://download.csdn.net/detail/ronaldong99/9303337

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章