MVP模式初探

之前一直聽說MVP的好處多多,也看過相關資料,但是沒有仔細研究過,今天看了看面試題,發現竟然讓用MVP模式實現登錄功能,於是抓緊研究了下。
關於MVP的介紹以及與MVC的區別網上有相當多的資料可以參考,這裏推薦鴻翔大神的博客
我這裏引用下MVP和MVC的區別,方便查看:

引用塊內容

其實最明顯的區別就是,MVC中是允許Model和View進行交互的,而MVP中很明顯,Model與View之間的交互由Presenter完成。還有一點就是Presenter與View之間的交互是通過接口的。

光看不練假把式,理解後那就寫一個demo吧。
先上效果圖:
這裏寫圖片描述

包結構如下:
包結構

說一說我的思路:
1、點擊LOGIN按鈕,會實現登錄的功能,所以需要聲明一個ILoginPresenter的接口,內置login方法,當然具體的實現需要一個LoginPresenterImpl類。
2、登錄失敗或成功後,需要給出提示,因此還需要定義一個ILoginView接口,內置loginFail和loginSuccess方法,而它的具體實現類便是這個Activity,同樣需要一個ILoginView的具體實現類。

再說一遍整體的流程:點擊login按鈕,activity調用presenter實現登錄功能,在presenter裏執行登錄邏輯,執行完畢登錄邏輯後,調用presenter裏的loginview(即登錄activity)的相關方法,完成UI的顯示。

相關代碼如下,進行了簡單的封裝,處理了內存泄漏的問題:

public interface ILoginView {//登錄view
    void loginSuccess();

    void loginFail();
}
public interface ILoginPresenter {
    void login(User user);
}
//所有Presenter的基類,簡單的封裝。
public class BasePresenter<T> {
    private static final String TAG = "BasePresenter";
    private WeakReference<T> mViewHolder;//利用弱引用解決內存泄漏的問題。

    public void attachView(T view) {//需要在Activity的onCreate或onResume方法裏調用
        mViewHolder = new WeakReference<>(view);
    }

    public T getView() {//必須attachView調用後才能調用此方法
        if (mViewHolder != null) {
            Log.w(TAG, "maybe do not call attachView().");
            return mViewHolder.get();
        }
        return null;
    }

    public void detachView() {//需要在Activity的onDestroy或onPause調用
        mViewHolder.clear();
        mViewHolder = null;
    }
}
//所有Activity都要繼承此類,簡單的封裝了下。
public abstract class BaseActivity<T, V extends BasePresenter<T>> extends AppCompatActivity {
    protected List<V> mPresenters = new ArrayList<>();//因爲一個Activity可能會有多個Presenter,因此這使用了一個鏈表用來存儲。

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPresenters();//onCreate方法裏添加Presenter,此方法需要在子類中實現。
        if (mPresenters != null) {
            for (V v : mPresenters) {
                v.attachView((T) this);
            }
        }
    }


    @Override
    protected void onDestroy() {//資源清理,防止內存泄漏。
        super.onDestroy();
        if (mPresenters != null) {
            for (V v : mPresenters) {
                v.detachView();
            }
            mPresenters.clear();
            mPresenters = null;
        }
    }

    /***
     * NOTE: 不要忘了在這個方法裏初始化 handler 和 presenter。
     * 子類中實現此方法。
     */
    public abstract void addPresenters();
}
//登錄代理的具體實現類。
public class LoginPresenterImp extends BasePresenter<MainActivity> implements ILoginPresenter {
    private volatile int mNum = 0;
    private ILoginView mLoginView;
    private Handler mHandler;

    public LoginPresenterImp(Handler handler) {
        mHandler = handler;
    }

    @Override
    public void login(User user) {
        mLoginView = getView();
        if (mLoginView != null) {
            new Thread(new Runnable() {
                @Override
                public void run() {//模擬子線程訪問網絡。
                    if (mNum % 2 == 0) {
                        mHandler.post(new Runnable() {//主線程中更新UI。
                            @Override
                            public void run() {
                                mLoginView.loginSuccess();
                            }
                        });
                    } else {
                        mHandler.post(new Runnable() {//主線程中更新UI。
                            @Override
                            public void run() {
                                mLoginView.loginFail();
                            }
                        });
                    }
                    mNum++;
                }
            }).start();
        }
    }
}
//實現BaseActivity,實現ILoginView接口
public class MainActivity extends BaseActivity<MainActivity, LoginPresenterImp>
        implements ILoginView {

    private EditText mEtUsername;
    private EditText mEtPassword;
    private Button mBtnLogin;

    private LoginPresenterImp mLoginPresenter;//presenter的引用
    private MyHandler mHandler;//handler爲了將UI操作切換到UI線程中來。

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    @Override
    public void addPresenters() {//處理presenter和handler
        mHandler = new MyHandler(this);
        mLoginPresenter = new LoginPresenterImp(mHandler);
        mPresenters.add(mLoginPresenter);
    }

    private void initView() {
        mEtUsername = (EditText) findViewById(R.id.main_et_username);
        mEtPassword = (EditText) findViewById(R.id.main_et_password);
        mBtnLogin = (Button) findViewById(R.id.main_btn_login);
        mBtnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String username = mEtUsername.getText().toString().trim();
                String password = mEtPassword.getText().toString().trim();
                if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
                    Toast.makeText(MainActivity.this, "must not empty", Toast.LENGTH_SHORT).show();
                    return;
                }
                User user = new User();
                user.setUsername(username);
                user.setPassword(password);
                mLoginPresenter.login(user);//調用presenter的login方法,將登錄邏輯從activity中解耦出去。
            }
        });
    }

    @Override
    public void loginSuccess() {//登錄成功後會調用
        Toast.makeText(this, "login success", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void loginFail() {//登錄失敗後會調用
        Toast.makeText(this, "login fail", Toast.LENGTH_SHORT).show();
    }

    private class MyHandler extends Handler {
        private WeakReference<MainActivity> mReference;

        public MyHandler(MainActivity activity) {
            mReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (mReference.get() != null) {
                switch (msg.what) {
                    default:
                        break;
                }
            }
        }
    }
}

github源碼
歡迎批評指正!

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