- 传送门
- 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模式的整个核心过程。
官方模式图
案例
这里以暴风体育中的话题列表为例来进行介绍:
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的速度
这里不赘述了,下面几篇文章都讲的很详细!
参考链接:
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
7、http://blog.csdn.net/wusuopubupt/article/details/8817826
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