MVP(Model-View-Presenter)框架整理

去年因为项目重构写了个 MVP 模式的框架 Demo,一直想整理一下,但上半年确实浪得一(*),现在难得有点空闲,马上整理一下,作为回顾,查漏补缺。

相关文章:《 RxJava + Retrofit 应用整理 》

MVP(Model-View-Presenter)框架整理_第1张图片

接口:

/**
 * @author 小侨
 * @time 2017/7/21  10:17
 * @desc View 层接口/约束类
 */
public interface IView {

    void showLoading();

    void hideLoading();

    void showToast(String message);
}

在 MVP 模式中:View 负责显示,Presenter 负责逻辑处理,Model 提供数据;且 View 并不直接使用 Model,它们之间的通信是通过 Presenter 来进行的,所有的交互都发生在 Presenter 内部,所以要在 Presenter 中对 View 进行绑定,然后退出时解绑避免内存泄漏。

/**
 * @author 小侨
 * @time 2017/7/21  10:17
 * @desc Presenter 层接口/约束类
 */
public interface IPresenter {

    void attachView(IView view);

    void detachView();
}

因为获取数据的内容、方式不同,所以没有特定的通用方法可以抽取,但是为求样式的统一,所以新建 IModel 接口,而且 IModel 可用于标明子类的类型。

/**
 * @author 小侨
 * @time 2017/7/21  11:05
 * @desc Model 层接口/约束类
 */
public interface IModel {

}

基类:

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

/**
 * @author 小侨
 * @time 2017/7/21  10:10
 * @desc activity 基类
 */
public abstract class BaseActivity<P extends BasePresenter> 
                                  extends AppCompatActivity implements IView {
    // 此处的泛型可以理解成用于提醒使用者创建 Presenter 以及规范创建的 Presenter 类型
    // 标记 View 绑定的 Presenter

    protected Context mContext;
    protected P mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(setContentView());
        mContext = this;
        initView();

        // 先创建才能绑定/解绑
        mPresenter = initPresenter();
        mPresenter.attachView(this);

        initData();
    }

    protected abstract int setContentView();

    protected abstract void initView();

    protected abstract P initPresenter();

    protected abstract void initData();

    @Override
    public void showLoading() {
        showToast("--- 下载中 ---");
    }

    @Override
    public void hideLoading() {
        showToast("+++ 下载完 +++");
    }

    @Override
    public void showToast(String message) {
        // TODO: 如果子类有自己的 showLoading()、hideLoading()、showToast(),可以重写
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.detachView();
        }
    }
}
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * @author 小侨
 * @time 2017/7/26  17:56
 * @desc fragment 基类(来源:http://www.jianshu.com/p/651146bd0688)
 */
public abstract class BaseFragment<P extends BasePresenter> extends Fragment implements IView {
    // 此处的泛型可以理解成用于提醒使用者创建 Presenter 以及规范创建的 Presenter 类型
    // 标记 View 绑定的 Presenter

    protected Context mContext; // activity 的上下文
    protected P mPresenter;
    private int mLayoutId;

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

    public BaseFragment getFragment() {
        return this;
    }

    /**
     * 绑定 activity
     */
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mContext = context;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // TODO: Activity 重新创建时,会重新构建它所管理的 Fragment,原先的 Fragment 的字段值将会全部丢失,
        // 但是通过 Fragment.setArguments(Bundle bundle)方法设置的 bundle 会保留下来。
        // 所以尽量使用 Fragment.setArguments(Bundle bundle)方式来传递参数
        Bundle bundle = getArguments();
        if (bundle != null) {
            mLayoutId = bundle.getInt("layoutId");
        }

        mPresenter = initPresenter();
    }

    /**
     * 运行在 onCreate 之后
     * 生成 view 视图
     */
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(getContentView(), container, false);
        return view;
    }

    /**
     * 运行在 onCreateView 之后
     * 加载数据
     */
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // 由于 fragment 生命周期比较复杂,所以 Presenter 在 onCreateView 创建视图之后再进行绑定,不然会报空指针异常
        mPresenter.attachView(this);
        initView();
    }

    protected abstract P initPresenter();

    private int getContentView() {
        mLayoutId = setContentView();
        Bundle bundle = new Bundle();
        bundle.putInt("layoutId", mLayoutId);
        setArguments(bundle);
        return mLayoutId;
    }

    protected abstract int setContentView();

    protected abstract void initView();

    @Override
    public void showLoading() {
        showToast("--- 下载中 ---");
    }

    @Override
    public void hideLoading() {
        showToast("+++ 下载完 +++");
    }

    /**
     * 跳转 fragment
     */
    public void startFragment(Fragment toFragment, String tag) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.hide(this).add(android.R.id.content, toFragment, tag);
        fragmentTransaction.addToBackStack(tag);
        fragmentTransaction.commitAllowingStateLoss();
    }

    /**
     * fragment 回退
     */
    public void onBack() {
        getFragmentManager().popBackStack();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.detachView();
        }
    }
}
/**
 * @author 小侨
 * @time 2017/7/21  10:11
 * @desc Presenter 基类
 */
public abstract class BasePresenter<V extends IView, M extends IModel> implements IPresenter {
    // 此处的泛型可以理解成用于提醒使用者 Presenter 是用来操作 Model,然后更新 View 的
    // 标记 Presenter 绑定的 View 和 Model

    protected V mView;
    protected M mModel;

    public BasePresenter() {
        mModel = initModel();
    }

    protected abstract M initModel();

    @Override
    public void attachView(IView view) {
        mView = (V) view;
    }

    @Override
    public void detachView() {
        mView = null;
    }
}

就像上面说的,因为获取数据的内容、方式不同,所以没有特定的通用方法可以抽取,但是为求样式的统一,所以新建 IModel 接口,用 IModel 标明子类的类型即可,就不用专门创建 BaseModel。

实现:

/**
 * @author 小侨
 * @time 2017/7/21  10:23
 * @desc movie 页面合约类
 */
public class MovieContract {

    /**
     * V 视图层
     */
    interface IView {
        void showData(String s);
    }

    /**
     * P 视图与逻辑处理的连接层
     */
    interface IPresenter {
        void getData();
    }

    /**
     * M 逻辑(业务)处理层
     */
    interface IModel {
        String downloadData();
    }
}

通过上面的合约类来约束下面的 M、V、P 各自需要、可以做的事情,其实这样是更有利于维护的:M、V、P 是环环相扣的,在 View 呈现某一效果,都得跟 P、M 联动;那么如果要添加、删除、修改某个特定功能,就可以在合约类中统一修改,实现类受到约束,就必须去响应这些实现。

import android.view.View;
import android.widget.TextView;

/**
 * @author 小侨
 * @time 2017/7/21  10:25
 * @desc movie 页面 View 层
 */
public class MovieActivity extends BaseActivity<MoviePresenter> 
                                implements MovieContract.IView {

    // 互相关联
    // MovieActivity 类中:extends BaseActivity
    // MoviePresenter 类中:extends BasePresenter

    private TextView mTv;

    @Override
    protected int setContentView() {
        return R.layout.activity_movie;
    }

    @Override
    protected void initView() {
        mTv = (TextView) findViewById(R.id.tv);
    }

    @Override
    protected MoviePresenter initPresenter() {
        return new MoviePresenter();
    }

    @Override
    protected void initData() {
        mPresenter.getData();
    }

    @Override
    public void showData(String s) {
        mTv.setText(s);
    }
}
/**
 * @author 小侨
 * @time 2017/7/21  10:25
 * @desc movie 页面 Presenter 层
 */
public class MoviePresenter extends BasePresenter<MovieActivity, MovieModel> 
                                implements MovieContract.IPresenter {

    // MoviePresenter 已经在 MovieActivity 中 onCreate 时绑定了
    // MoviePresenter 已经在 MoviePresenter 新建时调用 initModel()方法绑定了
    @Override
    protected MovieModel initModel() {
        return new MovieModel();
    }

    @Override
    public void getData() {
        mView.showData(mModel.downloadData());
    }
}
/**
 * @author 小侨
 * @time 2017/7/21  10:30
 * @desc movie 页面 Model 层
 */
public class MovieModel implements IModel, MovieContract.IModel {

    @Override
    public String downloadData() {
        return "abc";
    }
}

补充:
因为之前项目用的网络请求框架是 RxJava + Retrofit 【RxJava + Retrofit 应用整理】,所以在 Presenter 中使用 Medel 能直接回调 onSuccess 和 onError 方法,但是有一些网络请求框架没有这么方便,所以要自己写回调接口

/**
 * Model 层获取数据回调接口
 */
public interface IModelCallBack {

    void onSuccess(T data);

    void onError(String errorMsg);
}
    // Model
    @Override
    public void downloadData(IModelCallBack iModelCallBack) {
        iModelCallBack.onSuccess("Success");
        iModelCallBack.onError("Error");
    }
    // Presenter
    @Override
    public void getData() {
        mModel.downloadData(new IModelCallBack() {
            @Override
            public void onSuccess(String data) {

            }

            @Override
            public void onError(String errorMsg) {

            }
        });
    }

Demo 的 Github地址:
https://github.com/ZhangZeQiao/GeneralFramework.git

你可能感兴趣的:(Android,常用代码)