上篇文章(Andoid MVP架构 MVP软件架构介绍与实战运用),我们讲述了如何构建一个基于 MVP 的 HelloWord 级别的程序,让我们粗浅的了解了 MVC 与 MVP 之间的优缺点和联系,以及写了一个具有代表性的 MVP 的小 demo,通过这个 demo 我们可以发现很多问题,比如,还是有代码的重复。如果你没看过上一篇文章,强烈建议你先过一遍,否则这篇文章你可能会觉得我不知所云,且看以下的代码示例:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
mPresenter = new MainPresenter(this);
mPresenter.handlerData();
}
先来看看这一版本的分包结构,相比之前 v1 的版本就多了一个 basemvp 包,然这个包下的代码却很关键。
我们的 View 层要持有 Presenter 层的引用,就必须要先实例化 P 层的对象,然后才能调用 P 层的相关方法,否则就会造成空指针异常。这里的 new MainPresenter() 是一个具体实现类,因为不管哪个 View 层,都是强制性要求实例化 P 层,为了防止我们忘了实例化,所以在此把实例化方法抽象到父类中,实现它的子类必须实现抽象方法,也就避免了我们忘记。
还有不同的 Presenter 层,自然而然它的具体实现类就不同了,所以应该考虑用到泛型,这里要明白谁在引用 Presenter,就是谁要传入泛型。显然,我们的 IBaseView 的实现类 BaseActivity 就得传入一个 Presenter 的泛型参数,那先来看一看 IBaseView 接口如何写:
View 层,新建 IBaseView 接口:
package com.test.mvp.mvpdemo.mvp.v2.basemvp;
import android.content.Context;
public interface IBaseView {
Context getContext();
}
很简单,获取一个上下文对象,我们在 Activity 中或多或少的用到 Context,所以就干脆写个在这里写统一了,为了避免我们在匿名内部类中要传入 MainActivity.this 这样的麻烦事。既然有了 IBaseView 的接口,我们就该考虑写它的具体实现类,因为这里的 View 层就是 Activity,所以我们就得写一个 BaseActivity 基类,既然是基类,就要声明为抽象类,把共有的方法提取到基类中,这里使用到的就是模板模式。
View 层,新建 BaseActivity 基类:
package com.test.mvp.mvpdemo.mvp.v2.basemvp;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public abstract class BaseActivity<P extends IBasePresenter> extends AppCompatActivity implements IBaseView {
protected P mPresenter;
protected abstract void initLayout(@Nullable Bundle savedInstanceState);
protected abstract void initViews();
protected abstract void initData();
protected abstract P setPresenter();
protected <T extends View> T $(@IdRes int viewId) {
return findViewById(viewId);
}
@SuppressWarnings("unchecked")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initLayout(savedInstanceState);
/**
* 实例化和绑定 P 层
*/
mPresenter = setPresenter();
mPresenter.attech(this);
initViews();
initData();
}
@Override
protected void onDestroy() {
super.onDestroy();
/**
* 解绑,避免内存泄漏
*/
mPresenter.detech();
mPresenter = null;
}
@Override
public Context getContext() {
return this;
}
}
从上面的代码中,我把 new Presenter() 实现类写成了一个泛型,泛型应该是 Java 的基础和基本操作,我就不多说了。这里的抽象方法 setPresenter() 就相当于 new Presenter() 一样,子类继承 BaseActivity 时,只需要返回一个具体的 Presenter() 实例就行了,这样 View 层就持有了 Presenter 的引用。
然后看 presenter.attech(this) 这个方法,目的是传入一个 IBaseView 的接口类型,将 View 层的引用给了 Presenter 层,这样就形成了关联,就可以互相调用对象的方法了。
这里注意,一定要在 onDestory() 时释放 P 层引用,否则可能会造成内存泄漏,这个我们之后再说。然后我们来看看 IBasePresenter 接口的方法:
Presenter 层,新建 IBasePresenter 接口:
public interface IBasePresenter<V extends IBaseView> {
void attech(V view);
void detech();
}
这里也很简单,就是做了绑定 View 和解绑 View 的操作。那我们的 BasePresenter 实现类就需要持有 BaseView 的引用,所以必须要传入一个泛型的 View 层接口,具体实现代码如下。
Presenter 层,新建 BasePresenter 基类:
package com.test.mvp.mvpdemo.mvp.v2.basemvp;
public abstract class BasePresenter<V extends IBaseView> implements IBasePresenter {
protected V mView;
@SuppressWarnings("unchecked")
@Override
public void attech(IBaseView view) {
mView = (V) view;
}
@Override
public void detech() {
mView = null;
}
}
结束了这两个基类的封装,我们的 BaseMVP 差不多就可以形成了。剩下的就是要修改 MainActivity 和 MainPresenter 的操作了,这里的 Model 层是无需修改的,具体业务逻辑也是不用修改的。所以,我们在之前 v1 版本的 MVP 架构的基础之上做一些简单的修改。(请看我的上一篇文章:Andoid MVP架构MVP软件架构介绍与实战运用)
首先,我们需要修改的一个地方,就是契约类,这个类中的业务逻辑处理都不需要修改,还记得我们之前写过的那两个 IBaseView 与 IBasePresenter 吗?这时候就派上用场了。来看代码:
修改 MainContract 契约类:
package com.test.mvp.mvpdemo.mvp.v2;
import com.test.mvp.mvpdemo.mvp.v2.basemvp.IBasePresenter;
import com.test.mvp.mvpdemo.mvp.v2.basemvp.IBaseView;
import okhttp3.Callback;
/**
* 契约接口,可以很直观的看到 M、V、P 层接口中提供的方法
*/
public interface MainContract {
interface IMainModel {
void requestBaidu(Callback callback);
}
interface IMainView extends IBaseView{
void showDialog();
void succes(String content);
}
interface IMainPresenter extends IBasePresenter {
void handlerData();
}
}
为了支持泛型,这里的 IMainView 和 IMainPresenter 就需要继承它们各自的基类接口了。
其次,MainActivity 不再继承 AppCompatActivity 了,要继承我们刚刚写好的基类:BaseActivity,其中修改的代码如下:
View 层,修改 MainActivity 实现类:
package com.test.mvp.mvpdemo.mvp.v2.view;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.widget.TextView;
import android.widget.Toast;
import com.test.mvp.mvpdemo.R;
import com.test.mvp.mvpdemo.mvp.v2.MainContract;
import com.test.mvp.mvpdemo.mvp.v2.basemvp.BaseActivity;
import com.test.mvp.mvpdemo.mvp.v2.presenter.MainPresenter;
/**
* MVP 的写法,Version 2: 使用模板方法模式 + 泛型 封装 mvp 基类
*
* @author 神探丶威威猫
* @blog https://blog.csdn.net/smile_running
* @warning 点个赞哦,评个论哦
*/
public class MainActivity extends BaseActivity<MainContract.IMainPresenter> implements MainContract.IMainView {
private TextView tv;
@Override
protected void initLayout(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
}
@Override
protected void initViews() {
tv = $(R.id.tv);
}
@Override
protected void initData() {
mPresenter.handlerData();
}
@Override
protected MainContract.IMainPresenter setPresenter() {
return new MainPresenter();
}
@Override
public void showDialog() {
ProgressDialog dialog = new ProgressDialog(getContext());
dialog.show();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
dialog.dismiss();
}
}, 1500);
}
@Override
public void succes(String content) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getContext(), "" + content, Toast.LENGTH_SHORT).show();
tv.setText(content);
}
});
}
}
就这么简单,只要稍微的修改就可以了。然后我们看看 MainPresenter 实现类的代码,为了持有 View 层的引用,这里就需要传入泛型的接口了,代码修改成如下:
Presenter 层,修改 MainPresenter 实现类:
package com.test.mvp.mvpdemo.mvp.v2.presenter;
import android.util.Log;
import com.test.mvp.mvpdemo.mvp.v2.MainContract;
import com.test.mvp.mvpdemo.mvp.v2.basemvp.BasePresenter;
import com.test.mvp.mvpdemo.mvp.v2.basemvp.IBaseView;
import com.test.mvp.mvpdemo.mvp.v2.model.DataModel;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
/**
* presenter 层,承担业务逻辑处理,数据源处理等
*/
public class MainPresenter extends BasePresenter<MainContract.IMainView> implements MainContract.IMainPresenter {
private MainContract.IMainModel mModel;
@Override
public void attech(IBaseView view) {
super.attech(view);
mModel = new DataModel();
}
@Override
public void handlerData() {
if (mView != null) {
mView.showDialog();
}
/**
* 发起请求,获得回调数据
*/
mModel.requestBaidu(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String content = response.body().string();
if (mView != null) {
mView.succes(content);
}
}
});
}
@Override
public void detech() {
super.detech();
/**
* 释放内存、关闭网络请求、关闭线程等操作
*/
Log.d("==========", "detech: 解除绑定,释放内存");
}
}
终于修改完了一个基础框架,是不是觉得非常有成就感。不过呢,这个框架还会有一些隐患,我会在下一篇的文章中去解决这个隐患:那就是内存泄漏与动态代理的问题。
那么,这篇文章到此为止,我们就把新的一个版本的 MVP 架构给修改完了,其实运用的都是一些基础知识,比如模板模式、抽象类和抽象方法、泛型、继承、接口等的使用,每一个都是 Java 的基础知识,是不是很简单啊。如果你可以基于上一篇的 MVP 版本,自己手写一个泛型框架,那说明你掌握的基础知识很不错。不然,还是多推敲推敲别人的写法,多想为什么要这样写?这样才能有效的提高自己的水平。
最后
如果你看到了这里,觉得文章写得不错就给个赞呗!欢迎大家评论讨论!如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足,定期免费分享技术干货。谢谢!
网友评论