今天记录一下自己的使用的分层框架,也是现在的主流框架MVP+Rxjava,好处就不多说了,直接主题吧。
首先对照图片来总体看一下mvp是怎么工作的
步骤一:view调用presenter的方法,通知presenter自己需要哪些数据
步骤二:presenter调用model的方法获取数据
步骤三:model获取到数据以后返回给presenter
步骤四:presenter获取到数据以后再传递给view进行显示
上面的流程就可以清晰地看出presenter就是一个中间人的角色,它与隔离了view和model,已达到解耦的目的,view只负责显示数据,model只负责获取数据(网络或是本地)
具体如何实现请看我的github地址github
下面就根据github的内容讲解
代码的结构以功能模块来划分,我这里是登录模块,还是比较清晰地。
base
base包里面的内容集中抽象了各层的基础操作
一、view
View也就是Activity(或是Fragment)
这里有一个BaseView是所有View的接口,定义了大部分view会有的操作,来看代码:
public interface BaseView {
//显示等待框
void showLoading();
//显示错误提示
void showError();
}
接口里面只有两个方法,一般的view都会有这两个操作,如果你有更多可以添加更多方法。
二、model
public interface BaseModel {
}
model是一个空接口,因为每一个模块使用到的数据会不一样,具体的数据由他的实现类来确定,比如我们这里需要获取网络的User数据,那么BaseModel的实现就应该有getUser()这样的方法。
三、presenter
public abstract class BasePresenter<M, V> {
public M model;
V view;
public WeakReference<V> mViewRef;
public void attachModelView(M pModel, V pView) {
this.model = pModel;
mViewRef = new WeakReference<>(pView);
}
public boolean isAttach() {
return mViewRef != null && mViewRef.get() != null;
}
public V getView() {
if (isAttach()) {
return mViewRef.get();
} else {
return null;
}
}
public void onDettach() {
if (null != mViewRef) {
mViewRef.clear();
mViewRef = null;
}
}
}
presenter的内容比较多,毕竟他是model和view的中间人,所以它要同时持有这两个引用
这里使用了两个泛型M、V,BasePresenter并不知道model和view的具体类型,里面提供了一个初始化Model和view的方法。
到了这里base写好了,下面就来到了我们的Login模块
登录的逻辑很简单,输入用户名和密码,显示登录结果(失败或是成功),成功会返回User对象
LoginContract里面定义了两个接口和一个抽象类,你也可以分开了写,但是就会造成类太多开起来乱。
public interface LoginContract {
interface LoginView extends BaseView{
//在LoginPresenter中调用这个方法显示数据
void setUser(TestData<User> user);
}
interface LoginModel extends BaseModel{
//获取数据库的数据
void getUser(String name , String password, MVPListener listener);
}
abstract class LoginPresenter extends BasePresenter<LoginModel,LoginView>{
//调用LoginModel的getUser方法获取数据,传递给LoginView显示,起到中间人的作用
abstract void getUser(String name,String password);
}
}
这里面的方法都是Login模块需要用到的数据LoginPresenter的getUser会调用LoginModel的getUser,最后调用LoginView的setUser,也就是刚开始的流程图的实现。
好了,写了那么多接口终于到了他们的实现类的时候了
首先看看LoginModel
public class LoginModel implements LoginContract.LoginModel {
Retrofit retrofit;
@Override
public void getUser(String name, String password, final MVPListener mvpListener) {
//做一些网络请求
initHttp();
retrofit.create(ApiService.class).reLogin(name, password).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<TestData<User>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(TestData<User> value) {
//回调给presenter
mvpListener.onSuccess(value);
}
@Override
public void onError(Throwable e) {
mvpListener.onError();
}
@Override
public void onComplete() {
}
});
}
public void initHttp() {
retrofit = new Retrofit.Builder().baseUrl("http://wnd.agri114.cn/wndms/")
.client(new OkHttpClient())
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().setDateFormat("yyyy-MM-dd").create()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
}
LoginModel只负责获取数据,所以方法也简单只有一个getUser(),这里面用到了Retrofit和Rxjava,不会的请自行查阅(这个现在必须会),当然了我们这里只是讲解MVP的实现,具体的网络请求使用什么框架不重要。
这里面还有一个回调接口MVPListener 是用来将获取到的数据返回给presenter的,也就是流程图的步骤三。
public interface MVPListener<T> {
void onSuccess(T login);
void onError();
}
两个方法,不多解释。这里的地址是可以使用的,大家免费测试,用户名是18805174084,密码111111
再来看看LoginPresenter
public class LoginPresenter extends LoginContract.LoginPresenter {
@Override
void getUser(String name,String password) {
final LoginContract.LoginView view = getView();
view.showLoading();
model.getUser(name, password, new MVPListener() {
@Override
public void onSuccess(Object login) {
if(login instanceof TestData){
view.setUser((TestData<User>) login);
}
}
@Override
public void onError() {
view.showError();
}
});
}
}
方法也是非常的简单,就是调用Model的getUser方法让view显示(注意presenter持有model和view的引用,具体请看BasePresenter)
最后看看view
我们的activity基本都会有一个baseactivity,这里我在baseactivity里面增加一些内容
public abstract class BaseActivity<T extends BasePresenter, M extends BaseModel> extends AppCompatActivity {
public T mPresenter;
public M mModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
mPresenter = CreatUtil.getT(this, 0);
mModel = CreatUtil.getT(this,1);
mPresenter.attachModelView(mModel,this);
initView();
}
public abstract void initView();
public abstract int getLayoutId();
}
这里就是mvp工作开始的地方,BaseActivity<T extends BasePresenter, M extends BaseModel>定义了两个泛型并且在onCreate方法中初始化他们,初始化使用反射原理
public static <T> T getT(Object o, int i) {
try {
return ((Class<T>) ((ParameterizedType) (o.getClass().getGenericSuperclass())).getActualTypeArguments()[i]).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
然后初始化presenter的Model和View,至于initView()和getLayoutId()由子类实现。
基类定义好之后就到了LoginActivity
public class LoginActivity extends BaseActivity<LoginPresenter, LoginModel> implements LoginContract.LoginView {
AutoCompleteTextView name;
EditText password;
Button mButton;
@Override
public void showLoading() {
Toast.makeText(this, "showLoading", Toast.LENGTH_LONG).show();
}
@Override
public void showError() {
Toast.makeText(this, "Error", Toast.LENGTH_LONG).show();
}
@Override
public void setUser(TestData<User> user) {
if (user != null) {
Toast.makeText(this, user.getUser().getTel()+"", Toast.LENGTH_LONG).show();
}
}
//由父类调用
@Override
public void initView() {
name = (AutoCompleteTextView) findViewById(R.id.email);
password = (EditText) findViewById(R.id.password);
mButton = (Button) findViewById(R.id.email_sign_in_button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPresenter.getUser(name.getText().toString(), password.getText().toString());
}
});
}
@Override
public int getLayoutId() {
return R.layout.activity_login;
}
}
继承了BaseActivity并且给Model和View设置了确定的类型,因为是view所以要实现LoginView接口,里面的方法都很简单,
注:这里讲的是MVP的工作流程,所以对于代码的安全检查并没有做
其他的模块照着login模块就可以了
最后是一张接口和类的关系图
在登录模块我把两个接口和一个抽象类放在了一起,其他模块建议也这样做