用一个简单的登录例子了解MVP思想

相信大家都或多或少的了解过MVP(全称:Model View Presenter)架构,MVP是从MVC演变过来的。MVC模式中,Activity即充当VIew又充当了Controller,使得Activity内的代码十分臃肿,难以进行维护,而MVP模式就很好的解决了这个问题。学习MVP架构对于自身的面向对象的理解也能更加深刻。
MVP
上面这张图片就可以很清晰的看出MVP各层之间的交互,其中:

  1. Model层:主要用来提供数据,比如从网上获取数据
  2. View层:即视图层,主要用来展示视图
  3. Presenter层:即逻辑层,View层与Model层的桥梁,处理一些逻辑操作

MVP与MVC最大的区别在与,你无法在View层看到任何逻辑处理,MVP中所有的逻辑处理都交由Presenter层来处理,View层只需要负责展示视图即可,有Presenter层的存在,实现了View层与Model层的解耦

下面我们就动手搭建一个MVP架构的登录功能吧,下面所用到的登录API是wanAndroid网站所提供的开放API

1. 项目结构

先来看看这个登录Demo的项目结构
项目结构

2. 创建契约类

因为MVP有个很明显的缺点就是,接口数量太多,而契约类就很好的帮我们整理好这些接口

契约类的代码如下:
public interface IContract {
	//View层
    public interface ILoginView {
        String getUserName();
        String getPassword();
        void onLoginSuccess(String msg);
        void onLoginFails(String msg);
    }

	//Presenter层
    public interface ILoginPresenter {
        void requestMsg(String userName, String password);
        void destroyView();
    }

	//Model层
    public interface ILoginModel {
        public interface CallBack{
            void onSuccess(String msg);
            void onFailed();
        }
        void requestData(String userName, String password, IContract.ILoginModel.CallBack callBack);
    }
}

很明显的可以看出来,IContract接口托管了MVP的三层接口,其中CallBack接口用于数据获取完成之后的回调

3. View层

View层,即我们的LoginActivity,主要负责的是view的操作,所以我们的LoginActivity需要实现ILoginView接口,重写其中的方法

public class LoginActivity  implements IContract.ILoginView {
    @Override
    public String getUserName() {
        return null;
    }

    @Override
    public String getPassword() {
        return null;
    }

    @Override
    public void onLoginSuccess(String msg) {

    }

    @Override
    public void onLoginFails(String msg) {

    }
}

之后我们的View层应该与Presenter层进行关联,所以我们的LoginActivity中应该持有Presenter的引用。

Activity中所有的代码如下:
public class LoginActivity extends AppCompatActivity implements IContract.ILoginView {

    private EditText userNameET;
    private EditText passwordET;
    private Button loginButton;
    private IContract.ILoginPresenter loginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        loginPresenter = new LoginPresenter(this);

        userNameET = findViewById(R.id.username_et);
        passwordET = findViewById(R.id.password_et);
        loginButton = findViewById(R.id.login_button);
        loginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginPresenter.requestMsg(getUserName(), getPassword());
            }
        });
    }

    @Override
    public String getUserName() {
        return userNameET.getText().toString();
    }

    @Override
    public String getPassword() {
        return passwordET.getText().toString();
    }

    @Override
    public void onLoginSuccess(final String msg) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(LoginActivity.this, "用户 : " + msg + " 登录成功", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onLoginFails(final String msg) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (loginPresenter != null) {
            loginPresenter.destroyView();
        }
    }
}

可以看到我们没有在Activity中做任何逻辑相关的处理,我们把获取到的用户名和密码传到Presenter层,然后在重写的方法中更行UI就可以了,这里只是简单的给了一个Toast

4. Presenter层

同样的,首先先实现P层的接口,重写里面的方法。

public class LoginPresenter implements IContract.ILoginPresenter {
    @Override
    public void requestMsg(String userName, String password) {
        
    }

    @Override
    public void destroyView() {

    }
}

因为P层是V层与M层的桥梁,所以P层需要持有V层和M层的引用,为了防止内存泄漏,我们一个弱引用,这些引用赋值操作我们把它放在构造方法中

 private IContract.ILoginView iLoginView;
    private IContract.ILoginModel iLoginModel;
    private WeakReference<IContract.ILoginView> iLoginViewWeakReference;
    private WeakReference<IContract.ILoginModel> iLoginModelWeakReference;
    private User user;

    public LoginPresenter(IContract.ILoginView iLoginView) {
        this.iLoginView = iLoginView;
        iLoginModel = new LoginModel();
        iLoginViewWeakReference = new WeakReference<>(iLoginView);
        iLoginModelWeakReference = new WeakReference<>(iLoginModel);
    }

我们看看重写的方法做了哪些操作,先判断View层传过来的用户名和密码是否为空,如果为空,就通知View层做相应的处理,在这里我们是传一个错误信息过去给View层做显示。如果用户民和密码都不为空,那么我们就把数据传到Model层去请求数据,然后通过CallBack这个接口回调把结果返回给View层

    @Override
    public void requestMsg(String userName, String password) {
        if (userName.equals("") || userName.isEmpty()) {
            iLoginView.onLoginFails("用户名不能为空");
        } else if (password.equals("") || password.isEmpty()) {
            iLoginView.onLoginFails("密码不能为空");
        } else {
            iLoginModel.requestData(userName, password, new IContract.ILoginModel.CallBack() {
                @Override
                public void onSuccess(String msg) {
                    parseJSONByGson(msg);
                    if (user.getErrorCode() == -1) {
                        iLoginView.onLoginFails(user.getErrorMsg());
                    } else {
                        iLoginView.onLoginSuccess(user.getData().getUsername());
                    }
                }

                @Override
                public void onFailed() {
                    iLoginView.onLoginFails("网络错误");
                }
            });
        }
    }

destroyView就是销毁View的方法

@Override
    public void destroyView() {
        iLoginViewWeakReference.clear();
        iLoginModelWeakReference.clear();
    }
Presenter层所有代码如下:
public class LoginPresenter implements IContract.ILoginPresenter {

    private IContract.ILoginView iLoginView;
    private IContract.ILoginModel iLoginModel;
    private WeakReference<IContract.ILoginView> iLoginViewWeakReference;
    private WeakReference<IContract.ILoginModel> iLoginModelWeakReference;
    private User user;

    public LoginPresenter(IContract.ILoginView iLoginView) {
        this.iLoginView = iLoginView;
        iLoginModel = new LoginModel();
        iLoginViewWeakReference = new WeakReference<>(iLoginView);
        iLoginModelWeakReference = new WeakReference<>(iLoginModel);
    }

    @Override
    public void requestMsg(String userName, String password) {
        if (userName.equals("") || userName.isEmpty()) {
            iLoginView.onLoginFails("用户名不能为空");
        } else if (password.equals("") || password.isEmpty()) {
            iLoginView.onLoginFails("密码不能为空");
        } else {
            iLoginModel.requestData(userName, password, new IContract.ILoginModel.CallBack() {
                @Override
                public void onSuccess(String msg) {
                    parseJSONByGson(msg);
                    if (user.getErrorCode() == -1) {
                        iLoginView.onLoginFails(user.getErrorMsg());
                    } else {
                        iLoginView.onLoginSuccess(user.getData().getUsername());
                    }
                }

                @Override
                public void onFailed() {
                    iLoginView.onLoginFails("网络错误");
                }
            });
        }
    }

    @Override
    public void destroyView() {
        iLoginViewWeakReference.clear();
        iLoginModelWeakReference.clear();
    }

    public void parseJSONByGson(String jsonData) {
        Gson gson = new Gson();
        user = gson.fromJson(jsonData, User.class);
    }
}

5. Model层

Model层,也就是负责请求数据的,同样先实现Model层接口,重写里面的方法

public class Model implements IContract.ILoginModel {
    @Override
    public void requestData(String userName, String password, CallBack callBack) {
        
    }
}

之后我们通过OKHttp进行登录请求,通过post请求,把用户名和密码传到网上进行查询,并将结果返回

Model层的所有代码如下:
public class LoginModel implements IContract.ILoginModel {

    @Override
    public void requestData(String userName, String password, final IContract.ILoginModel.CallBack callBack) {
        String url = "https://www.wanandroid.com/user/login";
        RequestBody requestBody = new FormBody.Builder()
                .add("username", userName)
                .add("password", password)
                .build();
        OkHttpClient okHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callBack.onFailed();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String msg = response.body().string();
                callBack.onSuccess(msg);
            }
        });
    }
}

6. 总结

  1. 首先创建一个契约类来管理MVP三层接口
  2. 让Activity继承View层接口,重写方法,并持有Presenter层的引用
  3. 编写Presenter层实现类,重写方法做相关逻辑处理,将处理后的数据传到Model层,需持有View层和Model层的引用
  4. 编写Model层实现类,重写方法进行数据获取,然后将结果返回Presenter层
  5. Presenter层对Model层返回的结果再进行相关逻辑处理
  6. 没有问题后,把结果返回View层展示
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值