Android 从 MVC 到 MVP 的演变

MVC 简介

  • MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。
    在这里插入图片描述
  • Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
  • View(视图) - 视图代表模型包含的数据的可视化。
  • Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。

下面是一个简单的 mvc 实现的例子

  • MainActivity如下,就是简单的一个ListView展示了个列表,适配器就不粘出来了
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));
    }

}

效果图如下:
代码效果图

  • 以上是简单的一个 mvc 实现的逻辑,view 和 data 绑定都放在了activity中,如果代码逻辑比较复杂的页面 将来维护activity页面会非常麻烦,所以要想办法简化代码,降低耦合度。

MVP 简介和基本框架搭建

  • 简称:MVP 全称:Model-View-Presenter ;MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。
    在这里插入图片描述
    接下来我们按照图中所展示的来一点点改造上面的代码。

  • 上面的图所示 创建 view 接口

public interface IMainView {
	// 返回data
    void showData(List<InfoBean> infoBeans);
}
  • 创建 IModel接口和 model实现类
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);
    }
}
  • 创建 Presenter

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);
            }
        });
    }
}
  • view层代码
// 实现 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的重构。

MVP 重构(1)

  • 防止内存泄漏
  • 解决方案1是传入P层的View采用若引用方式。
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);
            }
        });
    }
}
  • 采用管理生命周期的方式防止内存泄漏 presenter中去掉构造方法
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);
            }
        });
    }
}

上面的代码不能每次使用的时候都需要写这么多的代码,怎么简化呢接下来继续抽离。

MVP 重构(2)

抽离到base。

  • BaseActiity
// 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();
    }
}
  • BasePresenter
/**
 * @param <V> 传入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;
        }
    }
}

  • 具体的Presenter
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);
            }
        });
    }

}

  • MainActivity 使用

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);

}
  • 新建 Helper 做初始化和调用工作
/**
 * 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();
        }
    }
}
  • 新建 ICalback
public interface ICallback {
    void onSuccess(String result);

    void onError(String result);
}
  • 新建解析 callBack数据的具体callback
/**
 * 回调接口的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) {
    }
}
  • 新建具体的网络访问 如 okHttp volley 等
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) {

    }

}

  • 使用:app添加对 commonLib 的依赖在 app 中的 applicaiton 中初始化操作
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // 传入具体的实现
        HttpHelper.getInstance().init(new OkHttpProcessor());
//        HttpHelper.getInstance().init(new VolleyProcessor(this));
    }
}
  • 在 mvp的 model层调用访问
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 实现就可以了。同样图片框架,数据库,都可以使用这种方式来切换。
源码已经上传有需要的可以下载自己完善 跳转连接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章