- Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
- View(视图) - 视图代表模型包含的数据的可视化。
- Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
下面是一个简单的 mvc 实现的例子
public class MainActivity extends BaseActivity {
private ListView listView;
private List<InfoBean> lists = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
getData();
}
private void init() {
listView = findViewById(R.id.listView);
}
private void getData() {
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336456530&di=d427f7650721a197f1dbe68169814608&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20200103%2Fcb1d64d373f54f21928a4813f41937e6.jpeg",
"语文书", "从小就学习的语文书"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336510815&di=8f8dfc6f48f2864409205a1a5c8bda1b&imgtype=0&src=http%3A%2F%2Fimg14.360buyimg.com%2Fn1%2Fjfs%2Ft7012%2F299%2F121318139%2F28880%2F44c02f66%2F5972f6bfN3eee543b.jpg",
"数学书", "从小就学习的数学书"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336548967&di=4bc1d03cd9a0e68d4b90740fcdd61bca&imgtype=0&src=http%3A%2F%2Fshopimg.kongfz.com.cn%2F20130326%2F118587%2F118587DE4vm0_b.jpg",
"英语书", "从小就学习的英语书"));
lists.add(new InfoBean("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=189950173,2182075783&fm=26&gp=0.jpg",
"化学书", "从小就学习的化学书"));
lists.add(new InfoBean("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2986308404,3738485014&fm=26&gp=0.jpg",
"物理书", "从小就学习的物理书"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336997608&di=9ac209c043c6dba2300cf4df9cc1b554&imgtype=0&src=http%3A%2F%2Fimg.jk51.com%2Fimg_jk51%2F148284641.jpeg",
"生物书", "从小就学习的生物书"));
listView.setAdapter(new MyAdapter(this, lists));
}
}
简称:MVP 全称:Model-View-Presenter ;MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。
接下来我们按照图中所展示的来一点点改造上面的代码。
上面的图所示 创建 view 接口
public interface IMainView {
// 返回data
void showData(List<InfoBean> infoBeans);
}
public interface IModel {
// 通过回调注入的形式获取数据
void loadInfo(OnLoadListener loadListener);
interface OnLoadListener {
void onComplete(List<InfoBean> infoBeans);
}
}
public class MainModel implements IModel {
// 通过接口获取数据
@Override
public void loadInfo(OnLoadListener loadListener) {
List<InfoBean> lists = new ArrayList<>();
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336456530&di=d427f7650721a197f1dbe68169814608&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20200103%2Fcb1d64d373f54f21928a4813f41937e6.jpeg",
"语文书", "从小就学习的语文书"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336510815&di=8f8dfc6f48f2864409205a1a5c8bda1b&imgtype=0&src=http%3A%2F%2Fimg14.360buyimg.com%2Fn1%2Fjfs%2Ft7012%2F299%2F121318139%2F28880%2F44c02f66%2F5972f6bfN3eee543b.jpg",
"数学书", "从小就学习的数学书"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336548967&di=4bc1d03cd9a0e68d4b90740fcdd61bca&imgtype=0&src=http%3A%2F%2Fshopimg.kongfz.com.cn%2F20130326%2F118587%2F118587DE4vm0_b.jpg",
"英语书", "从小就学习的英语书"));
lists.add(new InfoBean("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=189950173,2182075783&fm=26&gp=0.jpg",
"化学书", "从小就学习的化学书"));
lists.add(new InfoBean("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2986308404,3738485014&fm=26&gp=0.jpg",
"物理书", "从小就学习的物理书"));
lists.add(new InfoBean("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586336997608&di=9ac209c043c6dba2300cf4df9cc1b554&imgtype=0&src=http%3A%2F%2Fimg.jk51.com%2Fimg_jk51%2F148284641.jpeg",
"生物书", "从小就学习的生物书"));
loadListener.onComplete(lists);
}
}
public class MainPresenter {
// 依赖 view
private IMainView iMainView;
// 依赖 model
private IModel mainModel = new MainModel();
public MainPresenter(IMainView iMainView) {
this.iMainView = iMainView;
}
// 通过model 获取数据 返回回调
public void fetch() {
mainModel.loadInfo(new IModel.OnLoadListener() {
@Override
public void onComplete(List<InfoBean> infoBeans) {
// 通过 view 返回数据
iMainView.showData(infoBeans);
}
});
}
}
// 实现 IMainView接口
public class MainActivity extends BaseActivity implements IMainView {
private ListView listView;
private MainPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
// 初始化 Presenter
presenter = new MainPresenter(this);
// 获取数据
presenter.fetch();
}
private void init() {
listView = findViewById(R.id.listView);
}
@Override
public void showData(List<InfoBean> infoBeans) {
// 设置回调数据结果
listView.setAdapter(new MyAdapter(this, infoBeans));
}
}
以上就是一个基本的 mvp 架构。但是基本的架构首先不满足扩展,不能动态注入。并且当 Presenter有网络请求的时候,Activity销毁后不能及时回收,导致内存泄漏。接下来进行 mvp的重构。
public class MainPresenter<T extends IMainView> {
private WeakReference<T> iMainView;
private IModel mainModel = new MainModel();
public MainPresenter(T iMainView) {
this.iMainView = new WeakReference<T>(iMainView);
}
public void fetch() {
mainModel.loadInfo(new IModel.OnLoadListener() {
@Override
public void onComplete(List<InfoBean> infoBeans) {
iMainView.get().showData(infoBeans);
}
});
}
}
public class MainPresenter<T extends IMainView> {
private WeakReference<T> iMainView;
private IModel mainModel = new MainModel();
/**
* 绑定view
*/
public void attachView(T view) {
this.iMainView = new WeakReference<T>(view);
}
/**
* 解绑view
*/
public void detachView() {
iMainView.clear();
iMainView = null;
}
public void fetch() {
mainModel.loadInfo(new IModel.OnLoadListener() {
@Override
public void onComplete(List<InfoBean> infoBeans) {
iMainView.get().showData(infoBeans);
}
});
}
}
上面的代码不能每次使用的时候都需要写这么多的代码,怎么简化呢接下来继续抽离。
抽离到base。
// T 传入 Presenter V 传入View
public abstract class BaseActivity<T extends BasePresenter, V> extends AppCompatActivity {
protected T presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = createPresenter();
if (presenter!=null){
// 绑定View
presenter.attachView((V) this);
}
}
// 子类创建 presenter
protected abstract T createPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
// 解绑view
presenter.detachView();
}
}
/**
* @param 传入View
*/
public class BasePresenter<V> {
private WeakReference<V> referenceView;
V getView() {
if (referenceView != null) {
return referenceView.get();
}
return null;
}
public void attachView(V view) {
this.referenceView = new WeakReference<V>(view);
}
public void detachView() {
if (referenceView != null) {
referenceView.clear();
referenceView = null;
}
}
}
public class MainPresenter<T extends IMainView> extends BasePresenter<T> {
private IModel mainModel = new MainModel();
public void fetch() {
mainModel.loadInfo(new IModel.OnLoadListener() {
@Override
public void onComplete(List<InfoBean> infoBeans) {
getView().showData(infoBeans);
}
});
}
}
public class MainActivity extends BaseActivity<MainPresenter<IMainView>,IMainView> implements IMainView {
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
presenter.fetch();
}
@Override
public MainPresenter<IMainView> createPresenter() {
return new MainPresenter<>();
}
private void init() {
listView = findViewById(R.id.listView);
}
@Override
public void showData(List<InfoBean> infoBeans) {
listView.setAdapter(new MyAdapter(this, infoBeans));
}
}
以上就是 MVP 的基本框架了。有个性需求可以自己扩展。比如传入多个 Presenter等
以上最终代码下载请点击
新建一个 commonLib 的module。
新建一个 IHttpProcessor 接口,定义需要的访问类型接口。
/**
* 各种访问类型接口
*/
public interface IHttpProcessor {
/**
* 网络操作
*/
void post(String url, Map<String,Object> params, ICallback callback);
void delete(String url, Map<String,Object> params, ICallback callback);
}
/**
* Http 请求初始化类
*/
public class HttpHelper {
private static IHttpProcessor processor;
private HttpHelper() {
}
public static HttpHelper getInstance() {
return HttpHelperSingleTon.httpHelper;
}
private static class HttpHelperSingleTon {
private static HttpHelper httpHelper = new HttpHelper();
}
public void init(IHttpProcessor processor) {
HttpHelper.processor = processor;
}
// 调用 post 操作
public void post(String url, Map<String, Object> params, ICallback callback) {
// 请求代理给 具体的 processor 执行具体访问
// appendParams 是将 post可以转为 get请求 访问get请求的时候也可以使用此post方法
String finalUrl = appendParams(url, params);
processor.post(finalUrl, params, callback);
}
// 调用 delete 操作
public void delete(String url, Map<String, Object> params, ICallback callback) {
processor.delete(url, params, callback);
}
private static String appendParams(String url, Map<String, Object> params) {
if (params == null || params.isEmpty()) {
return url;
}
StringBuilder urlBuilder = new StringBuilder(url);
if (urlBuilder.indexOf("?") <= 0) {
urlBuilder.append("?");
} else {
if (!urlBuilder.toString().endsWith("?")) {
urlBuilder.append("&");
}
}
for (Map.Entry<String, Object> entry : params.entrySet()) {
urlBuilder.append("&" + entry.getKey())
.append("=")
.append(encode(entry.getValue().toString()));
}
return urlBuilder.toString();
}
private static String encode(String str) {
try {
return URLEncoder.encode(str, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
public interface ICallback {
void onSuccess(String result);
void onError(String result);
}
/**
* 回调接口的json版本的实现类
* 用于把网络返回的json字符串转让换成对象(Result就是用户接收数据的类型)
*/
public abstract class HttpCallback<Result> implements ICallback {
@Override
public void onSuccess(String result) {//result就是网络回来的数据
//result把转换成用户需要的对象
Gson gson=new Gson();
//需要得到用户输入的对象对应的字节码是什么样的
//得到用户接收数据的对象对应的class
Class<?> clz=analysisClassInfo(this);
Result objResult=(Result)gson.fromJson(result,clz);
//回调给调用层
this.onSuccess(objResult);
}
public abstract void onSuccess(Result result);
/**
* 获取反省<>中的类型
* @param object
* @return
*/
private Class<?> analysisClassInfo(Object object) {
//getGenericSuperclass可以得到包含原始类型,参数化类型,数组,类型变量,基本数据
Type genType=object.getClass().getGenericSuperclass();
//获取参数化类型
Type[] params=((ParameterizedType)genType).getActualTypeArguments();
return (Class<?>)params[0];
}
@Override
public void onError(String result) {
}
}
public class OkHttpProcessor implements IHttpProcessor {
private OkHttpClient mOkHttpClient;
private Handler myHandler;
public OkHttpProcessor() {
mOkHttpClient = new OkHttpClient();
myHandler = new Handler();
}
private RequestBody appendBody(Map<String, Object> params) {
FormBody.Builder body = new FormBody.Builder();
if (params == null || params.isEmpty()) {
return body.build();
}
for (Map.Entry<String, Object> entry : params.entrySet()) {
body.add(entry.getKey(), entry.getValue().toString());
}
return body.build();
}
@Override
public void post(String url, Map<String, Object> params, final ICallback callback) {
RequestBody requestBody = appendBody(params);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callback.onError(e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String result = response.body().string();
if (response.isSuccessful()) {
myHandler.post(new Runnable() {
@Override
public void run() {
callback.onSuccess(result);
}
});
}
}
});
}
@Override
public void delete(String url, Map<String, Object> params, ICallback callback) {
}
}
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 传入具体的实现
HttpHelper.getInstance().init(new OkHttpProcessor());
// HttpHelper.getInstance().init(new VolleyProcessor(this));
}
}
public class MainModel implements IModel {
@Override
public void loadInfo(OnLoadListener loadListener) {
//测试隔离层代码
String url="url";
HashMap<String,Object> params=new HashMap<>();
params.put("param","");
HttpHelper.getInstance().post(url, params, new HttpCallback<TestBean>() {
@Override
public void onSuccess(TestBean testBean) {
Log.e("onSuccess",testBean.toString());
}
@Override
public void onError(String result) {
}
});
}
}
使用这种方式的好处是当想切换新的框架时,不需要改业务逻辑的代码,只需要在 application 初始化的时候切换一下新增的 processor 实现就可以了。同样图片框架,数据库,都可以使用这种方式来切换。
源码已经上传有需要的可以下载自己完善 跳转连接