框架篇(一)-登录实例和Google官方实例讲解MVP模式

本文介绍

在Android开发中,梳理完需求后,要做的并不是马上写下你的第一行代码,而是需要先设计好整个项目的技术框架,本文主要通过登录实例和Google官方实例讲解MVP模式。

目录

  • 为什么要进行技术框架设计
  • MVP模式
  • 登录实例讲解MVP模式
  • Google官方实例讲解MVP模式
  • 源码下载地址

为什么要进行技术框架设计

  • 模块化功能

    使得程序模块化,即:内部的高聚合、模块之间的低耦合提高开发效率,使各模块各司其职,提高代码的阅读性、可扩展性、可维护性以及安全性。

  • 提高开发效率

    可实现多人协同开发同一模块,设计好接口后,开发人员只需专注于某一点(视图显示、业务逻辑 / 数据处理)。

  • 提高测试效率

    方便测试,可以对各模块进行单独测试,不需要必须要将所有模块完成后才可测试。

  • 提高代码的可维护性

    可以快速的定位问题属于哪一模块,进行更改,更改当前模块尽可能少的影响其余功能,减少并发bug的出现,减少测试的工作量。

切记:不要为了设计而设计,否则反而会提高开发量。
在这里插入图片描述

MVP模式

定义

MVP的全称为Model-View-Presenter模式,这种模式用于应用程序的分层开发。

  • Model(模型)
    模型是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据和业务逻辑(例如:从网络服务器获取数据并处理),在数据变化时更新控制器。

  • View(视图) -
    视图是应用程序中处理界面显示的部分,包含的界面内容的更新和获取,用户触发事件的处理。

  • Presenter(控制器)
    控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。

MVP间的调用关系

M调用P,P调用M,M返回结果给P,P将结果返回给V,其中V不能直接使用M,使界面和业务逻辑或数据处理完全分离开来。
在这里插入图片描述

MVP模式执行顺序

分3中情况讲解执行顺序,具体见下图。
在这里插入图片描述

android项目中MVP的角色说明

在这里插入图片描述

实际案例

MVP模式实现的登录功能

登录功能实现界面输入用户名和密码,然后与模拟从服务器获取到的用户名对比后,返回登录结果给界面。
界面展示如下:
在这里插入图片描述
角色与类和接口的对应关系
在这里插入图片描述

函数调用过程
在这里插入图片描述代码

  • View接口
public interface IUserLoginView {
    
    
     void setSuccessInfo();
     void setErrorInfo(int iErrorCode);
}
  • View XML文件
<?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接口-IUserLoginPresenter
public interface IUserLoginPresenter {
    
    
     void login(String username, String strPwd);
}
  • Presenter接口-OnUserLoginListener
public interface OnUserLoginListener {
    
    
     void onUserLoginSuccess();
     void onUserLoginError(int iErrorCode);
}
  • Presenter具体类
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接口
public interface IUserLoginMode {
    
    
     void handleLogin(String username, String strPwd,
                            OnUserLoginListener bOnUserLoginListener);
}
  • Model实现类
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官方MVPDemo

官方demo实现了数据加载过程的处理,界面显示开始加载数据,数据加载中,数据加载完成在界面的展示。
界面展示如下:
在这里插入图片描述
官方demo与登录案例的区别

  • model未抽象接口,直接具体实现。
  • 在v和p之间加入contract,原本v和p的接口归为contact的两个接口,在v和p接口的基础上在抽象出基础的v接口和p接口,使v和p的耦合更低,增加了程序的可扩展性和可维护性。

角色与接口和类的对应关系
在这里插入图片描述
函数调用过程
在这里插入图片描述

  • View接口
public interface BaseView<T> {
    
    
    void setPresenter(T presenter);
}
  • Presenter接口
public interface BasePresenter {
    
    
    void start();
}
  • Contract接口
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、具体分为4View-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具体类
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具体类
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模式:实例讲解让你知道得明明白白

猜你喜欢

转载自blog.csdn.net/li1500742101/article/details/105382015