最近学习MVVM想到之前看过的MVP框架,虽然已经过时了,但是还是觉得有可以借鉴的地方,所以又拿出来复习了一下,用一个小小的demo来练练手。
模拟一个注册登录过程,首先理一下代码逻辑:
1、注册登录的过程当中,P需要做什么?
V向P发起请求,P向M请求数据,M发起网络请求并在请求完成后通过接口将数据回调给P,P持有V的引用,完成数据请求玩的操作。
2、M需要做什么?
M负责发起网络请求以及数据的处理
3、V需要做什么?
V初始化P,并通过P来通知M发起网络请求
首先新建如上几个文件,具体作用如下
IPresenter中定义所需的功能 注册、登录、获取用户信息
public interface IPrestener {
void login();
void register();
void getUserInfo();
}
IView中定义网络请求后view需要执行的操作
public interface IView<T> {
void onNetStart();
void onSuccess(T result);
void onFail(String msg);
}
IModel内暂时不需要什么内容,主要为了在presenter中初始化的统一
IRegisterView继承IView,注册需要获取view中的用户名、密码,并且注册完成后需要将用户信息返给view所以IRegisterView中定义如下内容
public interface IRegisterView extends IView {
String getUserName();
String getPassword();
void userInfo(User user);
}
ILoginView继承IView,登录需要获取view中的用户名密码
public interface ILoginView extends IView {
String getUserName();
String getPassword();
}
MainPresenter考虑作为全局的presenter,可以在里面实现所有的presenter功能,实现IPresenter接口,所有方法在IPresenter中定义,在MainPresenter中实现
public class MainPresenter implements IPrestener {
private IView view;
private IModel model;
public MainPresenter(IView view) {
this.view = view;
}
@Override
public void login() {
model = new LoginModel();
String userName = ((ILoginView) view).getUserName();
String passWord = ((ILoginView) view).getPassword();
((LoginModel) model).getLoginResult(userName, passWord, new NetCallBack() {
@Override
public void onStart() {
view.onNetStart();
}
@Override
public void onSuccess(Object result) {
view.onSuccess(result);
}
@Override
public void onFail(String msg) {
view.onFail(msg);
}
});
}
@Override
public void register() {
model = new RegisterModel();
String userName = ((IRegisterView) view).getUserName();
String passWord = ((IRegisterView) view).getPassword();
((RegisterModel) model).userRegister(userName, passWord, new NetCallBack() {
@Override
public void onStart() {
view.onNetStart();
}
@Override
public void onSuccess(Object result) {
view.onSuccess(result);
}
@Override
public void onFail(String msg) {
view.onFail(msg);
}
});
}
@Override
public void getUserInfo() {
model = new RegisterModel();
((RegisterModel) model).getUserInfo(new NetCallBack() {
@Override
public void onStart() {
view.onNetStart();
}
@Override
public void onSuccess(Object result) {
((IRegisterView) view).userInfo((User) result);
}
@Override
public void onFail(String msg) {
view.onFail(msg);
}
});
}
}
这里可以看到MainPresenter构造函数需要传入view,并且MainPresenter始终持有view的引用。定义统一的成员变量IModel,在不同的方法中分别进行初始化。
RegisterModel、LoginModel中模拟注册以及返回用户信息的操作,模拟网络请求
public class RegisterModel implements IModel {
public void userRegister(final String username, final String password, final NetCallBack callBack) {
if (username == null || "".equals(username)) {
callBack.onFail("请输入用户名");
return;
}
if (password == null || "".equals(password)) {
callBack.onFail("请输入密码");
return;
}
callBack.onStart();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
LoginModel.USER = username;
LoginModel.PASS = password;
callBack.onSuccess("注册成功");
}
}, 3000);
}
public void getUserInfo(final NetCallBack callBack) {
callBack.onStart();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
callBack.onSuccess(new User("zyq", 25, "男"));
}
},3000);
}
}
public class LoginModel implements IModel {
public static String USER = "";
public static String PASS = "";
public void getLoginResult(final String s1, final String s2, final NetCallBack callBack) {
if (s1 == null || "".equals(s1)) {
callBack.onFail("请输入用户名");
return;
}
if (s2 == null || "".equals(s2)) {
callBack.onFail("请输入密码");
return;
}
callBack.onStart();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (USER.equals(s1) && PASS.equals(s2)) {
callBack.onSuccess("登陆成功");
} else {
callBack.onFail("请输入正确的用户名、密码");
}
}
}, 3000);
}
}
NetCallBack是一个Model回调给Presenter网络请求结果的接口,此处在具体项目中建议使用retrofit观察者代替。
public interface NetCallBack<T> {
void onStart();
void onSuccess(T result);
void onFail(String msg);
}
User是一个用户信息实体类
public class User {
private String username;
private int age;
private String sex;
public User(String username, int age, String sex) {
this.username = username;
this.age = age;
this.sex = sex;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
下面是两个activity中的代码
public class MainActivity extends AppCompatActivity implements ILoginView {
private ActivityMvpMainLayoutBinding binding;
private MainPresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_mvp_main_layout);
presenter = new MainPresenter(this);
}
@Override
public String getUserName() {
return binding.etUsername.getText().toString().trim();
}
@Override
public String getPassword() {
return binding.etPassword.getText().toString().trim();
}
@Override
public void onNetStart() {
Toast.makeText(getApplicationContext(), "开始登陆", Toast.LENGTH_SHORT).show();
}
@Override
public void onSuccess(Object result) {
Toast.makeText(getApplicationContext(), (String)result, Toast.LENGTH_SHORT).show();
}
@Override
public void onFail(String msg) {
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
public void loginClick(View view) {
presenter.login();
}
}
public class RegisterActivity extends AppCompatActivity implements IRegisterView {
private ProgressDialog dialog;
private ActivityRegisterLayoutBinding binding;
private MainPresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_register_layout);
presenter = new MainPresenter(this);
dialog = new ProgressDialog(this);
}
@Override
public String getUserName() {
return binding.etUsername.getText().toString().trim();
}
@Override
public String getPassword() {
return binding.etPassword.getText().toString().trim();
}
@Override
public void userInfo(User user) {
StringBuffer sb = new StringBuffer();
sb.append("用户:");
sb.append(user.getUsername());
sb.append("\n");
sb.append("年龄:");
sb.append(user.getAge());
sb.append("\n");
sb.append("性别:");
sb.append(user.getSex());
dialog.dismiss();
showToast(sb.toString());
startActivity(new Intent(RegisterActivity.this, MainActivity.class));
}
@Override
public void onNetStart() {
showToast("开始请求");
dialog.show();
}
@Override
public void onSuccess(Object result) {
dialog.dismiss();
showToast((String) result);
presenter.getUserInfo();
}
@Override
public void onFail(String msg) {
dialog.dismiss();
showToast(msg);
}
public void register(View view) {
presenter.register();
}
private void showToast(final String str) {
new Handler(getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();
}
});
}
}
现在来理一下整体的调用流程(以注册获取用户信息为例)
1、activity中实例化MainPresenter并将自己传给MainPresenter的构造函数,此时MainPresenter持有RegisterActivity的实例。
2、点击注册按钮调用presenter的register方法,register方法中实例化RegisterModel,并调用RegisterModel中的userRegister。
3、userRegister中模拟网络请求,模拟的网络请求结果回调给MainPresenter传入RegisterModel的接口。
4、收到RegisterModel回调后,通过持有的RegisterActivity通知activity进行相关操作。
5、在activity的onSuccess中调用presenter的获取用户信息方法
·
·
·
获取完用户信息后开始登录流程,登录流程和注册流程一样
总结:View、Model通过Presenter进行联系,View调用Presenter->Presenter调用Model->Model获取完数据回调给Presenter->Presenter通过持有的View的引用将数据交给View进行UI操作。MVP的好处是UI只负责显示,数据的获取和处理都交给Model去实现,并且View和Model不进行直接联系,降低了代码的耦合度。开始可能会觉得代码写起来复杂,可是在大型项目中可以大大降低耦合度,方便后期维护,并且代码逻辑看起来简洁明了,不会造成一个activity上千行的代码。
第一次做学习笔记,有错误或者有可以改进的地方,还望指出 蟹蟹