之前一直聽說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源碼
歡迎批評指正!