1. 程式人生 > >Android-architecture之MVC、MVP、MVVM、Data-Binding

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

傳送門

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

結構簡介

Drawing
這裡寫圖片描述

為什麼使用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模式的整個核心過程。

官方模式圖

案例

這裡以暴風體育中的話題列表為例來進行介紹:

這裡寫圖片描述
這裡寫圖片描述
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的速度

這裡不贅述了,下面幾篇文章都講的很詳細!

參考連結: