完整代码: https://github.com/fenggit/Lesson01-mvc-mvp-mvvm效果图:
一. Android中的MVC模式
1. 对应关系
MVC | M | C | V |
全称 | Model | Controller | View |
含义 | 模型 | 控制层 | 界面 |
对应Android的模块 | 网络请求,数据库 | Activity、Fragment | 自定义View、layout布局(xml文件) |
2. 关系图
3. 登录模块常见写法
- 登录模块功能:
- 输入用户名和密码
- 校验用户是否输入用户名和密码
- 请求服务器
- 具体实现:
- 代码结构
- 具有实现
LoginActivity代码
public class LoginActivity extends AppCompatActivity {
private EditText mUserView;
private EditText mPasswordView;
private Button mLoginView;
private ProgressDialog mProgressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mUserView = (EditText) findViewById(R.id.et_user);
mPasswordView = (EditText) findViewById(R.id.et_password);
mLoginView = (Button) findViewById(R.id.btn_login);
mLoginView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
}
/**
* 登录
*/
public void login() {
String user = mUserView.getText().toString();
String pwd = mPasswordView.getText().toString();
if (checkInput(user, pwd)) {
mProgressDialog = ProgressDialog.show(this, null, "正在登录");
// 模拟请求数据
new Thread(new Runnable() {
@Override
public void run() {
// 假设请求数据1秒
SystemClock.sleep(1000);
// 更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
mProgressDialog.dismiss();
Toast.makeText(getBaseContext(), "登录成功", Toast.LENGTH_SHORT).show();
}
});
}
}).start();
}
}
/**
* 检查输入是否合法
*
* @param user
* @param pwd
* @return
*/
public boolean checkInput(String user, String pwd) {
if (TextUtils.isEmpty(user)) {
Toast.makeText(this, "请输入用户名", Toast.LENGTH_SHORT).show();
return false;
}
if (TextUtils.isEmpty(pwd)) {
Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
}
布局代码activity_login.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="16dp"
tools:context="com.fenggit.lesson01.login.LoginActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用户名:" />
<EditText
android:id="@+id/et_user"
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="请输入用户名" />
</LinearLayout>
<LinearLayout
android:id="@+id/email_login_form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码:" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="请输入密码"
android:inputType="textPassword" />
</LinearLayout>
<Button
android:id="@+id/btn_login"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="登录"
android:textAllCaps="false"
android:textStyle="bold" />
</LinearLayout>
常规实现的问题
Activity类包含内容:网络请求+业务相关+界面相关
导致Activity类的庞大的,耦合度高,不利于扩展
4. 通过MVC模式改造登录模块
代码结构
剥离Activity中的网络请求模块到Model中,减轻Activity类的负担,这样Model层需要变更不需要变更Activity类
具有实现
将常规实现login()
的实现更改为:
/**
* 登录
*/
public void login() {
String user = mUserView.getText().toString();
String pwd = mPasswordView.getText().toString();
if (checkInput(user, pwd)) {
mProgressDialog = ProgressDialog.show(this, null, "正在登录");
// 使用model层
mLoginRequest.request(user, pwd, new LoginRequest.ResultListener() {
@Override
public void onSuccess() {
// 更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
mProgressDialog.dismiss();
Toast.makeText(getBaseContext(), "登录成功", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onFail() {
}
});
}
}
添加model模块,LoginRequest 请求数据类
public class LoginRequest {
/**
* 模拟请求网络数据
*
* @param user
* @param pwd
* @param resultListener
* @return
*/
public boolean request(final String user, final String pwd, final ResultListener resultListener) {
// 模拟请求数据
new Thread(new Runnable() {
@Override
public void run() {
// 假设请求数据1秒
SystemClock.sleep(1000);
Log.e("LoginRequest", "请求数据:" + user + "," + pwd);
if (resultListener != null) {
// 密码和用户是123 则登录成功
if("123".equals(user)&&"123".equals(pwd)){
resultListener.onSuccess();
}else{
resultListener.onFail();
}
}
}
}).start();
return true;
}
public interface ResultListener {
public void onSuccess();
public void onFail();
}
}
而view
模块可以自定义view,而Android中layout中xml就是view模块
MVC实现的问题
Activity类包含内容:业务相关+界面相关
总体来说减轻了Activity的职责,但是业务和界面还是在Activity模块中;
解决方式:
如果将Activity中的业务拆分–>MVP
如果将Activity中的界面控件拆分–>MVVM
二. Android中的MVP模式
1. 对应关系
MVP | M | V | P |
全称 | Model | View | Presenter |
含义 | 模型 | 界面 | 连接View和Model的桥梁 |
对应Android模块 | 网络请求,数据库 | 自定义View、layout布局(xml文件) | 业务逻辑 |
Presenter链接View和Model,使用接口进行隔离Activity业务和View.
2. 关系图
与MVC明显的特点就是View和Model不可以直接通信
3. 通过MVP模式改造登录模块
代码结构
其中:
LoginRequest
为Model层,主要是网络请求相关;
LoginPresenter
为Presenter层,主要是登录模块业务层
ILoginView
为View层,主要是关联业务和UI操作,传递接口而不传递Activity和Fragment主要是为了通用性,Presenter业务操作完需要更新UI。
LoginActivity
为纯界面相关操作,实现ILoginView
接口
具有实现
其中LoginRequest
和上面一样,Model不变;
添加Presenter层LoginPresenter
业务层
public class LoginPresenter {
private Context context;
private LoginRequest mLoginRequest;
// 这里传入接口,主要为了通用性,界面可能是Activity和Fragment
private ILoginView mView;
public LoginPresenter(Context context, ILoginView view) {
this.context = context;
this.mView = view;
this.mLoginRequest = new LoginRequest();
}
/**
* 登录
*
* @param user
* @param pwd
*/
public void login(String user, String pwd) {
mLoginRequest.request(user, pwd, new LoginRequest.ResultListener() {
@Override
public void onSuccess() {
// 登录成功,新UI
mView.success();
}
@Override
public void onFail() {
// 登录失败,新UI
mView.fail();
}
});
}
/**
* 检查输入是否合法
*
* @param user
* @param pwd
* @return
*/
public boolean checkInput(String user, String pwd) {
if (TextUtils.isEmpty(user)) {
Toast.makeText(context, "请输入用户名", Toast.LENGTH_SHORT).show();
return false;
}
if (TextUtils.isEmpty(pwd)) {
Toast.makeText(context, "请输入密码", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
}
添加View层接口ILoginView
public interface ILoginView {
/**
* 登录成功
*/
void success();
/**
* 登录失败
*/
void fail();
}
更改LoginActivity
代码,此类只保留UI相关操作
public class LoginActivity extends AppCompatActivity implements ILoginView {
private EditText mUserView;
private EditText mPasswordView;
private Button mLoginView;
private ProgressDialog mProgressDialog;
private LoginPresenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mUserView = (EditText) findViewById(R.id.et_user);
mPasswordView = (EditText) findViewById(R.id.et_password);
mLoginView = (Button) findViewById(R.id.btn_login);
// 第一个是Context对象,第二个是ILoginView
mLoginPresenter = new LoginPresenter(this, this);
mLoginView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
}
/**
* 登录
*/
public void login() {
String user = mUserView.getText().toString();
String pwd = mPasswordView.getText().toString();
if (mLoginPresenter.checkInput(user, pwd)) {
mProgressDialog = ProgressDialog.show(this, null, "正在登录");
mLoginPresenter.login(user, pwd);
}
}
public void success() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mProgressDialog.dismiss();
Toast.makeText(getBaseContext(), "登录成功", Toast.LENGTH_SHORT).show();
}
});
}
public void fail() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mProgressDialog.dismiss();
Toast.makeText(getBaseContext(), "登录失败", Toast.LENGTH_SHORT).show();
}
});
}
}
MVP实现的问题
主要就是分解Activity职责,原本Activity包含:网络请求+业务相关+界面相关等三个模块,现在完全解耦合;网络请求数据由model层处理,业务相关由presenter处理,而界面View层由Activity处理UI和layout中xml布局;
三. Android中的MVVM模式
未完待续,见下一篇博客