MVP模式入门

    |     2016年9月1日   |   Android经验   |     0 条评论   |    2115

应用开发总会碰到 UI、数据、业务逻辑。逻辑一复杂就要分框架。最早是 MVC,后来也用在 Android 上。MVP 可以看作 MVC 的升级:把 Activity 从「又当 View 又当 Controller」里拆出来。


一、概述:MVC 为什么会胖

MVC 架构

M : 业务层和模型层,相当与javabean和我们的业务请求代码
   model层主要负责:
   · 从网络,数据库,文件,传感器,第三方等数据源读写数据。
   · 对外部的数据类型进行解析转换为APP内部数据交由上层处理。
   · 对数据的临时存储,管理,协调上层数据请求。

 V : 视图层,对应Android的layout.xml布局文件
 C : 控制层,对应于Activity中对于UI 的各种操作

纸面很清晰,实际开发里请求代码经常塞进 Activity;layout.xml 只能给默认 UI,视图变化也被丢进 Activity;Activity 本身还承担控制层。于是 Activity 集齐 MVC,从几百行涨到几千行,维护成本越来越高。

新的 MVP 架构

M : 还是业务层和模型层
V : 视图层的责任由Activity来担当
P : 新成员Prensenter 用来代理 C(control) 控制层,是View和Model的中间“桥梁”,负责View和Model间的交互

MVP 与 MVC 最大的不同,是 Activity 的职责:从 C(控制层)变成 V(视图层),只管怎么显示。控制交给 Presenter。这样解开 Activity 对控制层和视图层的过度耦合。

mvp

角色 MVC 里谁扛 MVP 里谁扛
Model Bean + 请求(常被塞进 Activity) 仍负责数据源、解析、暂存
View layout.xml,变化却写在 Activity Activity / Fragment 只负责显示
控制 Activity 既是 C 又掺 V Presenter 当 View 与 Model 的桥

二、实例

用 MVP 实现下图效果:

cheap

第一步 View 层

先定义 UI 能做什么,Fragment 只实现接口、把点击交给 Presenter:

/**
 * 定义UI视图逻辑
 */
public interface ICheapView {
    void setListViewAdapter(ReadNewsAdapter adpater);
    void showToastMessage(String message);
    void setBannerLayoutData(List<AdsBean> list);
}

public class CheapFragment extends BaseMvpFragment<ICheapView, CheapPresenter> implements ICheapView, View.OnClickListener {
    private EditText mSearchKey;
    private ListView mListView;
    private FrameLayout mListHeadView;
    private BannerLayout mBannerLayout;

    public static CheapFragment newInstance() {
        CheapFragment fragment = new CheapFragment();
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        mListHeadView = (FrameLayout) inflater.inflate(fragment_cheap_banner_layout, null);
        return inflater.inflate(R.layout.fragment_cheap_layout, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mSearchKey = (EditText) getView().findViewById(R.id.com_search_edit);
        mBannerLayout = (BannerLayout) mListHeadView.findViewById(R.id.banner);
        mListView = (ListView) getView().findViewById(R.id.cheap_listview);
        mListView.addHeaderView(mListHeadView);
        mSearchKey.setOnClickListener(this);

        mPresenter.setListViewAdapter();
        mPresenter.setListBannerViewData();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.com_search_edit:
                getActivity().onSearchRequested();
                break;
        }
    }

    @Override
    public CheapPresenter initPresenter() {
        return new CheapPresenter(getActivity());
    }

    @Override
    public void setListViewAdapter(ReadNewsAdapter adpater) {
        mListView.setAdapter(adpater);
    }

    @Override
    public void showToastMessage(String message) {
        Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void setBannerLayoutData(List<AdsBean> urls) {
        List<String> list = new ArrayList<>();
        for (AdsBean ads : urls) {
            list.add(ads.getImgsrc());
        }
        mBannerLayout.setViewUrls(list);
        //添加监听事件
        mBannerLayout.setOnBannerItemClickListener(new BannerLayout.OnBannerItemClickListener() {
            @Override
            public void onItemClick(int position) {
                showToastMessage(String.valueOf(position));
            }
        });
    }
}

第二步 Presenter 层

业务接口由 Presenter 实现:向 Model 要数据,成功后再调 View。

/**
 * 定义业务逻辑
 */
public interface ICheapPresenter {
    void setListViewAdapter();
    void setListBannerViewData();
}

public class CheapPresenter extends BasePresenter<ICheapView> implements ICheapPresenter{
    private ICheapModel mCheapModel;
    private ReadNewsAdapter mReadNewsAdapter;

    public CheapPresenter(Context context) {
        mCheapModel = new CheapModel();
        mReadNewsAdapter = new ReadNewsAdapter(context);
    }

    @Override
    public void setListViewAdapter() {
        mView.setListViewAdapter(mReadNewsAdapter);
    }

    @Override
    public void setListBannerViewData() {
        mCheapModel.setListAndAdsData(new CallbackListener<NewsTextBean>() {
            @Override
            public void onSuccess(NewsTextBean data) {
                List<NewsTextItemBean> newsTextItemBeanList = data.getT1348647909107();
                mReadNewsAdapter.addDatas(0,newsTextItemBeanList);
                List<AdsBean> adsLists = newsTextItemBeanList.get(0).getAds();
                mView.setBannerLayoutData(adsLists);
            }

            @Override
            public void onFailure(String message) {
                mView.showToastMessage(message);
            }
        });
    }
}

第三步 Model 层

网络请求放在 Model,这里用 Retrofit 拉网易新闻接口:

public interface ICheapModel {
    void setListAndAdsData(final CallbackListener<NewsTextBean> listener);
}

public class CheapModel implements ICheapModel {
    @Override
    public void setListAndAdsData(final CallbackListener<NewsTextBean> listener) {
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl("http://c.m.163.com/")
                .build();

        RetrofitCall call = retrofit.create(RetrofitCall.class);
        Call<NewsTextBean> mCall = call.getNewsText();
        mCall.enqueue(new Callback<NewsTextBean>() {
            @Override
            public void onResponse(Call<NewsTextBean> call, Response<NewsTextBean> response) {
                NewsTextBean newsTextBean =  response.body();
                listener.onSuccess(newsTextBean);
            }

            @Override
            public void onFailure(Call<NewsTextBean> call, Throwable t) {
                listener.onFailure(t.getMessage());

            }
        });
    }
}

三、基类:绑定与解开 View

BaseMvpFragment 负责实例化 Presenter;BasePresenter 持有 View 引用,销毁时清掉,避免泄漏。

public abstract class BasePresenter<T> {
    /*这个基类的Presenter 主要的作用就是将当前Presenter持有的view 在合适的时候给清除掉*/
    public T mView;

    public void attach(T mView) {
        this.mView = mView;
    }

    public void dettach() {
        mView = null;
    }

}
public abstract class BaseMvpFragment<V, T extends BasePresenter<V>> extends Fragment {
    /*定义一个Presenter 用于解绑持有的View
       *
       * 在onAttach进行初始化Presenter的操作
       * 在onCreate中进行绑定
       * 在onDestroy 中进行解绑
       *
       **/
    public T mPresenter;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mPresenter = initPresenter();
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter.attach((V) this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mPresenter.dettach();
    }

    /* 这里是适配为不同的View 装载不同Presenter*/
    public abstract T initPresenter();
}

四、参考学习

一句话总结:MVP 让 Activity/Fragment 只做 View,Presenter 当桥,Model 管数据和请求;基类里 attach/detach,页面销毁时把 View 引用清掉。

转载请注明来源:MVP模式入门
本文链接地址:https://ai.zhousir.top/?p=1702
回复 取消