Android-architecture之MVC、MVP、MVVM、Data-Binding

  • 传送门
  • MVC
    • 结构简介
    • 实例分析
    • 总结
  • MVP
    • 结构简介
    • 为什么使用MVP模式
    • 实例分析
    • MVP与MVC的异同
  • MVVM
  • Data-Binding
    • 前言
    • 参考链接

传送门

Android Architecture(Is Activity God?)

MVC

结构简介

实例分析

Controller控制器式

public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener {

    private WeatherModel weatherModel;
    private Dialog loadingDialog;
    private EditText cityNOInput;
    private TextView city;
    private TextView cityNO;
    private TextView temp;
    private TextView wd;
    private TextView ws;
    private TextView sd;
    private TextView wse;
    private TextView time;
    private TextView njd;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        weatherModel = new WeatherModelImpl();
        initView();
    }

    /**
     * 初始化View
     */
    private void initView() {
        cityNOInput = findView(R.id.et_city_no);
        city = findView(R.id.tv_city);
        cityNO = findView(R.id.tv_city_no);
        temp = findView(R.id.tv_temp);
        wd = findView(R.id.tv_WD);
        ws = findView(R.id.tv_WS);
        sd = findView(R.id.tv_SD);
        wse = findView(R.id.tv_WSE);
        time = findView(R.id.tv_time);
        njd = findView(R.id.tv_njd);
        findView(R.id.btn_go).setOnClickListener(this);

        loadingDialog = new ProgressDialog(this);
        loadingDialog.setTitle(加载天气中...);

    }

    /**
     * 显示结果
     *
     * @param weather
     */
    public void displayResult(Weather weather) {
        WeatherInfo weatherInfo = weather.getWeatherinfo();
        city.setText(weatherInfo.getCity());
        cityNO.setText(weatherInfo.getCityid());
        temp.setText(weatherInfo.getTemp());
        wd.setText(weatherInfo.getWD());
        ws.setText(weatherInfo.getWS());
        sd.setText(weatherInfo.getSD());
        wse.setText(weatherInfo.getWSE());
        time.setText(weatherInfo.getTime());
        njd.setText(weatherInfo.getNjd());
    }

    /**
     * 隐藏进度对话框
     */
    public void hideLoadingDialog() {
        loadingDialog.dismiss();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_go:
                loadingDialog.show();
                weatherModel.getWeather(cityNOInput.getText().toString().trim(), this);
                break;
        }
    }

    @Override
    public void onSuccess(Weather weather) {
        hideLoadingDialog();
        displayResult(weather);
    }

    @Override
    public void onError() {
        hideLoadingDialog();
        Toast.makeText(this, 获取天气信息失败, Toast.LENGTH_SHORT).show();
    }

    private  T findView(int id) {
        return (T) findViewById(id);
    }

}

Model模型

public interface WeatherModel {
    void getWeather(String cityNumber, OnWeatherListener listener);
}
public class WeatherModelImpl implements WeatherModel {

    @Override
    public void getWeather(String cityNumber, final OnWeatherListener listener) {

        /*数据层操作*/
        VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html,
                Weather.class, new Response.Listener() {
                    @Override
                    public void onResponse(Weather weather) {
                        if (weather != null) {
                            listener.onSuccess(weather);
                        } else {
                            listener.onError();
                        }
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        listener.onError();
                    }
                });
    }

总结

  • 扩展性好、维护性、模块职责明确
  • 耦合性低(解耦)、V和M非真正意义上的分离

什么时候适合使用MVC设计模式?

当一个小的项目且无需频繁修改需求就不用MVC框架来设计了,那样反而觉得代码过度设计,代码臃肿。一般在大的项目中,且业务逻辑处理复杂,页面显示比较多,需要模块化设计的项目使用MVC就有足够的优势了。

MVP

结构简介

为什么使用MVP模式

在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,并接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。当我们将其中复杂的逻辑处理移至另外的一个类(Presneter)中时,Activity其实就是MVP模式中View,它负责UI元素的初始化,建立UI元素与Presenter的关联(Listener之类),同时自己也会处理一些简单的逻辑(复杂的逻辑交由Presenter处理).

另外,回想一下你在开发Android应用时是如何对代码逻辑进行单元测试的?是否每次都要将应用部署到Android模拟器或真机上,然后通过模拟用户操作进行测试?然而由于Android平台的特性,每次部署都耗费了大量的时间,这直接导致开发效率的降低。而在MVP模式中,处理复杂逻辑的Presenter是通过interface与View(Activity)进行交互的,这说明了什么?说明我们可以通过自定义类实现这个interface来模拟Activity的行为对Presenter进行单元测试,省去了大量的部署及测试的时间。

实例分析

MVP模式

View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有Viwe层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。当View层某个界面需要展示某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。这就是MVP模式的整个核心过程。

官方模式图

android-architecture官方传送门

案例

这里以暴风体育中的话题列表为例来进行介绍:

TopicModel

public interface TopicModel {
    /**
     * 加载话题列表首页数据
     *
     * @param context
     * @param listener
     */
    void loadTopicList(Context context, TopicModelImpl.OnLoadTopicListListener listener);

    /**
     * 从本地数据库中获取我关注的话题数据
     *
     * @param context
     * @param listener
     * @return
     */
    ArrayList<TopicItem> loadFollowTopic(Context context, TopicModelImpl.OnLoadTopicListListener listener);

    /**
     * 全部话题加载更多数据
     *
     * @param context
     * @param paramMap
     * @param listener
     */
    void loadMoreAllTopic(Context context, Map<String, String> paramMap, TopicModelImpl.OnLoadTopicListListener listener);

    /**
     * 更新我关注的话题的最新帖子数和帖子最近的更新时间
     *
     * @param context
     * @param threadItem
     * @param listener
     */
    void updateThreadItem(final Context context, ThreadItem threadItem, TopicModelImpl.OnLoadTopicListListener listener);
}

TopicPresenter

public interface TopicPresenter {
    /**
     * 加载话题列表首页数据
     *
     * @param context
     */
    void loadTopicList(Context context);

    /**
     * 全部话题加载更多数据
     *
     * @param context
     * @param paramMap
     */
    void loadMoreAllTopic(Context context, Map<String, String> paramMap);

    /**
     *
     * @param context
     * @return
     */
    ArrayList<TopicItem> loadFollowTopic(Context context);

}

TopicView

public interface TopicView {
    void showProgress();

    void addTopics(List<TopicItem> topicList);

    void addSwipeUpItem(SwipeUpItem item);

    void addLoadMoreTopics(List<TopicItem> topicList);

    void hideProgress();

    void showLoadFailMsg();

    //二次请求需要重新刷新界面
    void notifyAdapter();
}

TopicModelImpl

/**
 * DES:
 * Created by sushuai on 2016/4/13.
 */
public class TopicModelImpl implements TopicModel {
    private static final String TAG = "TopicModelImpl";

    /**
     * 加载话题列表首页数据
     *
     * @param context
     * @param listener
     */
    @Override
    public void loadTopicList(final Context context, final OnLoadTopicListListener listener) {
        AsyncHttpRequest.doASynGetRequest(context, UrlContainer.HOME_TOPIC, null, true, new AsyncHttpRequest.CallBack() {

            @Override
            public void fail(String ret) {
                listener.onFailure(Net.ErrorNo.NO_DATA);
            }

            @Override
            public void call(String data) {
                try {
                    ArrayList<TopicItem> items = (ArrayList<TopicItem>) TopicListDataParseUtils.readJsonTopicLists(data, listener);
                    //items.addAll(0, loadFollowTopic(context, listener));
                    if (items != null) {
                        listener.onSuccess(items);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                    listener.onFailure(Net.ErrorNo.ERROR_JSON);
                }
            }
        });
    }

    /**
     * 从本地数据库中获取我关注的话题数据
     *
     * @param context
     * @param listener
     * @return
     */
    @Override
    public ArrayList<TopicItem> loadFollowTopic(Context context, final OnLoadTopicListListener listener) {
        ArrayList<TopicItem> items = new ArrayList<>();
        ArrayList<ThreadItem> ThreadItems = (ArrayList<ThreadItem>) FollowTopicDao.getInstance(context).getLatest3Topics();
        if (ThreadItems.size() <= 0) {
            return items;
        }
        for (int i = 0; i < ThreadItems.size(); i++) {
            ThreadItem threadItem = ThreadItems.get(i);
            updateThreadItem(context, threadItem, listener);
        }
        TopicItem meItem = new TopicItem();
        meItem.setType(TopicAdapter.TYPE_TOPIC_TITLE_ME);
        items.add(meItem);
        for (int i = 0; i < ThreadItems.size(); i++) {
            TopicItem topicItem = new TopicItem();
            topicItem.setType(TopicAdapter.TYPE_TOPIC_THREAD);
            topicItem.setOther(ThreadItems.get(i));
            items.add(topicItem);
        }

        return items;

    }

    /**
     * 更新我关注的话题的最新帖子数和帖子最近的更新时间
     *
     * @param context
     * @param threadItem
     * @param listener
     */
    @Override
    public void updateThreadItem(final Context context, final ThreadItem threadItem, final OnLoadTopicListListener listener) {
        Map<String, String> map = new HashMap<>();
        map.put(Net.Field.id, String.valueOf(threadItem.getId()));
        final int prePosts = threadItem.getCount();
        AsyncHttpRequest.doASynGetRequest(context, UrlContainer.GET_TOPIC_POSTS, (HashMap<String, String>) map, true, new AsyncHttpRequest.CallBack() {

            @Override
            public void fail(String ret) {
            }

            @Override
            public void call(String data) {
                try {
                    JSONObject jo = new JSONObject(data);
                    int errno = DataParseUtils.getJsonInt(jo, Net.Field.errno);
                    if (errno == Net.ErrorNo.SUCCESS) {
                        JSONObject jsonObj = DataParseUtils.getJsonObj(jo, Net.Field.data);
                        int count = DataParseUtils.getJsonInt(jsonObj, Net.Field.count);
                        long latest_update_tm = DataParseUtils.getJsonLong(jsonObj, Net.Field.latest_update_tm);
                        threadItem.setUpdateCount(count - prePosts);
                        threadItem.setCount(count);
                        threadItem.setUpdateTime(latest_update_tm);
                        FollowTopicDao.getInstance(context).updatePostsById(threadItem.getId(), count);
                        listener.onUpdateThreadItem();
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 全部话题加载更多数据
     *
     * @param context
     * @param paramMap
     * @param listener
     */
    @Override
    public void loadMoreAllTopic(Context context, Map<String, String> paramMap, final OnLoadTopicListListener listener) {
        AsyncHttpRequest.doASynGetRequest(context, UrlContainer.TOPIC_LIST, (HashMap<String, String>) paramMap, true, new AsyncHttpRequest.CallBack() {

            @Override
            public void fail(String ret) {
                listener.onFailure(Net.ErrorNo.NO_DATA);
            }

            @Override
            public void call(String data) {
                try {
                    ArrayList<TopicItem> items = (ArrayList<TopicItem>) TopicListDataParseUtils.readMoreAllTopic(data, listener);
                    if (items != null) {
                        listener.onLoadMoreAllTopics(items);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                    listener.onFailure(Net.ErrorNo.ERROR_JSON);
                }
            }
        });
    }

    public interface OnLoadTopicListListener {
        //加载话题列表首页数据成功
        void onSuccess(List<TopicItem> list);

        //加载话题列表首页数据失败
        void onFailure(int erroNo);

        //全部话题加载更多相关配置
        void onLoadMoreSwipeUp(SwipeUpItem item);

        //回去加载更多数据
        void onLoadMoreAllTopics(List<TopicItem> list);

        //更新我关注的话题的相关数据
        void onUpdateThreadItem();

    }

}

TopicPresenterImpl

/**
 * DES:
 * Created by sushuai on 2016/4/13.
 */
public class TopicPresenterImpl implements TopicPresenter, TopicModelImpl.OnLoadTopicListListener {
    private static final String TAG = "TopicPresenterImpl";
    private TopicModel mTopicModel;
    private TopicView mTopicView;

    public TopicPresenterImpl(TopicView topicView) {
        this.mTopicModel = new TopicModelImpl();
        this.mTopicView = topicView;

    }

    @Override
    public void loadTopicList(Context context) {
        mTopicModel.loadTopicList(context, this);
    }

    @Override
    public void loadMoreAllTopic(Context context, Map<String, String> paramMap) {
        mTopicModel.loadMoreAllTopic(context, paramMap, this);
    }

    @Override
    public ArrayList<TopicItem> loadFollowTopic(Context context) {
        return mTopicModel.loadFollowTopic(context,this);
    }

    @Override
    public void onSuccess(List<TopicItem> list) {
        mTopicView.hideProgress();
        mTopicView.addTopics(list);
    }

    @Override
    public void onFailure(int erroNo) {
        mTopicView.hideProgress();
        mTopicView.showLoadFailMsg();
    }

    @Override
    public void onLoadMoreSwipeUp(SwipeUpItem item) {
        mTopicView.addSwipeUpItem(item);
    }

    @Override
    public void onLoadMoreAllTopics(List<TopicItem> list) {
        mTopicView.addLoadMoreTopics(list);
    }

    @Override
    public void onUpdateThreadItem() {
        mTopicView.notifyAdapter();
    }
}

TabTopicFragment

/**
 * 话题
 * SuShuai
 * 2016/4/12 14:39
 */
public class TabTopicFragment extends BaseFragment implements TopicAdapter.AdapterCallback, TopicView, IHandlerMessage, XListView.IXListViewListener {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String TAG = "TabTopicFragment";
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;
    private XListView listView;
    private TopicAdapter topicAdapter;
    private ArrayList<TopicItem> topicList = new ArrayList<>();
    private ArrayList<TopicItem> homeList = new ArrayList<>();
    private ArrayList<TopicItem> followTopicList = new ArrayList<>();
    private TopicPresenter mTopicPresenter;
    private CommonHandler<TabTopicFragment> handler;
    private SwipeUpItem swipeUpItem;
    private View rootView;
    private String after = "";

    public TabTopicFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment TabTopicFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static TabTopicFragment newInstance(String param1, String param2) {
        TabTopicFragment fragment = new TabTopicFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        LogHelper.e(TAG, "SuS--> onAttach: ");
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
        mTopicPresenter = new TopicPresenterImpl(this);
        LogHelper.e(TAG, "SuS--> onCreate: ");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        LogHelper.e(TAG, "whb--> onCreateView: ");
        if (rootView == null) {
            rootView = inflater.inflate(R.layout.fragment_tab_topic, container, false);
            initViews(rootView);

        }
        return rootView;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        LogHelper.e(TAG, "SuS--> onActivityCreated: ");
        super.onActivityCreated(savedInstanceState);
        initData();
    }

    private void initData() {
        handler = new CommonHandler<TabTopicFragment>(this);
        topicAdapter = new TopicAdapter(getActivity(), this);
        listView.setAdapter(topicAdapter);
        if (NetUtils.isNetworkAvaliable(getActivity())) {
            showLoadingView();
            mTopicPresenter.loadTopicList(getActivity());
            followTopicList = mTopicPresenter.loadFollowTopic(getActivity());
        } else {
            if (topicList.size() > 0) {
                topicAdapter.update(topicList);
                ToastUtils.toast(getActivity(), "没有网络");
                listView.restListView();
                return;
            }
            showNetErroView(R.string.tips_net_error);
        }
    }

    private void initViews(View v) {
        setImmerseLayout(v.findViewById(R.id.common_back));
        setTitleBar(v, R.string.tab_topic);
        setLeftGone(v);
        listView = (XListView) v.findViewById(R.id.lv_topic);
        listView.setPullRefreshEnable(true);
        listView.setPullLoadEnable(true);
        listView.setAutoLoadEnable(true);
        listView.setXListViewListener(this);
    }

    @Override
    public void onAdapterCallback(int eventId, Object obj) {
        if (isAdded()) {
            BaofengStatistics.onUmengEvent(getActivity(), BfCountConst.TopicConst.BBS_MOREFOLLOW_CLICK);
            LogHelper.v("umeng", "bbs_morefollow_click  计数一次");
        }
        ActivityUtil.startActivity(getActivity(), MoreFollowTopicActivity.class, null, false);
    }

    @Override
    public void showProgress() {

    }

    @Override
    public void addTopics(List<TopicItem> topicList) {
        handler.obtainMessage(HandlerMsg.MSG_LOAD_TOPIC_LIST_SUC,
                topicList).sendToTarget();

    }

    @Override
    public void addSwipeUpItem(SwipeUpItem item) {
        if (item == null) {
            return;
        }
        handler.obtainMessage(HandlerMsg.MSG_LOAD_SWIPE_UP_ITEM,
                item).sendToTarget();
    }

    @Override
    public void addLoadMoreTopics(List<TopicItem> topicList) {
        handler.obtainMessage(HandlerMsg.MSG_LOAD_MORE_TOPICS,
                topicList).sendToTarget();
    }

    @Override
    public void hideProgress() {
        // handler.obtainMessage(HandlerMsg.MSG_DISMISS_LOADING).sendToTarget();
    }

    @Override
    public void showLoadFailMsg() {
        if (topicList == null || topicList.size() == 0) {
            handler.obtainMessage(HandlerMsg.MSG_SHOW_EMPTY_CONTENT).sendToTarget();
        }else {
            handler.obtainMessage(HandlerMsg.MSG_SHOW_FAIL).sendToTarget();
        }
    }

    @Override
    public void notifyAdapter() {
        handler.obtainMessage(HandlerMsg.MSG_NOTIFY_ADAPTER_CONTENT).sendToTarget();
    }

    @Override
    public void handlerCallback(Message msg) {
        switch (msg.what) {
            case HandlerMsg.MSG_LOAD_TOPIC_LIST_SUC:
                dealTopicListSuc(msg);
                break;
            case HandlerMsg.MSG_LOAD_SWIPE_UP_ITEM:
                SwipeUpItem item = (SwipeUpItem) msg.obj;
                this.swipeUpItem = item;
                break;
            case HandlerMsg.MSG_LOAD_MORE_TOPICS:
                dealLoadMoreTopics(msg);
                break;
            case HandlerMsg.MSG_DISMISS_LOADING:
                dismissLoadingView();
                break;
            case HandlerMsg.MSG_SHOW_EMPTY_CONTENT:
                showContentEmptyView();
                break;
            case HandlerMsg.MSG_NOTIFY_ADAPTER_CONTENT:
                topicAdapter.notifyDataSetChanged();
                break;
            case HandlerMsg.MSG_SHOW_FAIL:
                ToastUtils.toast(getActivity(),R.string.error_no);
                break;
            default:
                break;
        }
    }

    private void dealLoadMoreTopics(Message msg) {
        List<TopicItem> moreList = (List<TopicItem>) msg.obj;
        int count1 = listView.getLastVisiblePosition();
        int count2 = topicAdapter.getCount()-1+2;
        if (moreList.size() < swipeUpItem.getLimit() && count1 == count2) {
            ToastUtils.toast(getActivity(), "已到达底部");
        }
        if (moreList.size() > 0) {
            after = TabTopicUtil.getLastKey(moreList);
        }
        TabTopicUtil.filterDuplicatedTopic(moreList,homeList);
        this.topicList.addAll(moreList);
        topicAdapter.update(this.topicList);
        listView.restListView();
    }

    private void dealTopicListSuc(Message msg) {
        List<TopicItem> topicList = (List<TopicItem>) msg.obj;
        if (topicList.size() <= 0) {
            showContentEmptyView();
            return;
        }
        after = TabTopicUtil.getLastKey(topicList);
        TabTopicUtil.removeDuplicateWithOrder(topicList);
        topicList.addAll(0,followTopicList);
        this.topicList = (ArrayList<TopicItem>) topicList;
        this.homeList = (ArrayList<TopicItem>) topicList;
        topicAdapter.update(topicList);
        dismissLoadingView();
        listView.restListView();
    }

    @Override
    public void onRefresh() {
        //handler.postDelayed(new Runnable() {
        // @Override
        //public void run() {
        if (NetUtils.isNetworkAvaliable(getActivity())) {
            mTopicPresenter.loadTopicList(getActivity());
        } else {
            if (topicList.size() > 0) {
                ToastUtils.toast(getActivity(), "没有网络");
                listView.restListView();
                return;
            }
            showNetErroView(R.string.tips_net_error);
        }
        // }
        //}, 2000);
    }

    @Override
    public void onLoadMore() {

        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Map<String, String> m = new HashMap<>();
                int size = topicList.size();
                if (size <= 0)
                    return;
                m.put(Net.Param.ID, String.valueOf(swipeUpItem.getId()));
                m.put(Net.Param.AFTER, after);
                m.put(Net.Param.LIMIT, String.valueOf(swipeUpItem.getLimit()));
                if (NetUtils.isNetworkAvaliable(getActivity())) {
                    mTopicPresenter.loadMoreAllTopic(getActivity(), m);
                } else {
                    ToastUtils.toast(getActivity(), "没有网络");
                    listView.restListView();
                }

            }
        }, 500);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.fragment_net_error_subTree:
                reQuestData();
                break;
            default:
                break;
        }

    }

    /**
     * 重新请求数据
     */
    private void reQuestData() {
        dismissNetErroView();
        dismissContentEmptyView();
        if (NetUtils.isNetworkAvaliable(getActivity())) {
            showLoadingView();
            mTopicPresenter.loadTopicList(getActivity());
        } else {
            showNetErroView(R.string.tips_net_error);
        }
    }

    public interface HandlerMsg {
        //获取话题列表成功
        int MSG_LOAD_TOPIC_LIST_SUC = 2002;
        //获取加载更多配置选项
        int MSG_LOAD_SWIPE_UP_ITEM = 2003;
        //加载更多话题
        int MSG_LOAD_MORE_TOPICS = 2004;
        //隐藏loading
        int MSG_DISMISS_LOADING = 2005;
        //显示空
        int MSG_SHOW_EMPTY_CONTENT = 2006;
        //二次请求刷新界面
        int MSG_NOTIFY_ADAPTER_CONTENT = 2007;
        //显示失败
        int MSG_SHOW_FAIL = 2008;
    }

    @Override
    public void onDestroyView() {
//        unbindDrawables(getView());
        LogHelper.e(TAG, "whb--> onDestroyView: ");
        super.onDestroyView();
    }
    @Override
    public void onResume() {
        super.onResume();
        LogHelper.d(TAG, "SuS--> onResume: ");
            BaofengStatistics.onUmengEvent(getActivity(), BfCountConst.TopicConst.BBS_CHANNELLIST_SHOW);
            LogHelper.v("umeng", "bbs_channelList_show  计数一次");
        topicList.removeAll(followTopicList);
        followTopicList = mTopicPresenter.loadFollowTopic(getActivity());
        topicList.addAll(0,followTopicList);
        topicAdapter.notifyDataSetChanged();
        //initData();
    }

}

MVP与MVC的异同

MVC模式与MVP模式都作为用来分离UI层与业务层的一种开发模式被应用了很多年。在我们选择一种开发模式时,首先需要了解一下这种模式的利弊:

无论MVC或是MVP模式都不可避免地存如下弊端,这就导致了这两种开发模式也许并不是很小型应用。

  • 额外的代码复杂度和学习成本

但比起他们的优点,这点弊端基本可以忽略了:

  • 降低耦合度
  • 模块职责划分明显
  • 利于测试驱动开发
  • 代码复用
  • 隐藏数据
  • 代码灵活性

对于MVP与MVC这两种模式,它们之间也有很大的差异。以下是这两种模式之间最关键的差异:

MVP模式:

  • View不直接与Model交互,而是通过与Presenter交互来与Model间接交互
  • Presenter与View的交互是通过接口来进行的,更有利于添加单元测试
  • 通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑

MVC模式:

  • View可以与Model直接交互
  • Controller是基于行为的,并且可以被多个View共享
  • 可以负责决定显示哪个View

MVVM

Data-Binding

前言

  • 第三方的数据绑定框架随时有停止更新的风险,官方的则相对更稳定一些
  • 大量的findViewById,增加代码的耦合性
  • 虽然可以通过注解框架抛弃大量的findViewById,但是注解注定要拖慢我们代码的速度,Data Binding则不会,官网文档说还会提高解析XML的速度

这里不赘述了,下面几篇文章都讲的很详细!

精通 Android Data Binding

Android官方数据绑定框架DataBinding(一)

Android官方数据绑定框架DataBinding(二)

官方Data Binding Library

参考链接:

1、https://www.zhihu.com/question/21406685

2、http://liuling123.com/2015/12/mvp-pattern-android.html

3、http://www.2cto.com/kf/201506/405766.html

4、http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0313/2599.html

5、http://blog.csdn.net/qibin0506/article/details/47393725

6、http://zjutkz.net/2016/04/13/%E9%80%89%E6%8B%A9%E6%81%90%E6%83%A7%E7%97%87%E7%9A%84%E7%A6%8F%E9%9F%B3%EF%BC%81%E6%95%99%E4%BD%A0%E8%AE%A4%E6%B8%85MVC%EF%BC%8CMVP%E5%92%8CMVVM/#plg_nld=1&plg_auth=1&plg_nld=1&plg_dev=1&plg_uin=1&plg_usr=1&plg_vkey=1&plg_nld=1&more?hmsr=toutiao.io&utm_source=toutiao.io&plg_uin=1&plg_auth=1&utm_medium=toutiao.io&plg_dev=1&plg_nld=1&plg_usr=1&plg_vkey=1

7、http://blog.csdn.net/wusuopubupt/article/details/8817826

8、[https://github.com/LyndonChin/MasteringAndroidDataBinding](https://github.com/LyndonChin/MasteringAndroidDataBinding)

9、https://github.com/googlesamples/android-architecture

10、http://www.jianshu.com/p/569ab68da482

11、http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0425/4178.html

12、http://blog.csdn.net/vector_yi/article/details/24719873

时间: 2024-10-19 18:09:05

Android-architecture之MVC、MVP、MVVM、Data-Binding的相关文章

Android 中的MVC MVP MVVM

app架构是一种开发方案 首先就是关于Model层的理解: 我的理解:Model层是属性和获取这些属性方法的集合. 比如在Android里,数据库里的数据是Model层,ORM框架也是Model层,假如要进行搜索操作,那么搜索的条件不属于Model层,以及搜索后所对数据进行的预处理也不是Model层. 其实这两种不属于 在Android中: 默认的开发模式是MVC的 其中Activity是Controller,xml文件和自定义的控件的是View.而那些网络请求操作的Network类是Model

Android App的设计架构:MVC,MVP,MVVM与架构经验谈

来源: Android App的设计架构:MVC,MVP,MVVM与架构经验谈 和MVC框架模式一样,Model模型处理数据代码不变在Android的App开发中,很多人经常会头疼于App的架构如何设计: 我的App需要应用这些设计架构吗? MVC,MVP等架构讲的是什么?区别是什么? 本文就来带你分析一下这几个架构的特性,优缺点,以及App架构设计中应该注意的问题. 1.架构设计的目的 通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.这样做的好处是使得程序在开发的过程中,开发人员

转: GUI应用程序架构的十年变迁:MVC,MVP,MVVM,Unidirectional,Clean

十年前,Martin Fowler撰写了 GUI Architectures 一文,至今被奉为经典.本文所谈的所谓架构二字,核心即是对于对于富客户端的 代码组织/职责划分 .纵览这十年内的架构模式变迁,大概可以分为MV*与Unidirectional两大类,而Clean Architecture则是以严格的层次划分独辟蹊径.从笔者的认知来看,从MVC到MVP的变迁完成了对于View与Model的解耦合,改进了职责分配与可测试性.而从MVP到MVVM,添加了View与ViewModel之间的数据绑

[转]MVVM架构~mvc,mvp,mvvm大话开篇

MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示.作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller. 在MVC里,View是可以直接访问

MVC, MVP, MVVM比较以及区别(上)

原文:MVC, MVP, MVVM比较以及区别(上) MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式.以前只是对它们有部分的了解,没有深入的研究过,对于一些里面的概念和区别也是一知半解.现在一边查资料,并结合自己的理解,来谈一下对于这三种模式思想的理解,以及它们的区别.欢迎各位高手拍砖. 阅读目录: 一. MVC, MVP, MVVM诞生的需求? 二. 一段典型的耦合代码 三. MVC模式 3.1 主动MVC 3.2 被动MVC 3.3 Web应用中的MVC框架 3.4

浅析前端开发中的 MVC/MVP/MVVM 模式

MVC,MVP和MVVM都是常见的软件架构设计模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式.不同于设计模式(Design Pattern),只是为了解决一类问题而总结出的抽象方法,一种架构模式往往使用了多种设计模式. 要了解MVC.MVP和MVVM,就要知道它们的相同点和不同点.不同部分是C(Controller).P(Presenter).VM(View-Model),而相同的部分则是MV(Model-View). Model&View 这里有一个可

MVC, MVP, MVVM比较以及区别

MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式.以前只是对它们有部分的了解,没有深入的研究过,对于一些里面的概念和区别也是一知半解.现在一边查资料,并结合自己的理解,来谈一下对于这三种模式思想的理解,以及它们的区别.欢迎各位高手拍砖. 阅读目录: 一. MVC, MVP, MVVM诞生的需求? 二. 一段典型的耦合代码 三. MVC模式 3.1 主动MVC 3.2 被动MVC 3.3 Web应用中的MVC框架 3.4 MVC总结 一,MVC, MVP, MVVM诞生的需

Android MVC,MVP,MVVM模式入门——重构登陆注册功能

一  MVC模式: M:model,业务逻辑 V:view,对应布局文件 C:Controllor,对应Activity 项目框架: 代码部分: layout文件(适用于MVC和MVP两个Demo): <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" and

热门前沿知识相关面试问题-MVC/MVP/MVVM架构设计模式面试问题详解

MVC: MVC的定义:M:业务逻辑处理.[业务MODEL]V:处理数据显示的部分.[如xml布局文件]C:Activity处理用户交互的问题.[也就是Activity在MVC中扮演着C的角色] MVC的特点:①.耦合性低.②.可扩展性好.③.模块职责划分明确. MVC的实例详解: 总结:①.利用MVC设计模式,使得项目有了很好的可扩展和维护性.②.controller(控制器)是一个中间桥梁的作用.③.什么时候适合使用MVC模式呢?当一个项目很小, MVP: MVVM: 原文地址:https:

浅谈iOS中的MVC MVP MVVM

MVP MVVM都源自MVC. 传统的MVC,业务逻辑放在Model层,UI在View层,Controller只是Model和View的粘合剂. 实际情况,Model层只存放从json解析的数据模型,Controller层(ViewController)有很多UI逻辑,导致Controller里面既包含业务逻辑(请求数据.处理数据)又包含控制UI逻辑,因此Controller巨大无比. 所以衍生出MVP和MVVM. MVP:把Controller中的业务逻辑抽出来放到Presenter层,Vie