最近感觉自己的代码耦合性太高,打算重构一下自己代码的时候,于是花了两天的时间研究了一下MVP模式在Android中的应用。这里特地的记录一下所学。
什么叫做MVP模式?MVP模式的原理?这些这里推荐一片博文:http://www.jianshu.com/p/9a6845b26856写的很好,同时里面也记录了一些实现MVP模式的其他思路。
因为在Android中,我自己编程大多数使用的是Model-View模式,就是在Activity以及Fragment的生命周期中实现自己的功能,所以对于我们来说,Activity以及Fragment更多的承载了在传统MVC模式钟View以及Controller的功能。
而在Android开发中使用MVP模式,一种是将Activity作为View,自己创建Presenter来剥离逻辑以及功能。Presenter实现功能,View提供UI变化。
另外一种就是将Activity作为Presenter,具体实现思路有几种在 http://www.jianshu.com/p/9a6845b26856中有介绍。
除了MVP模式,Android开发中听说最近流行的是MVVM模式,后续如果有时间会研究一下!
Google官方也提供了MVP模式的Demo供大家学习,源码地址为:https://github.com/googlesamples/android-architecture ,其中的主要思路也是将Fragment作为View,同时书写Presenter。
这里借鉴Google思路,我尝试将自己目前进行的公司的项目中的登录模块改造为MVP模式,只不过是以Activity作为View。关于Google的Demo详细分析见:http://blog.youkuaiyun.com/u011459799/article/details/51360882"
首先,创建基本的View与Presenter,这里参考Google的Demo,如下:
public interface BasePresenter {
//使用于Activity OnResume,用于恢复现场
<span style="white-space:pre"> </span>void start();
}
public interface BaseView<T> {
//用于传递给View Presenter来操作(这么设计是有时候把Fragment作为View时)
<span style="white-space:pre"> </span>void setPresenter(T presenter);
}
这里的BaseView中的setPresenter函数实际上是为了关联View和Presenter,因为在Google的Demo中,Presenter是在Activity中声明,然后Fragment作为View直接在Activty中直接设置了Presenter,在自己的例子中并不需要。
接下来,然后创建View以及Presenter实际接口,View提供UI变化接口,Presenter提供对View的操作以及功能的具体实现,如下:
public class LoginContract {
interface View extends BaseView<Presenter> {
/**
* 显示Loading对话框
*/
void showLoading();
void dismissLoading();
/**
* 设置密码
* @param password
*/
void setPassword(String password);
/**
* 设置用户名
* @param userName
*/
void setUserName(String userName);
/**
* 设置用户名Edit的光标位置
* @param index
*/
void setUserNameSelection(int index);
void setPasswordSelection(int index);
String getUserName();
String getPassword();
RemoteDealEnsurenPopupWindow getThirdEnsureWindow();
void setThirdEnsureWindow(RemoteDealEnsurenPopupWindow thirdEnsureWindow);
/**
* 显示第三方登录授权结果对话框
*/
void showThirdEnsureWindow();
void dismissThirdEnsureWindow();
}
interface Presenter extends BasePresenter {
/**
* 利用用户名登录
*/
void userNameLogin();
/**
* qq第三方登录
*/
void qqLogin();
void wxLogin();
void sinaLogin();
/**
* 忘记密码
*/
void forgetPassword();
/**
* 注册新用户
*/
void register();
/**
* 初始化UserName以及Password(账户密码存储)
*/
void initLoginInfo();
/**
* 配置友盟第三方登录
*/
void initUmengLogin();
void umengAuthActivityResult(int requestCode, int resultCode, Intent data);
}
}
如上完成接口的定义,然后在就是对Presenter以及View接口的具体实现。
Presenter实现:
public class LoginPresenter implements LoginContract.Presenter {
/**
* persenter初始化就是获取到View来操作View <br>
* 同时将presenter传递给View<br>
* 建立presenter与View的联系<br>
*
* @param rootView
*/
public LoginPresenter(LoginContract.View rootView) {
//获取View
if (rootView instanceof LoginActivity)
mRootView = (LoginActivity) rootView;
//关联View和Presenter
mRootView.setPresenter(this);
}
/**
* 用户名密码输入
*/
@Override
public void userNameLogin() {
这里在Presenter的构造函数中获取到View,并且给View设置Presenter(本例中其实没必要)
接下来是View的实现,基本上是在Activity中完成简单UI操作封装,如下:
package cn.com.egova.securities_police.ui.Login;
import android.content.Intent;
import android.os.Bundle;
import android.text.InputType;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import cn.com.egova.securities_police.R;
import cn.com.egova.securities_police.ui.BaseActivity;
import cn.com.egova.securities_police.ui.widget.CustomImageEditText;
import cn.com.egova.securities_police.ui.widget.ProgressDialog;
import cn.com.egova.securities_police.ui.widget.RemoteDealEnsurenPopupWindow;
public class LoginActivity extends BaseActivity implements View.OnClickListener, LoginContract.View {
public static final String TAG = "LoginActivity";
//UI
private TextView mRegisterText;
private TextView mPasswordForgetText;
private Button mLoginBtn;
private CustomImageEditText mPhoneNoEdit;
private CustomImageEditText mPasswordEdit;
private ProgressDialog mProgressDialog;
private ImageView mQqLoginImg;
private ImageView mWcLoginImg;
private ImageView mAilpayLoginImg;
private View mContianer;
private RemoteDealEnsurenPopupWindow mThirdLoginEnsureWindow;
//MVP Presenter
private LoginContract.Presenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_login);
//申明Presenter
new LoginPresenter(this);
//申明View
initView();
//初始化Umeng登录
mLoginPresenter.initUmengLogin();
}
public void initView() {
//控件优化
mContianer = findViewById(R.id.login_activity_container);
mRegisterText = (TextView) findViewById(R.id.login_activity_register_text);
mRegisterText.setOnClickListener(this);
mPasswordForgetText = (TextView) findViewById(R.id.login_activity_password_forget_text);
mPasswordForgetText.setOnClickListener(this);
mLoginBtn = (Button) findViewById(R.id.login_activity_login_btn);
mLoginBtn.setOnClickListener(this);
mPhoneNoEdit = (CustomImageEditText) findViewById(R.id.login_activity_phone_number_edit);
mPasswordEdit = (CustomImageEditText) findViewById(R.id.login_activity_password_edit);
mPhoneNoEdit.getEditText().setInputType(InputType.TYPE_CLASS_TEXT);
mPasswordEdit.getEditText().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
mPhoneNoEdit.getIcon().setImageResource(R.mipmap.edit_icon_phone_num);
mPasswordEdit.getIcon().setImageResource(R.mipmap.edit_icon_password);
mProgressDialog = new ProgressDialog(this);
//第三方登录
mQqLoginImg = (ImageView) findViewById(R.id.login_activity_qq_icon);
mWcLoginImg = (ImageView) findViewById(R.id.login_activity_wc_icon);
mAilpayLoginImg = (ImageView) findViewById(R.id.login_activity_alipay_icon);
mQqLoginImg.setOnClickListener(this);
mWcLoginImg.setOnClickListener(this);
mAilpayLoginImg.setOnClickListener(this);
//初始化登录信息
mLoginPresenter.initLoginInfo();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.login_activity_register_text:
mLoginPresenter.register();
break;
case R.id.login_activity_password_forget_text:
mLoginPresenter.forgetPassword();
break;
case R.id.login_activity_login_btn:
mLoginPresenter.userNameLogin();
break;
case R.id.login_activity_qq_icon:
//友盟QQ登陆
mLoginPresenter.qqLogin();
break;
case R.id.login_activity_wc_icon:
//跳转到微信授权界面
mLoginPresenter.wxLogin();
break;
case R.id.login_activity_alipay_icon:
//新浪微博登陆
mLoginPresenter.sinaLogin();
break;
default:
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//友盟QQ授权登陆
mLoginPresenter.umengAuthActivityResult(requestCode, resultCode, data);
}
@Override
public void onResume() {
super.onResume();
mLoginPresenter.start();
}
@Override
public void showLoading() {
mProgressDialog.show();
}
@Override
public void dismissLoading() {
mProgressDialog.dismiss();
}
@Override
public void setPassword(String password) {
mPasswordEdit.getEditText().setText(password);
}
@Override
public void setUserName(String userName) {
mPhoneNoEdit.getEditText().setText(userName);
}
@Override
public void setUserNameSelection(int index) {
mPhoneNoEdit.getEditText().setSelection(index);
}
@Override
public void setPasswordSelection(int index) {
mPasswordEdit.getEditText().setSelection(index);
}
@Override
public String getUserName() {
return String.valueOf(mPhoneNoEdit.getEditText().getText());
}
@Override
public String getPassword() {
return String.valueOf(mPasswordEdit.getEditText().getText());
}
@Override
public RemoteDealEnsurenPopupWindow getThirdEnsureWindow() {
return mThirdLoginEnsureWindow;
}
@Override
public void setThirdEnsureWindow(RemoteDealEnsurenPopupWindow thirdEnsureWindow) {
mThirdLoginEnsureWindow = thirdEnsureWindow;
}
@Override
public void showThirdEnsureWindow() {
if (mThirdLoginEnsureWindow != null) {
mThirdLoginEnsureWindow.showPopupWindow(mContianer);
}
}
@Override
public void dismissThirdEnsureWindow() {
if (mThirdLoginEnsureWindow != null) {
mThirdLoginEnsureWindow.dismiss();
}
}
@Override
public void setPresenter(LoginContract.Presenter presenter) {
mLoginPresenter = presenter;
}
}
这样,基本上就将所有功能实现抽离在Presenter中了,比起原来臃肿的Activity,现在看起来也舒服多了。
目前,这也是简单的研究了一下MVP模式的使用,想要大范围的使用肯定还会遇到一些问题,后续会专门进行讲解。
附上例子的部分代码 :