如何使用BaseActivity来创建一个新的Activity, 并且遵循MVP设计模式?
抽象
首先需要了解清楚需求, 确认页面有哪些行为, 抽象出View的接口以及Model的接口. 下面以登录界面为例. 一个基本登录界面应该包含用户名和密码输入框, 登录按钮, 以及注册和忘记密码的入口. 那么我们应该如何抽象出View呢? 从登录流程入手(从程序的角度出发):
1. Activity检测到用户点击了登录按钮
2. Activity转发事件到Presenter
3. Presenter简单验证用户名和密码是否合法
3.1. Presenter通知View用户名或者密码不合法
4. Presenter通知View显示加载框
5. Presenter调用Model的登录接口
6. Model返回登录结果给Presenter
7. Presenter解析结果
7.1. Presenter通知View隐藏加载框
7.2. Presenter通知View登录成功
7.3. Presenter通知View错误消息
既然我们使用的是MVP被动视图, 那么我们应该抽象的是View的被动行为, 即Presenter告诉View要做的行为. 从上述流程我们可以提取出这么几个行为: 提示用户名不合法, 提示密码不合法, 显示加载框, 隐藏加载框, 处理登录成功, 显示登录接口返回的错误. 但是BaseView已经包含了显示加载框, 隐藏加载框以及显示一些错误消息, 那么对应到代码则应该是这样:
public interface LoginView extends BaseView {
void loginComplete();
void usernameError();
void passwordError();
}
同时我们也能顺水推舟找出Presenter里的公共方法以及Model中的接口.
public class LoginPresenter extends BasePresenter<LoginView> {
public void login(String username, String password){}
}
public interface LoginManager {
void login(String username, String password);
}
实现
现在有了View Presenter和Model之后我们就可以开始创建界面了, 首先声明一个LoginActivity, 如下所示:
public class LoginActivity extends BaseActivity<LoginView, LoginPresenter>
implements LoginView {
然后实现BaseActivity中的三个抽象方法:
@Override
protected int getLayoutId() {
return R.layout.activity_login;
}
@Override
protected void init(Bundle savedInstanceState) {
initNav(); //初始化标题栏
}
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter();
}
接着实现LoginView中的几个方法:
@Override
public void loginComplete() {
startActivity(MainActivity.getLaunchIntent(this)); //跳转至主页
finish();
}
@Override
public void usernameError() {
showToast("请输入正确的手机号"); //建议使用string资源
}
@Override
public void passwordError() {
showToast("请输入6-16位密码"); //建议使用string资源
}
Activity中的功能基本已经实现. 另外在这里建议每个Activity都提供以下方法:
public static Intent getLaunchIntent(Context context, int flag) {
Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra(flag, EXTRA_FLAG);
return intent;
}
在其他界面需要跳转至LoginActivity的时候就只需要:
startActivity(LoginActivity.getLaunchIntent(this, 1));
这样写的好处是其他界面里不用去组装intent, 也不必在意intent里extra的名字, 特别是很多界面需要跳至这个界面的时候, 能减少很多重复代码, EXTRA这种静态常量也只需要在一个类的内部维护就可以了.
接下来看Presenter:
public void login(String username, String password) {
if (!checkUserInput(username, password)) return;
view.showLoading();
manager.login(username, password)
.compose(new ResponseTransformer<>(this.<BaseData> bindLifeCycle()))
.subscribe(new ResponseSubscriber<BaseData>() {
@Override public void success(BaseData baseData) {
// 此处意味着登录成功, 其他错误情况已自动处理
view.loginComplete();
}
});
}
private boolean checkUserInput(String username, String password) {
if (!ValidationUtil.validatePhone(username)) {
view.usernameError();
return false;
}
if (stringIsNull(password) || password.length() < 6 || password.length() > 16) {
view.passwordError();
return false;
}
return true;
}
Presenter的代码比较简单, 主要就是做了一次用户名和密码的验证, 然后发送网络请求, 需要注意的是发送网络请求的代码使用了Retrofit2与RxJava. 隐藏加载框, 错误提示等操作都在ResponseSubscriber类里面做了, 后续会提到这一块.
Model里则是直接使用的Retrofit2, 这里就不赘述了.
总结
创建一个新Activity或者Fragment可以遵循以下步骤:
创建Presenter, 填入View的泛型并提供公共方法
创建新的Activity并继承自BaseActivity
如果一个界面没有什么逻辑只是单纯的显示应该怎么做?
创建新的Activity并继承自BaseActivity
实现BaseActivity的三个抽象方法并在createPresenter()方法中返回null
单纯的继承BaseActivity与继承其他Activity一样并无太大区别, 只需要实现三个抽象方法即可, 其他用法都一样. 但是如果一旦使用了presenter变量则会抛出空指针异常.