Android MVP 模式与使用

参考资料:####

MVP模式也使用了一段时间,没有特定成熟的框架,各有各的实现方式;之前不太清楚为什么要这么设计,后来项目中,增加了新的模块,完全采用MVP来实现,刚开始很痛苦,分离了一大堆接口,直到有一次,某个业务方,要求替换页面,由于采用了MVP,发现只需修改V层,然后根据约定的接口方式,一个个实现,即可,M与P层,不动;这才有点感觉了。
通过查看崩溃日志,发现采用MVP设计的功能,崩溃率很低;

目前总结的2个好处:

  1. 降低耦合度,让各层各司其事,给测试与开发带来好处;移动端开发,变化最大的可能就是界面,如果在界面中,柔和了逻辑与数据处理,导致维护成本急剧增加;
  2. 降低出错率;

目前遇到的问题:

  1. 大量接口的引入,对开发的前期接口设计能力有一定要求;
  2. view层,界面是会回收的,如何恢复,目前没找到很好的方案,实际操作中,我还是将这部分,让 activity or fragment 自己处理;
  3. 关于对应一个 页面 V是否可使用多个P问题;在实际中,我们的项目都是一个V对应一个P,但是P可能会对应多个M,来完成功能;
  4. 如果在V层中,需要取消某一个网络请求(V不销毁),这种情况,目前我们项目没有遇到过,但如有此情况,就智者见智;

demo代码:##

https://github.com/zhaoyubetter/mvpDemo

1. MVP的分层####

  • M 数据层:所有与数据操作相关的都放入这里,比如:网络请求,数据库操作等;
  • P 业务逻辑层:与业务相关的操作,放入这里,P分别引用了M与V层;
    M不直接与V打交道,区别于MVC;
    http://www.jianshu.com/p/ee8125b1429d
  • V 视图层:用来展示页面,如:Activity,Fragment等;

2.MVP调用简单类图

MVP

登录图例:

扫描二维码关注公众号,回复: 11942342 查看本文章

登录图例

3.show me the code MVP框架

  1. View层公共接口:

/**
 * MVP中View层抽象接口
 */
public interface IMVPView {
    /**
     * 加载填充界面数据时显示加载进度
     * @param msg 提示文字。传null时显示默认
     */
    void showLoading(String msg);

    /**
     * 失败
     * @param msg 提示文字。传null时显示默认
     */
    void showError(String msg);
    void removeLoading();
    /**
     * 获取Context对象
     */
    Context getContext();
}

2.Model(Repo)层公共接口:

/**
 * mvp中M层抽象接口,这里采用 Repo 仓库,参考了google mvp demo
 */
public interface IMVPRepo {
    /**
     * 销毁方法
     * 1,释放P引用<br/>
     * 2,释放具体逻辑上的数据
     */
    void onDestroy();
}

3.Presenter层公共接口:

/**
 * MVP层中P层顶层抽象
 */
public interface IMVPPresenter {
    /**
     * 加载填充界面需要的数据
     */
    void onCreate();

    /**
     * <ol>
     *     <li>释放View</li>
     *     <li>释放Model</li>
     *     <li>释放其他资源</li>
     * </ol>
     */
    void onDestroy();
}

4.Presenter抽象类的引入,在抽象层,没有强制要求需要传递 M层,
是这样考虑的,

在传统的 JavaEE开发中,Dao层Service层是分开的,
一个Service层可以引入多个Dao层,来实现业务;
在Android中,我们给P层模拟类似于EE开发中的Service层与Dao层的结合,避免层次太多,也就是说:我们可在P层引入多个M,来完成业务;
另: P层只引用一个V,一般情况下,我们是一个页面对应一个具体的业务,多V对应一P,还没用接触过
总结: 可以多M对应一P对应一V;

/**
 * MVP层中P层抽象类
 */
public abstract class AbsMVPPresenter<T extends IMVPView> implements IMVPPresenter {

    protected WeakReference<T> mViewRef;

    /**
     * 必要的构造方法
     * 可以在构造方法中创建对应的Model
     *
     * @param view : 绑定对应的View
     */
    public AbsMVPPresenter(T view) {
        this.mViewRef = new WeakReference<>(view);
    }

    /**
     * 界面恢复时,调用该方法通过M层恢复数据。不需要的界面不要重写
     * 此方法为扩展,界面恢复时的处理,目前暂没想好放在哪里,这里留一个入口
     *
     * @param bundle 恢复数据时需要的参数
     */
    public void restore(Bundle bundle) {
        //empty
    }

    /**
     * view 是否存活
     *
     * @return
     */
    protected boolean isAlive() {
        return mViewRef != null && mViewRef.get() != null && mViewRef.get().getContext() != null;
    }
  
    @Override
    public void onDestroy() {
        if (null != mViewRef) {
            mViewRef.clear();
            mViewRef = null;
        }
    }
}

4.MVP 框架的实战练习

需求:访问网络,将结果显示在界面上,现设计约束接口如下:

4. 1 新建Contract契约接口:######

每个业务建立自己的契约接口,如下:

public interface SimpleContract {
    interface View extends IMVPView {
        void showContent(String data);
    }

    interface Presenter extends IMVPPresenter {
        void doGetData(String url);
    }

    interface Repo extends IMVPRepo {
        void getData(String url, final LoadDataCallback<String> callback);
    }
}

4. 2 先从M层下手:######

先将M层实现,这个时候,我们可以针对M来编写测试脚本,测试M层

public class SimpleRepo implements SimpleContract.Repo {

    private AbsRequest mHttpRequest;

    @Override
    public void getData(final String url, final LoadDataCallback<String> callback) {
        mHttpRequest = new OkHttpRequest.Builder().url(url).callback(new AbsRequestCallBack<String>() {
            @Override
            public void onSuccess(Response<String> response) {
                super.onSuccess(response);
                callback.onDataLoaded(response.responseBody);
            }

            @Override
            public void onFailure(Throwable e) {
                super.onFailure(e);
                callback.onDataNotAvailable(e.toString(), 0);
            }
        }).build();
        mHttpRequest.request();
    }

    @Override
    public void onDestroy() {
        // 销毁网络请求
        if (mHttpRequest != null) {
            mHttpRequest.cancel();
        }
    }
}

4. 2 编写P层:#####

P层比较关键,M与V如何进行交互,就在这里了

public class SimplePresenter extends AbsMVPPresenter<SimpleContract.View> implements SimpleContract.Presenter {
    /**
     * 接口
     */
    SimpleContract.Repo mRepo;

    /**
     * 也可以在 V 层中传入M,这里没这样做,各有各的好处
     * @param view
     */
    public SimplePresenter(SimpleContract.View view) {
        super(view);
        mRepo = new SimpleRepo();
    }

    @Override
    public void onCreate() {
    }

    @Override
    public void doGetData(String url) {
        if (!isAlive()) return;
        final SimpleContract.View view = mViewRef.get();

        mRepo.getData(url, new LoadDataCallback<String>() {
            @Override
            public void onDataLoaded(String data) {
                if (isAlive()) {
                    view.showContent(data);
                }
            }

            @Override
            public void onDataNotAvailable(String msg, int code) {
                if (isAlive()) {
                    view.showError(msg);
                }
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mRepo != null) {
            mRepo.onDestroy();
        }
    }
}

4. 3 编写V层:######

V层,再这里简单实现契约接口的View接口:

public class SimpleActivity extends AppCompatActivity implements SimpleContract.View {
    private ProgressDialog mDialog;
    private TextView content;
    private SimpleContract.Presenter mPresenter;
    private EditText et;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple);
        content = (TextView) findViewById(R.id.content);
        et = (EditText) findViewById(R.id.et);

        // 初始化P
        mPresenter = new SimplePresenter(this);
        findViewById(R.id.request).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final String url = et.getText().toString();
                content.setText("");
                mPresenter.doGetData(url);
            }
        });
    }

    @Override
    public void showLoading(String msg) {
        if (mDialog == null) {
            mDialog = new ProgressDialog(this);
        }
        mDialog.setMessage(msg);
        mDialog.show();
    }

    @Override
    public void showError(String msg) {
        Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void removeLoading() {
        if (mDialog != null) {
            mDialog.dismiss();
        }
    }

    @Override
    public Context getContext() {
        return getApplicationContext();
    }

    @Override
    public void showContent(String data) {
        content.setText(data);
    }
}



作者:zhaoyubetter
链接:https://www.jianshu.com/p/7cdfd5fd9d14
 

猜你喜欢

转载自blog.csdn.net/az44yao/article/details/108879212