Introduction to this article
In Android development, after sorting out the requirements, what you need to do is not to write your first line of code immediately, but to design the technical framework of the entire project first. This article mainly explains the MVP model through login examples and Google official examples.
Table of contents
- Why carry out technical framework design?
- MVP model
- Login example to explain MVP model
- Google official examples explain MVP model
- Source code download address
Why carry out technical framework design?
-
Modular functionality
Make the program modular, that is: high internal aggregation and low coupling between modules improve development efficiency, enable each module to perform its own duties, and improve the readability, scalability, maintainability and security of the code.
-
Improve development efficiency
It allows multiple people to collaboratively develop the same module. After designing the interface, developers only need to focus on a certain point (view display, business logic/data processing).
-
Improve testing efficiency
It is convenient for testing. Each module can be tested individually. It is not necessary to complete all modules before testing.
-
Improve code maintainability
You can quickly locate which module the problem belongs to, make changes, and change the current module as little as possible to affect other functions, reduce the occurrence of concurrent bugs, and reduce the workload of testing.
Remember: don’t design for the sake of design, otherwise it will increase the amount of development.
MVP model
definition
MVP stands for Model-View-Presenter mode, which is used for layered development of applications.
-
Model (Model)
The model is the part of the application that is used to process application data logic. Usually the model object is responsible for accessing data and business logic in the database (for example: getting data from a network server and processing it), and updating controls when the data changes. device. -
View -
View is the part of the application that handles interface display, including updating and retrieving interface content, and processing of user-triggered events. -
Presenter (Controller)
Controller acts on models and views. It controls the flow of data to model objects and updates the view when the data changes. It separates the view from the model.
Calling relationship between MVPs
M calls P, P calls M, M returns the result to P, and P returns the result to V. V cannot use M directly, completely separating the interface from business logic or data processing.
MVP mode execution sequence
The execution sequence is explained in 3 situations. See the figure below for details.
Role description of MVP in android project
actual case
Login function implemented in MVP mode
The login function implementation interface enters the user name and password, and then compares it with the simulated user name obtained from the server, and then returns the login result to the interface.
The interface is shown as follows:
the corresponding relationship between roles, classes and interfaces
function call procedure
code
- View interface
public interface IUserLoginView {
void setSuccessInfo();
void setErrorInfo(int iErrorCode);
}
- View XML file
<?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>
- View-Activity
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());
}
}
- Presenter interface-IUserLoginPresenter
public interface IUserLoginPresenter {
void login(String username, String strPwd);
}
- Presenter interface-OnUserLoginListener
public interface OnUserLoginListener {
void onUserLoginSuccess();
void onUserLoginError(int iErrorCode);
}
- Presenter concrete class
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);
}
}
- Model interface
public interface IUserLoginMode {
void handleLogin(String username, String strPwd,
OnUserLoginListener bOnUserLoginListener);
}
- Model implementation class
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);
}
}
}
Google official MVPDemo
The official demo implements the processing of the data loading process. The interface shows that the data has started to be loaded, the data is loading, and the data loading is completed and displayed on the interface.
The interface is displayed as follows:
The difference between the official demo and the login case
- The model does not abstract the interface and directly implements it concretely.
- Add a contract between v and p. The original interfaces of v and p are classified into the two interfaces of contact. Based on the v and p interfaces, the basic v interface and p interface are abstracted to make the coupling between v and p lower. , increasing the scalability and maintainability of the program.
Correspondence between roles, interfaces and classes
Function calling process
- View interface
public interface BaseView<T> {
void setPresenter(T presenter);
}
- Presenter interface
public interface BasePresenter {
void start();
}
- Contract interface
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();
}
}
- View-XML
<?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>
- View-Activity
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;
}
}
- Presenter concrete class
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();
}
}
- Model concrete class
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;
}
}
Summarize
When analyzing the code, pay attention to the holding of objects between the two case MVPs, the transfer of objects and the way of transferring data (method parameter passing and method parameter passing)
Case download address
Reference link
Illustrated MVC, MVP, MVVM patterns: examples to explain so you know clearly