前言有理。这次是真的。搞得我身心俱疲,所以在这里跟关注我的人说一声对不起,我没有给大家带来常规的福利,所以在这里给大家送上一个大大的福利:爱吖妹纸——Retrofit&RxJava&MVP&Butterknife说起最近让我身心疲惫的问题的答案,无疑让我在开源的道路上越走越远。虽然我不是技术达人,但还是被一些很简单的问题轰炸了。其实作者内心是真的拒绝的。不得不说,写信给你和技术群的群主,为什么你问问题,却没人回答你!文笔很好。概览废话不多说。对于MVP(ModelViewPresenter),相信大部分人都能说点什么,“MVC的进化”,“彻底解耦Model和View”等等,但是用过MVP的人肯定会觉得在Android中,代码说的很清楚,但是类多了很多。对大部分人来说,看MVP的demo的时候,一看慢慢就好了,但是让自己写个例子,却很头疼,写不出来。但确实MVC模式更像是一帆风顺。你只需要将自己的业务逻辑放入Activity中即可顺利完成工作。不得不说,我们之前的项目确实是用MVC来写的。很容易发现,任何一个Activity的代码都有成百上千行,甚至上万行。好像是一样的东西,但是仔细想想,其实这个View对于布局文件能做的事情很少。实际上,布局文件中数据绑定和事件处理的操作都在Activity中,导致把Activity当成View和Controller,鄙视代码难看,后面的代码真的很难看。不相信我?看这里。也许业务逻辑比较简单的功能用MVC还可以,但是你有没有想过,如果你的产品后面需求变了怎么办?是的,你接受强奸产品的要求,但你还是要忍辱负重。在越来越复杂的业务逻辑上,你的Activity和Fragment代码越来越多,最终导致代码爆炸,难以维护。在网上浏览了一下,发现关于MVP的文章很多,可见MVP的火爆程度,但是大部分文章都是理论性的,稍微好一点的会附带一个简单的登录Demo。但是一个简单的demo对于刚接触MVP模式的人来说很难掌握它的使用。于是爱吖妹纸应运而生。什么是MVP?当然,我们不能跑题。前面我们对MVP做了一个简单的概述。让我们用一个简单的图表来展示它。如上图所示,View和Model在项目中并不直接交互,而是使用Presenter作为View和Model之间的桥梁。其中Presenter同时持有View层和Model层Interface的引用,View层持有Presenter层Interface的引用。当View层的一个页面需要展示一些数据时,会先调用Presenter层的一个接口,然后Presenter层会调用Model层请求数据。当Model层数据加载成功后,会调用Presenter层的回调方法,通知Presenter层数据加载完成。***Presenter层会调用View层的接口将加载的数据显示给用户。这就是MVP模式的核心流程。这种分层的好处是大大降低了Model和View层之间的耦合度。一方面,View层和Model层可以独立开发和测试,互不依赖。另一方面,Model层可以封装和复用,可以大大减少代码量。当然,MVP还有一些其他的优势,这里就不赘述了。功能展示这里给大家介绍一下干货板块的功能。布局相当简单。干货模块,即一个Fragment,里面有一个RecyclerView,支持下拉刷新和拉取-上传数据。所以我们的Presenter和View只需要定义一些简单的方法即可。1)加载数据时显示加载进度条;2)如果数据加载成功,提醒Adapter刷新数据;3)如果加载失败,在窗口中提示用户相关信息;4)加载完成后隐藏进度条;publicinterfaceCategoryContract{interfaceICategoryViewextendsBaseView{voidgetCategoryItemsFail(StringfailMessage);voidsetCategoryItems(CategoryResultcategoryResult);voidaddCategoryItems(CategoryResultcategoryResult);voidshowSwipeLoading();voidhideSwipeLoading();voidsetLoading();StringgetCategoryName();voidnoMore();}interfaceICategoryPresenterextendsBasePresenter{voidgetCategoryItems(booleanisRefresh);}}编写Presenter现实类。publicclassCategoryPresenterimplementsICategoryPresenter{privateICategoryViewmCategoryICategoryView;privateintmPage=1;privateSubscriptionmSubscription;publicCategoryPresenter(ICategoryViewandroidICategoryView){mCategoryICategoryView=androidICategoryView;}@Overridepublicvoidsubscribe(){getCategoryItems(true);}@OverridepublicvoidunSubscribe(){if(mSubscription!=null&&!mSubscription.isUnsubscribed()){mSubscription.unsubscribe();}}@OverridepublicvoidgetCategoryItems(finalbooleanisRefresh){if(isRefresh){mPage=1;mCategoryICategoryView.showSwipeLoading();}else{mPage++;}mSubscription=NetWork.getGankApi().getCategoryData(mCategoryICategoryView.getCategoryName(),GlobalConfig.CATEGORY_COUNT,mPage).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(newObserver(){@OverridepublicvoidonCompleted(){}@OverridepublicvoidonError(Throwablee){mCategoryICategoryView.hideSwipeLoading复制代码();mCategoryICategoryView.getCategoryItemsFail(mCategoryICategoryView.getCategoryName()+"列表数据获取失败!");}@OverridepublicvoidonNext(CategoryResultcategoryResult){if(isRefresh){mCategoryICategoryView.setCategoryItems(categoryResult);mCategoryICategoryView.hideSwipeingLoadingCategory();{mCategoryICategoryView.addCategoryItems(categoryResult);}}});}}写Adapter显示数据classCategoryRecyclerAdapterextendsCommonRecyclerAdapterimplementsListenerWithPosition.OnClickWithPositionListener{CategoryRecyclerAdapter(Contextcontext){super(context,null,R.layout.item_category);}@Overridepublicvoidconvert(CommonRecyclerHolderholder,ResultsBeanresultsBean){if(resultsBean!=null){ImageViewimageView=holder.getView(R.id.category_item_img);if(ConfigManage.INSTANCE.isListShowImg()){//列表显示图片imageView.setVisibility(View.VISIBLE);Stringquality="";if(resultsBean.images!=null&&resultsBean.images.size()>0){switch(ConfigManage.INSTANCE.getThumbnailQuality()){case0://原图quality="";break;case1://quality="?imageView2/0/w/400";break;case2:quality="?imageView2/0/w/190";break;}Glide.with(mContext).load(resultsBean.images.get(0)+quality).placeholder(R.mipmap.image_default).error(R.mipmap.image_default).into(imageView);}else{//列表不显示图片Glide.with(mContext).load(R.mipmap.image_default).into(imageView);}}else{imageView.setVisibility(View.GONE);}holder.setTextViewText(R.id.category_item_desc,resultsBean.desc==空?“未知”:resultsBean.desc);holder.setTextViewText(R.id.category_item_author,resultsBean.who==null?“未知”:resultsBean.who);holder.setTextViewText(R.id.category_item_time,TimeUtil.dateFormat(resultsBean.publishedAt));holder.setTextViewText(R.id.category_item_src,resultsBean.source==null?"unknown":resultsBean.source);holder.setOnClickListener(this,R.id.category_item_layout);}}@OverridepublicvoidonClick(Viewv,intposition,CommonRecyclerHolderholder){//Toasty.info(mContext,"跳转到相应网页!",Toast.LENGTH_SHORT,true).show();Intentintent=newIntent(mContext,WebViewActivity.class);intent.putExtra(WebViewActivity.GANK_TITLE,mData.get(position).desc);intent.putExtra(WebViewActivity.GANK_URL,mData.get(position).url);mContext.startActivity(intent);}}***当然是片段publicclassCategoryFragmentextendsBaseFragmentimplementsICategoryView,OnRefreshListener,OnLoadMoreListener{publicstaticfinalStringCATEGORY_NAME="com.nanchen.aiyagirl.module.category.CategoryFragment.CATEGORY_NAME";@BindView(R.id.recyclerView)RecyclerViewWithFootermRecyclerView;@BindView(R.id.swipe_refresh_layout)SwipeRefreshLayoutmSwipeRefreshLayout;privateStringcategoryName;privateCategoryRecyclerAdaptermAdapter;privateICategoryPresentermICategoryPresenter;publicstaticCategoryFragmentnewInstance(StringmCategoryName){CategoryFragmentcategoryFragment=newCategoryFragment();Bundlebundle=newBundle();bundle.putString(CATEGORY_NAME,mCategoryName);categoryFragment.setArguments(bundle);returncategoryFragment;}@OverrideprotectedintgetContentViewLayoutID(){returnR.layout.fragment_category;}@Overrideprotectedvoidinit(){mICategoryPresenter=newCategoryPresenter(this);categoryName=getArguments().getString(CATEGORY_NAME);mSwipeRefreshLayout.setOnRefreshListener(this);mAdapter=newCategoryRecyclerAdapter(getActivity());mRecyclerView.setLayoutManager(newLinearLayoutManager(getActivity()));mRecyclerView.addItemDecoration(newRecyclerViewDivider(getActivity(),LinearLayoutManager.HORIZONTAL));mRecyclerView.setAdapter(mAdapter);mRecyclerView...{mICategoryPresenter.getCategoryItems(true);}@OverridepublicvoidonLoadMore(){mICategoryPresenter.getCategoryItems(false);}@OverridepublicvoidgetCategoryItemsFail(StringfailMessage){if(getUserVisibleHint()){Toasty.error(this.getContext(),failMessage).show();}}@OverridepublicvoidsetCategoryItems(CategoryResultcategoryResult){mAdapter.setData(categoryResult.results);}@OverridepublicvoidaddCategoryItems(CategoryResultcategoryResult){mAdapter.addData(categoryResult.results);}@OverridepublicvoidshowSwipeLoading(){mSwipeRefreshLayout.setRefreshing(true);}@OverridepublicvoidhideSwipeLoading(){mSwipeRefreshLayout.setRefreshing(false);}@OverridepublicvoidsetLoading(){mRecyclerView.setLoading();}@OverridepublicStringgetCategoryName(){returnthis.categoryName;}@OverridepublicvoidnoMore(){mRecyclerView.setEnd("Nomoredata");}}项目截图给大家看下项目截图,免得慌张,一款集成了RxJava等主流框架的干货APP,项目资源来自码农干货集中营。代码量不多,但基本涉及到方方面面。界面采用了设计风格,所以也是学习设计的一剂良药。作者也希望能在开源的道路上越走越远,多多支持。