之前一直听说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源码
欢迎批评指正!