在Android开发中,梳理完需求后,要做的并不是马上写下你的第一行代码,而是需要先设计好整个项目的技术框架,本文主要通过登录实例和Google官方实例讲解MVP模式。
模块化功能
使得程序模块化,即:内部的高聚合、模块之间的低耦合提高开发效率,使各模块各司其职,提高代码的阅读性、可扩展性、可维护性以及安全性。
提高开发效率
可实现多人协同开发同一模块,设计好接口后,开发人员只需专注于某一点(视图显示、业务逻辑 / 数据处理)。
提高测试效率
方便测试,可以对各模块进行单独测试,不需要必须要将所有模块完成后才可测试。
提高代码的可维护性
可以快速的定位问题属于哪一模块,进行更改,更改当前模块尽可能少的影响其余功能,减少并发bug的出现,减少测试的工作量。
MVP的全称为Model-View-Presenter模式,这种模式用于应用程序的分层开发。
Model(模型)
模型是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据和业务逻辑(例如:从网络服务器获取数据并处理),在数据变化时更新控制器。
View(视图) -
视图是应用程序中处理界面显示的部分,包含的界面内容的更新和获取,用户触发事件的处理。
Presenter(控制器)
控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
M调用P,P调用M,M返回结果给P,P将结果返回给V,其中V不能直接使用M,使界面和业务逻辑或数据处理完全分离开来。
登录功能实现界面输入用户名和密码,然后与模拟从服务器获取到的用户名对比后,返回登录结果给界面。
界面展示如下:
角色与类和接口的对应关系
public interface IUserLoginView {
void setSuccessInfo();
void setErrorInfo(int iErrorCode);
}
<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="案例说明:"
android:textColor="@android:color/holo_red_dark"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登录功能的MVPDemo\n1、具体分为3层View-Presenter-Model\n2、V-P层传数据采用函数的参数传参\n3、M层模拟从网络获取数据。\n4、正确用户名和密码分别为:mvp,123456"
android:textColor="@android:color/holo_red_dark"/>
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="mvp" />
<EditText
android:id="@+id/et_pwd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="123456" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="Login"
android:onClick="Login"/>
</LinearLayout>
public class LoginMVPDemoActivity extends AppCompatActivity implements IUserLoginView {
private EditText etUserName;
private EditText etPwd;
IUserLoginPresenter bUserLoginPresenter = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_mvpdemo);
etUserName = (EditText) findViewById(R.id.et_username);
etPwd = (EditText) findViewById(R.id.et_pwd);
bUserLoginPresenter = new UserLoginPresenter(this);
}
@Override
public void setSuccessInfo() {
Toast.makeText(this,"登录成功!",Toast.LENGTH_SHORT).show();
}
@Override
public void setErrorInfo(int iErrorCode) {
if(iErrorCode == 1){
Toast.makeText(this,"登录失败:用户名不存在或不正确",Toast.LENGTH_SHORT).show();
}else if(iErrorCode == 2){
Toast.makeText(this,"登录失败:密码不正确",Toast.LENGTH_SHORT).show();
}
}
public void Login(View v){
bUserLoginPresenter.login(etUserName.getText().toString(),
etPwd.getText().toString());
}
}
public interface IUserLoginPresenter {
void login(String username, String strPwd);
}
public interface OnUserLoginListener {
void onUserLoginSuccess();
void onUserLoginError(int iErrorCode);
}
public class UserLoginPresenter implements IUserLoginPresenter,OnUserLoginListener{
IUserLoginMode bUserLoginMode = null;
IUserLoginView bUserLoginView = null;
public UserLoginPresenter(IUserLoginView bUserLoginView){
bUserLoginMode = new UserLoginMode();
this.bUserLoginView = bUserLoginView;
}
@Override
public void onUserLoginSuccess() {
bUserLoginView.setSuccessInfo();
}
@Override
public void onUserLoginError(int iErrorCode) {
bUserLoginView.setErrorInfo(iErrorCode);
}
@Override
public void login(String username, String strPwd) {
bUserLoginMode.handleLogin(username,strPwd,this);
}
}
public interface IUserLoginMode {
void handleLogin(String username, String strPwd,
OnUserLoginListener bOnUserLoginListener);
}
public class UserLoginMode implements IUserLoginMode {
@Override
public void handleLogin(String username, String strPwd,
OnUserLoginListener bOnUserLoginListener) {
String s_username = "mvp";
String s_strPwd ="123456";
//用户名不存在
if(!username.equals(s_username)){
bOnUserLoginListener.onUserLoginError(1);
} else if(username.equals(s_username) && s_strPwd.equals(strPwd)){
bOnUserLoginListener.onUserLoginSuccess();
}else if(username.equals(s_username) && !s_strPwd.equals(strPwd)){
//密码不正确
bOnUserLoginListener.onUserLoginError(2);
}
}
}
官方demo实现了数据加载过程的处理,界面显示开始加载数据,数据加载中,数据加载完成在界面的展示。
界面展示如下:
官方demo与登录案例的区别
public interface BaseView<T> {
void setPresenter(T presenter);
}
public interface BasePresenter {
void start();
}
public interface UserInfoContract {
interface View extends BaseView<Presenter>{
void showLoading();//展示加载框
void dismissLoading();//取消加载框展示
void showUserInfo(UserInfoModel userInfoModel);//将网络请求得到的用户信息回调
String loadUserId();//假设接口请求需要一个userId
}
interface Presenter extends BasePresenter {
void loadUserInfo();
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="案例说明:"
android:textColor="@android:color/holo_red_dark"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Google官方提供的MVPDemo\n1、具体分为4层View-Crontract-Presenter-Model\n2、contract层处于V层和P层中间,实现了原本在V层和P层的两个接口,V层和P层的接口是在Contract中对应接口的基础上再抽象一层,
使V层和P层的耦合性更低,灵活性更高。\n3、V-P层传参采用接口函数传参\n4、此Demo重点在于V层和P层的分离,M层的较少。"
android:textColor="@android:color/holo_red_dark"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="Hello World!" />
<TextView
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<TextView
android:id="@+id/tv_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</LinearLayout>
public class GoogleMVPDemoActivity extends AppCompatActivity implements UserInfoContract.View {
private TextView tv_name;
private TextView tv_age;
private TextView tv_address;
private UserInfoContract.Presenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_google_mvpdemo);
tv_name = (TextView) findViewById(R.id.tv_name);
tv_age = (TextView) findViewById(R.id.tv_age);
tv_address = (TextView) findViewById(R.id.tv_address);
//通知P层要
new UserInfoPresenter(this);
presenter.start();
}
@Override
public void showLoading() {
Toast.makeText(this, "正在加载", Toast.LENGTH_LONG).show();
}
@Override
public void dismissLoading() {
Toast.makeText(this, "加载完成", Toast.LENGTH_LONG).show();
}
@Override
public void showUserInfo(UserInfoModel userInfoModel) {
if (userInfoModel != null) {
tv_name.setText(userInfoModel.getName());
tv_age.setText(String.valueOf(userInfoModel.getAge()));
tv_address.setText(userInfoModel.getAddress());
}
}
@Override
public String loadUserId() {
return "1000";//假设需要查询的用户信息的userId是1000
}
@Override
public void setPresenter(UserInfoContract.Presenter presenter) {
this.presenter = presenter;
}
}
ublic class UserInfoPresenter implements UserInfoContract.Presenter {
private UserInfoContract.View view;
public UserInfoPresenter(UserInfoContract.View view) {
this.view = view;
view.setPresenter(this);
}
@Override
public void loadUserInfo() {
String userId = view.loadUserId();
view.showLoading();//接口请求前显示loading
//这里模拟接口请求回调-
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//模拟接口返回的json,并转换为javaBean
UserInfoModel userInfoModel = new UserInfoModel("小宝", 1, "杭州");
view.showUserInfo(userInfoModel);
view.dismissLoading();
}
}, 3000);
}
@Override
public void start() {
loadUserInfo();
}
}
public class UserInfoModel {
private String name;
private int age;
private String address;
public UserInfoModel(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
分析代码时注意两个案例mvp间对象的持有,对象的传递和传递数据的方式(方法传参和方法参数传参)
MVPDemo 下载地址
图解MVC、MVP、MVVM模式:实例讲解让你知道得明明白白