框架篇(一)-登录实例和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模式:实例讲解让你知道得明明白白

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的Android MVP架构模式实现的登录功能的示例代码: 1. 首先定义一个登录的接口 LoginContract,其中包括视图层、控制层和数据层的接口: ``` public interface LoginContract { interface View { void onLoginSuccess(); void onLoginFailure(String errorMessage); } interface Presenter { void login(String username, String password); } interface Model { void login(String username, String password, OnLoginFinishedListener listener); } interface OnLoginFinishedListener { void onLoginSuccess(); void onLoginFailure(String errorMessage); } } ``` 2. 在视图层,实现LoginContract.View接口,并创建Presenter对象: ``` public class LoginActivity extends AppCompatActivity implements LoginContract.View { private EditText mUsernameEditText; private EditText mPasswordEditText; private Button mLoginButton; private ProgressBar mProgressBar; private LoginContract.Presenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); mUsernameEditText = findViewById(R.id.username_edit_text); mPasswordEditText = findViewById(R.id.password_edit_text); mLoginButton = findViewById(R.id.login_button); mProgressBar = findViewById(R.id.progress_bar); mPresenter = new LoginPresenter(this); mLoginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String username = mUsernameEditText.getText().toString(); String password = mPasswordEditText.getText().toString(); mPresenter.login(username, password); } }); } @Override public void onLoginSuccess() { // 登录成功 mProgressBar.setVisibility(View.GONE); Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show(); Intent intent = new Intent(this, MainActivity.class); startActivity(intent); finish(); } @Override public void onLoginFailure(String errorMessage) { // 登录失败 mProgressBar.setVisibility(View.GONE); Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show(); } } ``` 3. 在控制层,实现LoginContract.Presenter接口,并通过构造函数将视图层和数据层传递进来: ``` public class LoginPresenter implements LoginContract.Presenter, LoginContract.OnLoginFinishedListener { private LoginContract.View mView; private LoginContract.Model mModel; public LoginPresenter(LoginContract.View view) { mView = view; mModel = new LoginModel(); } @Override public void login(String username, String password) { mView.showProgressBar(); mModel.login(username, password, this); } @Override public void onLoginSuccess() { mView.hideProgressBar(); mView.onLoginSuccess(); } @Override public void onLoginFailure(String errorMessage) { mView.hideProgressBar(); mView.onLoginFailure(errorMessage); } } ``` 4. 在数据层,实现LoginContract.Model接口,并在登录完成后将结果回调给Presenter: ``` public class LoginModel implements LoginContract.Model { @Override public void login(final String username, final String password, final LoginContract.OnLoginFinishedListener listener) { // 模拟登录过程,这里使用Handler模拟异步请求 new Handler().postDelayed(new Runnable() { @Override public void run() { if (username.equals("admin") && password.equals("123456")) { listener.onLoginSuccess(); } else { listener.onLoginFailure("用户名或密码错误"); } } }, 2000); } } ``` 这就是一个简单的Android MVP架构模式实现的登录功能的示例代码,通过这种架构模式,可以将代码分层,实现逻辑清晰,易于维护和测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值