欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android MVP 模式与使用

程序员文章站 2024-03-15 19:28:54
...

参考资料:####

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调用简单类图

Android MVP 模式与使用

MVP

登录图例:

 

Android MVP 模式与使用

登录图例

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