MVP+Retrofit+Rxjava实战

xiaoxiao2021-02-28  47

MVP+Retrofit+Rxjava实战

2017年07月07日 16:10:10 阅读数:4389

文章背景

在刚接触Android开始起,很长一段时间都在使用MVC模式开发项目,MVC模式很适合小项目的开发,简单方便。但当我们的项目越来越大时,MVC就显得力不从心,Activty或者Fragment中代码也会越来越多,导致项目的维护变的越来越复杂,业务剥离和复用难度大,一个新的框架或技术的出现必定是来解决前有的框架的缺陷,所以MVP的出现很优雅的解决了MVC中存在缺陷或不足。

文章目标

MVP在Android中的原理解析 MVP+Retrofit+Rxjava在项目中实战解析 架构经验分享

MVP简单介绍

MVC分层: View:对应于布局文件 Model:业务逻辑和实体模型 Controllor:对应于Activity 看起来的确像那么回事,但是细细的想想这个View对应于布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller(当然了Data-Binder的出现,可能会让View更像View吧)。这可能也就是为何,在该文中有一句这样的话:

Most of the modern android applications just use View-Model architecture,everything is connected with Activity.

而当将架构改为MVP以后,Presenter的出现,将Actvity视为View层,Presenter负责完成View层与Model层的交互。现在是这样的: View 对应于Activity,负责View的绘制以及与用户交互 Model 依然是业务逻辑和实体模型 Presenter 负责完成View于Model间的交互

MVP模式的核心思想

MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。

 模型(Model):负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;

 视图(View):负责界面数据的展示,与用户进行交互;

 主持人(Presenter):相当于协调者,是模型与视图之间的桥梁,将模型与视图分离开来。

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

这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面Model层可以封装复用,可以极大的减少代码量。当然,MVP还有其他的一些优点,这里不再赘述

MVP在真实项目中的实战

上面已经介绍过MVP的核心思想以及基本架构,当然我们在实际项目中不仅仅要把建构划分出来,还要加以延伸,这样才能够使项目的整体架构具备可扩展行、可复用性、可维护性、灵活性。下面我用我在实际项目中的角度来解析我所理解的MVP。

总体架构图:

项目目录结构:

1、View层

a、Iview接口代码如下:

/** * @Description MVP之V层 是所有VIEW的基类,其他类可以继承该类 * @Author ydc * @CreateDate 2016/10/10 * @Version 1.0 */ public interface Iview<T> { /** * @description 全局的显示加载框 * @author ydc * @createDate * @version 1.0 */ void showLoading(); /** * @description 全局的显示加载框 * @author ydc * @createDate * @version 1.0 */ void showLoading(String msg); /** * @description 全局的显示加载框 * @author ydc * @createDate * @version 1.0 */ void showLoading(String msg, int progress); /** * @description 全局的隐藏加载框 * @author ydc * @createDate * @version 1.0 */ void hideLoading(); /** * @description 全局消息展示 * @author ydc * @createDate * @version 1.0 */ void showMsg(String msg); /** * @description 全局错误消息展示 * @author ydc * @createDate * @version 1.0 */ void showErrorMsg(String msg, String content); /** * @description 关闭界面信息 * @author ydc * @createDate * @version 1.0 */ void close(); /** * @description 当前fragment是否有效 * @author ydc * @createDate * @version 1.0 */ boolean isActive(); } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768

可以看出Iview 接口是所以activity 和fragment最基本且共有的方法定义。

b、NewsView接口代码如下:

/**ydc 新闻列表所特有的方法定义 * Created by Administrator on 2017/7/6. */ public interface NewsView extends Iview { void addNews(List<NewsBean> newsList); void showLoadFailMsg(String msg); } 123456789

NewsView接口继承自Iview接口,定义新闻列表特有的方法。

c、BaseActivity基类代码如下:

package com.example.ydcretrofitmvp.Base; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.Window; /** * Created by Administrator on 2017/7/8. */ public class BaseActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); } @Override protected void onStart() { super.onStart(); } @Override protected void onResume() { super.onResume(); } @Override protected void onRestart() { super.onRestart(); } @Override protected void onPause() { super.onPause(); } @Override protected void onStop() { super.onStop(); } @Override protected void onDestroy() { super.onDestroy(); } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

BaseActivity作为所以activity的基类,你可以把所以activity共有的方法和属性提取到该中。

d、activity_main.xml布局文件代码如下:

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.ydcretrofitmvp.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recycle_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbars="vertical" tools:layout_editor_absoluteY="0dp" tools:layout_editor_absoluteX="8dp"> </android.support.v7.widget.RecyclerView> </android.support.constraint.ConstraintLayout> 1234567891011121314151617181920

布局里面仅仅放了一个RecyclerView,用来展示数据列表。

e、NewListActivity代码如下:

public class NewListActivity extends BaseActivity implements NewsView { private RecyclerView mRecyclerView; private LinearLayoutManager mLayoutManager; private NewsAdapter mAdapter; private List<NewsBean> mData; private int pageIndex = 0; private NewsPresenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPresenter = new NewsPresenter(); mPresenter.attachView(this); mPresenter.subscribe(); mRecyclerView = (RecyclerView)findViewById(R.id.recycle_view); mRecyclerView.setHasFixedSize(true); mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mAdapter = new NewsAdapter(getApplicationContext()); mRecyclerView.setAdapter(mAdapter); } @Override protected void onResume() { super.onResume(); mPresenter.loadNews(0,0); } @Override protected void onDestroy() { super.onDestroy(); if (mPresenter != null) { mPresenter.detachView(); mPresenter.unsubscribe(); } } @Override public void showLoading() { } @Override public void showLoading(String msg) { } @Override public void showLoading(String msg, int progress) { } @Override public void hideLoading() { } @Override public void showMsg(String msg) { } @Override public void showErrorMsg(String msg, String content) { } @Override public void close() { } @Override public boolean isActive() { return false; } @Override public void addNews(List<NewsBean> newsList) { mAdapter.isShowFooter(true); if(mData == null) { mData = new ArrayList<NewsBean>(); } mData.addAll(newsList); if(pageIndex == 0) { mAdapter.setmDate(mData); } else { //如果没有更多数据了,则隐藏footer布局 if(newsList == null || newsList.size() == 0) { mAdapter.isShowFooter(false); } mAdapter.notifyDataSetChanged(); } } @Override public void showLoadFailMsg(String msg) { } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113

在NewListActivity 中我们可以看到,NewListActivity 显示实现了NewsView 接口,实现了NewsView和Iview 未实现的方法,在代码中可以看出NewListActivity并没有做一些逻辑处理工作,仅仅做了添加数据和展示数据以及一些提示消息等工作,数据处理的工作都是调用 NewsPresenter 完成的。

2、Presenter层

a、Ipresenter 代码如下:

/** * @Description MVP的P层 * @Author ydc * @CreateDate 2016/10/10 * @Version 1.0 */ public interface Ipresenter<T extends Iview> { /** * @description 关联P与V(绑定,VIEW销毁适合解绑) * @author ydc * @createDate * @version 1.0 */ void attachView(T view); /** * @description 取消关联P与V(防止内存泄漏) * @author ydc * @createDate * @version 1.0 */ void detachView(); /** * @description RX订阅 * @author ydc * @createDate * @version 1.0 */ void subscribe(); /** * @description RX取消订阅 * @author ydc * @createDate * @version 1.0 */ void unsubscribe(); } 123456789101112131415161718192021222324252627282930313233343536373839404142

Ipresenter定义了所有presenter最基本且共有的方法。

b、BasePresenter代码如下:

/** * @Description 抽象的公用Presenter * @Author ydc * @CreateDate 20170707 * @Version 1.0 */ public abstract class BasePresenter<T extends Iview> implements Ipresenter<T> { protected T mMvpView;//所有View protected SubscriptionList mSubscriptions;//rx注册中心 protected DataRepository mDataCenter;//数据中心 //protected abstract SubscriptionList createSubscriptionList();//引入darger后取缔 /** * @description 获取V * @author ydc * @createDate * @version 1.0 */ public T getMvpView() { return mMvpView; } /** * @description view绑定P的时候初始化 * @author ydc * @createDate * @version 1.0 */ @Override public void attachView(T view) { this.mMvpView = view; this.mSubscriptions = new SubscriptionList(); this.mDataCenter = DataRepository.getInstance(); } /** * @description view失去绑定清除 * @author ydc * @createDate * @version 1.0 */ @Override public void detachView() { unsubscribe(); this.mMvpView = null; this.mSubscriptions = null; this.mDataCenter = null; } @Override public void unsubscribe(){ if(mSubscriptions!=null){ mSubscriptions.clear(); } } /** * @description 当前的view(fragemnt&activity是否存在) * @author ydc * @createDate * @version 1.0 */ public boolean isViewAttached() { return mMvpView != null; } /** * @description 是否viewb绑定过P * @author ydc * @createDate * @version 1.0 */ public void checkViewAttached() { if (!isViewAttached()) throw new MvpViewNotAttachedException(); } /** * @description p&v没有绑定的异常 * @author ydc * @createDate * @version 1.0 */ public static class MvpViewNotAttachedException extends RuntimeException { public MvpViewNotAttachedException() { super("Please call Presenter.attachView(MvpView) before requesting data to the Presenter"); } } /** * @description 统一添加订阅关联被观察者和观察者 * @author ydc * @createDate * @version 1.0 */ public void addSubscription(Observable observable, Subscriber subscriber) { if( observable!=null && subscriber!=null ){ if (mSubscriptions == null) { mSubscriptions = new SubscriptionList(); } mSubscriptions.clear(); mSubscriptions.add(observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber)); } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109

BasePresenter是一个abstract类,在实现Ipresenter的未实现的方法之外,又扩展了几个所以presenter共用的方法。

c、Presenter代码如下:

/**ydc 新闻类的协议也可以是接口 * Created by Administrator on 2017/7/6. */ abstract class Presenter extends BasePresenter<NewsView> { public abstract void loadNews(int type, int page); } 1234567

可以看出Presenter也是一个abstract类、继承自BasePresenter抽象类,同时定义了新闻列表所特有的方法。

d、NewsPresenter代码如下:

/** * Created by Administrator on 2017/7/6. */ public class NewsPresenter extends Presenter { private Model mModel; public NewsPresenter(){ mModel=new NewsModel(); } @Override public void loadNews(int type, int page) { addSubscription(mModel.loadNews("nc/article/headline/T1348647909107/0-20.html",0), new ApiCallBack<NewsRequestModel>() { @Override public void onStart() { getMvpView().showLoading(); } @Override public void onSuccess(NewsRequestModel modelBean) { if(modelBean!=null){ getMvpView().addNews(modelBean.getT1348647909107()); } } @Override public void onFailure(String errorMsg) { getMvpView().showLoadFailMsg(errorMsg); } @Override public void onFinished() { getMvpView().hideLoading(); } }); } @Override public void subscribe() { } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445

可以看出NewsPresenter持有view和model的接口或是抽象类,起到中转的作用。

3、Model层

a、Imodel接口代码如下:

** * @Description MVP的M层 * @Author ydc * @CreateDate 2016/10/10 * @Version 1.0 */ public interface Imodel { } 12345678

我这里其实并没有做什么,只是留了一个接口而已,你可以定义所以model的基本方法。

b、BaseModel代码如下:

/** * @Description 数据模型基础类 * @Author ydc * @CreateDate 2016/11/2 * @Version 1.0 */ public abstract class BaseModel implements Imodel { /** * @description 返回服务接口对象实例 * @author ydc * @createDate * @version 1.0 */ public <T> T createService(final Class<T> clazz) { validateServiceInterface(clazz); return (T) RxService.RETROFIT.createRetrofit().create(clazz); } /** * @description 校验接口合法性 * @author ydc * @createDate * @version 1.0 */ public <T> void validateServiceInterface(Class<T> service) { if (service == null) { //AppToast.ShowToast("服务接口不能为空!"); } if (!service.isInterface()) { throw new IllegalArgumentException("API declarations must be interfaces."); } if (service.getInterfaces().length > 0) { throw new IllegalArgumentException("API interfaces must not extend other interfaces."); } } } 1234567891011121314151617181920212223242526272829303132333435363738

这个类我是用它来做Retrofit的初始化以及网络请求的工作,当然我要进一步包装Retrofit,所以这里只看到一个方法调用。

c、Model代码如下:

/**ydc 获取数据的逻辑模块协议,也可以是接口,提供给P调用,在callback中更新V * Created by Administrator on 2017/7/6. */ public abstract class Model extends BaseModel { public abstract Observable<NewsRequestModel> loadNews(String url, int type); } 123456789

既然公共BaseModel的职责任命为整个网络调用的工作,那么我就要在抽象一个Model抽象类来定义新闻数据处理逻辑模块协议,提供给P调用。

d、NewsModel代码如下:

/**ydc 新闻数据处理协议 * Created by Administrator on 2017/7/6. */ public class NewsModel extends Model { private INewService service=createService(INewService.class); @Override public Observable<NewsRequestModel> loadNews(String url, int type) { Map<String, String> map = new HashMap<>(); //map.put("type", type+""); return service.getNewList(url,map); } } 123456789101112131415

这个类实现了Model作为具体的新闻列表数据处理层。

e、网络接口

/**网络接口 * Created by Administrator on 2017/7/6. */ public interface INewService { @GET Observable<NewsRequestModel> getNewList(@Url String url, @QueryMap Map<String, String> params); } 1234567891011

MVP总结:

当用户进入到NewListActivity界面之后,界面需要展示新闻列表信息给用户。首先NewListActivity会调用NewsPresenter的loadNews方法,NewsPresenter 的loadNews方法中又会调用NewsModel中的loadNews方法。NewsModel中的loadNews方法中就是加载数据的核心,通过Retrofit请求服务器接口获取数据,无论数据获取成功与否,都会通过ApiCallBack回调给NewsPresenter 。如果获取成功,NewsPresenter 会调用NewsView的addNews方法将获取的新闻列表信息展示到RecyclerView。如果获取失败,则调用NewsView的showLoadFialMsg方法向用户提示失败信息。

RxJava 与 Retrofit 的结合简单介绍

Retrofit 是 Square 的一个著名的网络请求库,是okHTTP的升级版,目前公认的最好的网络请求框架。

响应式编程RxJava就更不用说,它的强大之处只有用过的人才会体会得到。

Retrofit 除了提供了传统的 Callback 形式的 API,还有 RxJava 版本的 Observable 形式 API。下面我用对比的方式来介绍 Retrofit 的 RxJava 版 API 和传统版本的区别。

以获取一个 User 对象的接口作为例子。使用Retrofit 的传统 API,你可以用这样的方式来定义请求:

@GET("/user") public void getUser(@Query("userId") String userId, Callback<User> callback); 12

在程序的构建过程中, Retrofit 会把自动把方法实现并生成代码,然后开发者就可以利用下面的方法来获取特定用户并处理响应:

getUser(userId, new Callback<User>() { @Override public void success(User user) { userView.setUser(user); } @Override public void failure(RetrofitError error) { // Error handling ... } }; 123456789101112

其实 Retrofit传统的API调用与okHTTP功能和使用上没有什么本质的区别,它的强大之处在于与RxJava结合使用。

而使用 RxJava 形式的 API,定义同样的请求是这样的:

@GET("/user") public Observable<User> getUser(@Query("userId") String userId); 12

使用的时候是这样的:

getUser(userId) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onNext(User user) { userView.setUser(user); } @Override public void onCompleted() { } @Override public void onError(Throwable error) { // Error handling ... } }); 123456789101112131415161718

看到区别了吗?

当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。

对比来看, Callback 形式和 Observable 形式长得不太一样,但本质都差不多,而且在细节上 Observable 形式似乎还比 Callback 形式要差点。那 Retrofit 为什么还要提供 RxJava 的支持呢?

单个请求体现不出它的优势所在,但是情景复杂起来, Callback 形式马上就会开始让人头疼。

假设 /user 接口并不能直接访问,而需要填入一个在线获取的 token ,代码应该怎么写?

Callback 方式,可以使用嵌套的 Callback:

GET("/token") public void getToken(Callback<String> callback); @GET("/user") public void getUser(@Query("token") String token, @Query("userId") String userId, Callback<User> callback); ... getToken(new Callback<String>() { @Override public void success(String token) { getUser(token, userId, new Callback<User>() { @Override public void success(User user) { userView.setUser(user); } @Override public void failure(RetrofitError error) { // Error handling ... } }; } @Override public void failure(RetrofitError error) { // Error handling ... } }); 12345678910111213141516171819202122232425262728293031

倒是没有什么性能问题,可是迷之缩进而且充满了无穷无尽的回调,这种后果你懂我也懂,做过大项目的人应该更懂。

而使用 RxJava 的话,代码是这样的:

@GET("/token") public Observable<String> getToken(); @GET("/user") public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId); ... getToken() .flatMap(new Func1<String, Observable<User>>() { @Override public Observable<User> onNext(String token) { return getUser(token, userId); }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onNext(User user) { userView.setUser(user); } @Override public void onCompleted() { } @Override public void onError(Throwable error) { // Error handling ... } }); 12345678910111213141516171819202122232425262728293031

用一个 flatMap() 就搞定了逻辑,整个请求都在一条链当中。读者看到这里应该明白我为什么选择RxJava 与 Retrofit 的结合来处理网络请求。其实RxJava有两个比较核心的功能就是数据转换和线程调度,当然它还有其它的强大之处,只是我们用的最多的是这两个而已。

RxJava 与 Retrofit 的结合在本项目中的应用

1、RxService类代码如下:

/** * @Description RX&Retrofit * @Author ydc * @CreateDate 2016/10/31 * @Version 1.0 */ public enum RxService { RETROFIT; private Retrofit mRetrofit; private static final int READ_TIMEOUT = 60;//读取超时时间,单位秒 private static final int CONN_TIMEOUT = 50;//连接超时时间,单位秒 /** * @description Head信息拦截 * @author ydc * @createDate * @version 1.0 */ private Interceptor mHeadInterceptor = new Interceptor() {//头信息 @Override public Response intercept(Chain chain) throws IOException { //这个chain里面包含了request和response,所以你要什么都可以从这里拿 Request request = chain.request(); long t1 = System.nanoTime();//请求发起的时间 String method = request.method(); if ("POST".equals(method)) { StringBuilder sb = new StringBuilder(); if (request.body() instanceof FormBody) { FormBody body = (FormBody) request.body(); for (int i = 0; i < body.size(); i++) { sb.append(body.encodedName(i) + "=" + body.encodedValue(i) + ","); } sb.delete(sb.length() - 1, sb.length()); Log.d("NET_RELATIVE",String.format("发送请求 %s on %s %n%s %nRequestParams:{%s}", request.url(), chain.connection(), request.headers(), sb.toString())); } } else { Log.d("NET_RELATIVE",String.format("发送请求 %s on %s%n%s", request.url(), chain.connection(), request.headers())); } Response response = chain.proceed(initHead(null,chain)); long t2 = System.nanoTime();//收到响应的时间 //这里不能直接使用response.body().string()的方式输出日志 //因为response.body().string()之后,response中的流会被关闭,程序会报错,我们需要创建出一 //个新的response给应用层处理 ResponseBody responseBody = response.peekBody(1024 * 1024); Log.d("NET_RELATIVE", String.format("接收响应: [%s] %n返回json:【%s】 %.1fms %n%s", response.request().url(), responseBody.string(), (t2 - t1) / 1e6d, response.headers() )); return response; } }; /** * @description 创建Retrofit对象 * @author ydc * @createDate * @version 1.0 */ public Retrofit createRetrofit() { if(mRetrofit == null){ OkHttpClient client = new OkHttpClient.Builder()//初始化一个client,不然retrofit会自己默认添加一个 .addInterceptor(mHeadInterceptor) .connectTimeout(CONN_TIMEOUT, TimeUnit.MINUTES)//设置连接时间为50s .readTimeout(READ_TIMEOUT, TimeUnit.MINUTES)//设置读取时间为一分钟 .build(); mRetrofit = new Retrofit.Builder() .client(client) .baseUrl(URLRoot.API_PATH) .addConverterFactory(GsonConverterFactory.create())//返回值为Gson的支持(以实体类返回) .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//返回值为Oservable<T>的支持 .build(); } return mRetrofit; } /** * @description 头信息初始化 * @author ydc * @createDate * @version 1.0 */ public Request initHead(File file, Interceptor.Chain chain) { Request.Builder mBuilder = chain.request().newBuilder(); Map<String, String> map = new HashMap<String, String>(); //HeaderUtil.setHeader(map);//ydc 移植注释 if (file != null) { //String mSize = MD5Util.MD5(file.length() + ""); String mSize ="10"; //SharedUtil.setFileSize(mSize);//andy.fang 文件大小MD5保存头信息,方便后台校验 map.put("filesize", mSize);//上传下载文件的MD5值 不需要每个地方都加,在需要的地方加 } Set keys = map.keySet(); if (keys != null) { Iterator iterator = keys.iterator(); while (iterator.hasNext()) { String key = (String) iterator.next(); String value = (String) map.get(key); /*if(StrUtil.isNotBlank(key)&& StrUtil.isNotBlank(value)){ mBuilder.addHeader(key, value); }*/ } } //系统级请求参数 //mBuilder.addHeader("token", SharedUtil.getPreferStr("TOKEN"));//ydc 移植注释 // mBuilder.addHeader("v", "1.0"); // mBuilder.addHeader("format", "JSON"); // mBuilder.addHeader("appKey", "00001"); Request mRequest = mBuilder.build(); return mRequest; } /** * @description 返回服务接口对象实例 * @author ydc * @createDate * @version 1.0 */ public <T> T createService(final Class<T> service) { validateServiceInterface(service); return (T) RxService.RETROFIT.createRetrofit().create(service); } /** * @description 校验接口合法性 * @author ydc * @createDate * @version 1.0 */ public <T> void validateServiceInterface(Class<T> service) { if (service == null) { //Toast.ShowToast("服务接口不能为空!"); } if (!service.isInterface()) { throw new IllegalArgumentException("API declarations must be interfaces."); } if (service.getInterfaces().length > 0) { throw new IllegalArgumentException("API interfaces must not extend other interfaces."); } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152

可以看出RxService类主要用来初始化Retrofit以及添加头部和系统参数,NewsModel初始化时,顺带完成了以上工作。

2、INewService接口代码如下:

/**网络接口 * Created by Administrator on 2017/7/6. */ public interface INewService { @GET Observable<NewsRequestModel> getNewList(@Url String url, @QueryMap Map<String, String> params); } 1234567891011

这个类是来定义新闻列表网络接口

3、管理被观察者和观察者

统一添加订阅关联被观察者和观察者

protected SubscriptionList mSubscriptions;//rx注册中心 /** * @description 统一添加订阅关联被观察者和观察者 * @author ydc * @createDate * @version 1.0 */ public void addSubscription(Observable observable, Subscriber subscriber) { if( observable!=null && subscriber!=null ){ if (mSubscriptions == null) { mSubscriptions = new SubscriptionList(); } mSubscriptions.clear(); mSubscriptions.add(observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber)); } } 123456789101112131415161718192021

RX取消订阅代码如下:

@Override public void unsubscribe(){ if(mSubscriptions!=null){ mSubscriptions.clear(); } } 123456

以上两段代码是在BasePresenter抽象类中。

4、把请求添加到rx注册中心SubscriptionLis中

5、ApiCallBack代码如下:

/** * @Description 处理网络数据处理完成后的回调响应(观察者) * @Author ydc * @CreateDate 2016/10/28 * @Version 1.0 */ public abstract class ApiCallBack<M> extends Subscriber<M> { /** * @description 成功接口回调,提供给View处理页面问题 * @author ydc * @createDate * @version 1.0 */ public abstract void onSuccess(M modelBean); /** * @description 失败接口回调,提供给View处理页面问题 * @author ydc * @createDate * @version 1.0 */ public abstract void onFailure(String errorMsg); /** * @description 请求结束,提供给View处理页面问题 * @author ydc * @createDate * @version 1.0 */ public abstract void onFinished(); @Override public void onNext(M modelBean) { if(modelBean!=null){ BaseFeed feed = (BaseFeed) modelBean; String status = feed.getStatus(); status="S";//由于是演示所以手动赋值,正在开发中和服务器协商好 if("S".equalsIgnoreCase(status)){ onSuccess(modelBean); }else { onFailure(feed.getMessage()); } } } @Override public void onStart() { super.onStart(); } @Override public void onCompleted() { onFinished(); } /** * @description 統一处理异常的回调 * @author Andy.fang * @createDate * @version 1.0 */ @Override public void onError(Throwable e) { e.printStackTrace(); if (e instanceof HttpException) { HttpException httpException = (HttpException) e; int exceptionCode = httpException.code(); String msg = httpException.getMessage(); if (exceptionCode == 401) { msg = "用户名密码错误,请重新登录!"; } if (exceptionCode == 403 || exceptionCode == 404 || exceptionCode == 407 || exceptionCode == 408) { msg = "网络链接超时,请稍后再试!"; } if (exceptionCode == 501 || exceptionCode == 502 || exceptionCode == 504) { msg = "服务器无响应,请稍后再试!"; } onFailure(msg); } else { onFailure(e.getMessage()); } onFinished(); } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485

ApiCallBack是一个抽象类,处理网络数据处理完成后的回调响应(即RxJava的观察者),把它和被观察者作为参数一起传入到addSubscription方法中:

6、NewsRequestModel代码如下

public class NewsRequestModel extends BaseFeed { public List<NewsBean> getT1348647909107() { return T1348647909107; } public void setT1348647909107(List<NewsBean> t1348647909107) { T1348647909107 = t1348647909107; } private List<NewsBean> T1348647909107; } 123456789101112

该类继承自BaseFeed,作为新闻列表接口返回实体映射,这个需要和后台api接口开发人员协商好再定义。

7、BaseFeed代码如下:

/** * @Description 返回接口的基类 * @Author ydc * @CreateDate 2016/11/15 * @Version 1.0 */ public class BaseFeed { private String token; private String status; private String message; private String solution; private int totalSize; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public int getTotalSize() { return totalSize; } public void setTotalSize(int totalSize) { this.totalSize = totalSize; } public String getSolution() { return solution; } public void setSolution(String solution) { this.solution = solution; } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455

作为所以接口返回实体映射基类,这个也需要和后台api开发人员协商好,至少我是这么做的。

8、NewsBean(新闻实体)代码如下:

public class NewsBean implements Serializable { /** * docid */ private String docid; /** * 标题 */ private String title; /** * 小内容 */ private String digest; /** * 图片地址 */ private String imgsrc; /** * 来源 */ private String source; /** * 时间 */ private String ptime; /** * TAG */ private String tag; public String getDocid() { return docid; } public void setDocid(String docid) { this.docid = docid; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDigest() { return digest; } public void setDigest(String digest) { this.digest = digest; } public String getImgsrc() { return imgsrc; } public void setImgsrc(String imgsrc) { this.imgsrc = imgsrc; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } public String getPtime() { return ptime; } public void setPtime(String ptime) { this.ptime = ptime; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788

RxJava 与 Retrofit 的结合小结

使用NewListActivity中onResume()方法调用 NewsPresenter中的loadNews(0,0),接着NewsPresenter再调用NewsModel()中的loadNews方法发起网络请求,同时把请求中的被观察者和观察者添加到Rx注册中心,注册中心统一管理所有网络请求。接下来NewsModel()初始化Retrofit以及各种基本参数添加,同时调用INewService网络协议真正发起网络请求。接下来网络请求被我们的ApiCallBack观察者所接收,然后网络数据又被观察者回调到NewsPresenter类中的观察者回调函数onSuccess中,再把所获得的网络数据使用getMvpView().addNews(modelBean.getT1348647909107())回发到NewListActivity中的addNews(List newsList)方法中,然后显示到UI列表中,最后退出时在onDestroy()中把Rx注册中心的当前请求清除。

效果图:

Demo现在地址: http://download.csdn.net/download/xinanheishao/9892674

如果对你有所帮助的话,赏我1元奶粉钱吧,多谢!

MVP+Retrofit+Rxjava实战

2017年07月07日 16:10:10 阅读数:4389

文章背景

在刚接触Android开始起,很长一段时间都在使用MVC模式开发项目,MVC模式很适合小项目的开发,简单方便。但当我们的项目越来越大时,MVC就显得力不从心,Activty或者Fragment中代码也会越来越多,导致项目的维护变的越来越复杂,业务剥离和复用难度大,一个新的框架或技术的出现必定是来解决前有的框架的缺陷,所以MVP的出现很优雅的解决了MVC中存在缺陷或不足。

文章目标

MVP在Android中的原理解析 MVP+Retrofit+Rxjava在项目中实战解析 架构经验分享

MVP简单介绍

MVC分层: View:对应于布局文件 Model:业务逻辑和实体模型 Controllor:对应于Activity 看起来的确像那么回事,但是细细的想想这个View对应于布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller(当然了Data-Binder的出现,可能会让View更像View吧)。这可能也就是为何,在该文中有一句这样的话:

Most of the modern android applications just use View-Model architecture,everything is connected with Activity.

而当将架构改为MVP以后,Presenter的出现,将Actvity视为View层,Presenter负责完成View层与Model层的交互。现在是这样的: View 对应于Activity,负责View的绘制以及与用户交互 Model 依然是业务逻辑和实体模型 Presenter 负责完成View于Model间的交互

MVP模式的核心思想

MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。

 模型(Model):负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;

 视图(View):负责界面数据的展示,与用户进行交互;

 主持人(Presenter):相当于协调者,是模型与视图之间的桥梁,将模型与视图分离开来。

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

这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面Model层可以封装复用,可以极大的减少代码量。当然,MVP还有其他的一些优点,这里不再赘述

MVP在真实项目中的实战

上面已经介绍过MVP的核心思想以及基本架构,当然我们在实际项目中不仅仅要把建构划分出来,还要加以延伸,这样才能够使项目的整体架构具备可扩展行、可复用性、可维护性、灵活性。下面我用我在实际项目中的角度来解析我所理解的MVP。

总体架构图:

项目目录结构:

1、View层

a、Iview接口代码如下:

/** * @Description MVP之V层 是所有VIEW的基类,其他类可以继承该类 * @Author ydc * @CreateDate 2016/10/10 * @Version 1.0 */ public interface Iview<T> { /** * @description 全局的显示加载框 * @author ydc * @createDate * @version 1.0 */ void showLoading(); /** * @description 全局的显示加载框 * @author ydc * @createDate * @version 1.0 */ void showLoading(String msg); /** * @description 全局的显示加载框 * @author ydc * @createDate * @version 1.0 */ void showLoading(String msg, int progress); /** * @description 全局的隐藏加载框 * @author ydc * @createDate * @version 1.0 */ void hideLoading(); /** * @description 全局消息展示 * @author ydc * @createDate * @version 1.0 */ void showMsg(String msg); /** * @description 全局错误消息展示 * @author ydc * @createDate * @version 1.0 */ void showErrorMsg(String msg, String content); /** * @description 关闭界面信息 * @author ydc * @createDate * @version 1.0 */ void close(); /** * @description 当前fragment是否有效 * @author ydc * @createDate * @version 1.0 */ boolean isActive(); } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768

可以看出Iview 接口是所以activity 和fragment最基本且共有的方法定义。

b、NewsView接口代码如下:

/**ydc 新闻列表所特有的方法定义 * Created by Administrator on 2017/7/6. */ public interface NewsView extends Iview { void addNews(List<NewsBean> newsList); void showLoadFailMsg(String msg); } 123456789

NewsView接口继承自Iview接口,定义新闻列表特有的方法。

c、BaseActivity基类代码如下:

package com.example.ydcretrofitmvp.Base; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.Window; /** * Created by Administrator on 2017/7/8. */ public class BaseActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); } @Override protected void onStart() { super.onStart(); } @Override protected void onResume() { super.onResume(); } @Override protected void onRestart() { super.onRestart(); } @Override protected void onPause() { super.onPause(); } @Override protected void onStop() { super.onStop(); } @Override protected void onDestroy() { super.onDestroy(); } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

BaseActivity作为所以activity的基类,你可以把所以activity共有的方法和属性提取到该中。

d、activity_main.xml布局文件代码如下:

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.ydcretrofitmvp.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recycle_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbars="vertical" tools:layout_editor_absoluteY="0dp" tools:layout_editor_absoluteX="8dp"> </android.support.v7.widget.RecyclerView> </android.support.constraint.ConstraintLayout> 1234567891011121314151617181920

布局里面仅仅放了一个RecyclerView,用来展示数据列表。

e、NewListActivity代码如下:

public class NewListActivity extends BaseActivity implements NewsView { private RecyclerView mRecyclerView; private LinearLayoutManager mLayoutManager; private NewsAdapter mAdapter; private List<NewsBean> mData; private int pageIndex = 0; private NewsPresenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPresenter = new NewsPresenter(); mPresenter.attachView(this); mPresenter.subscribe(); mRecyclerView = (RecyclerView)findViewById(R.id.recycle_view); mRecyclerView.setHasFixedSize(true); mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mAdapter = new NewsAdapter(getApplicationContext()); mRecyclerView.setAdapter(mAdapter); } @Override protected void onResume() { super.onResume(); mPresenter.loadNews(0,0); } @Override protected void onDestroy() { super.onDestroy(); if (mPresenter != null) { mPresenter.detachView(); mPresenter.unsubscribe(); } } @Override public void showLoading() { } @Override public void showLoading(String msg) { } @Override public void showLoading(String msg, int progress) { } @Override public void hideLoading() { } @Override public void showMsg(String msg) { } @Override public void showErrorMsg(String msg, String content) { } @Override public void close() { } @Override public boolean isActive() { return false; } @Override public void addNews(List<NewsBean> newsList) { mAdapter.isShowFooter(true); if(mData == null) { mData = new ArrayList<NewsBean>(); } mData.addAll(newsList); if(pageIndex == 0) { mAdapter.setmDate(mData); } else { //如果没有更多数据了,则隐藏footer布局 if(newsList == null || newsList.size() == 0) { mAdapter.isShowFooter(false); } mAdapter.notifyDataSetChanged(); } } @Override public void showLoadFailMsg(String msg) { } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113

在NewListActivity 中我们可以看到,NewListActivity 显示实现了NewsView 接口,实现了NewsView和Iview 未实现的方法,在代码中可以看出NewListActivity并没有做一些逻辑处理工作,仅仅做了添加数据和展示数据以及一些提示消息等工作,数据处理的工作都是调用 NewsPresenter 完成的。

2、Presenter层

a、Ipresenter 代码如下:

/** * @Description MVP的P层 * @Author ydc * @CreateDate 2016/10/10 * @Version 1.0 */ public interface Ipresenter<T extends Iview> { /** * @description 关联P与V(绑定,VIEW销毁适合解绑) * @author ydc * @createDate * @version 1.0 */ void attachView(T view); /** * @description 取消关联P与V(防止内存泄漏) * @author ydc * @createDate * @version 1.0 */ void detachView(); /** * @description RX订阅 * @author ydc * @createDate * @version 1.0 */ void subscribe(); /** * @description RX取消订阅 * @author ydc * @createDate * @version 1.0 */ void unsubscribe(); } 123456789101112131415161718192021222324252627282930313233343536373839404142

Ipresenter定义了所有presenter最基本且共有的方法。

b、BasePresenter代码如下:

/** * @Description 抽象的公用Presenter * @Author ydc * @CreateDate 20170707 * @Version 1.0 */ public abstract class BasePresenter<T extends Iview> implements Ipresenter<T> { protected T mMvpView;//所有View protected SubscriptionList mSubscriptions;//rx注册中心 protected DataRepository mDataCenter;//数据中心 //protected abstract SubscriptionList createSubscriptionList();//引入darger后取缔 /** * @description 获取V * @author ydc * @createDate * @version 1.0 */ public T getMvpView() { return mMvpView; } /** * @description view绑定P的时候初始化 * @author ydc * @createDate * @version 1.0 */ @Override public void attachView(T view) { this.mMvpView = view; this.mSubscriptions = new SubscriptionList(); this.mDataCenter = DataRepository.getInstance(); } /** * @description view失去绑定清除 * @author ydc * @createDate * @version 1.0 */ @Override public void detachView() { unsubscribe(); this.mMvpView = null; this.mSubscriptions = null; this.mDataCenter = null; } @Override public void unsubscribe(){ if(mSubscriptions!=null){ mSubscriptions.clear(); } } /** * @description 当前的view(fragemnt&activity是否存在) * @author ydc * @createDate * @version 1.0 */ public boolean isViewAttached() { return mMvpView != null; } /** * @description 是否viewb绑定过P * @author ydc * @createDate * @version 1.0 */ public void checkViewAttached() { if (!isViewAttached()) throw new MvpViewNotAttachedException(); } /** * @description p&v没有绑定的异常 * @author ydc * @createDate * @version 1.0 */ public static class MvpViewNotAttachedException extends RuntimeException { public MvpViewNotAttachedException() { super("Please call Presenter.attachView(MvpView) before requesting data to the Presenter"); } } /** * @description 统一添加订阅关联被观察者和观察者 * @author ydc * @createDate * @version 1.0 */ public void addSubscription(Observable observable, Subscriber subscriber) { if( observable!=null && subscriber!=null ){ if (mSubscriptions == null) { mSubscriptions = new SubscriptionList(); } mSubscriptions.clear(); mSubscriptions.add(observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber)); } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109

BasePresenter是一个abstract类,在实现Ipresenter的未实现的方法之外,又扩展了几个所以presenter共用的方法。

c、Presenter代码如下:

/**ydc 新闻类的协议也可以是接口 * Created by Administrator on 2017/7/6. */ abstract class Presenter extends BasePresenter<NewsView> { public abstract void loadNews(int type, int page); } 1234567

可以看出Presenter也是一个abstract类、继承自BasePresenter抽象类,同时定义了新闻列表所特有的方法。

d、NewsPresenter代码如下:

/** * Created by Administrator on 2017/7/6. */ public class NewsPresenter extends Presenter { private Model mModel; public NewsPresenter(){ mModel=new NewsModel(); } @Override public void loadNews(int type, int page) { addSubscription(mModel.loadNews("nc/article/headline/T1348647909107/0-20.html",0), new ApiCallBack<NewsRequestModel>() { @Override public void onStart() { getMvpView().showLoading(); } @Override public void onSuccess(NewsRequestModel modelBean) { if(modelBean!=null){ getMvpView().addNews(modelBean.getT1348647909107()); } } @Override public void onFailure(String errorMsg) { getMvpView().showLoadFailMsg(errorMsg); } @Override public void onFinished() { getMvpView().hideLoading(); } }); } @Override public void subscribe() { } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445

可以看出NewsPresenter持有view和model的接口或是抽象类,起到中转的作用。

3、Model层

a、Imodel接口代码如下:

** * @Description MVP的M层 * @Author ydc * @CreateDate 2016/10/10 * @Version 1.0 */ public interface Imodel { } 12345678

我这里其实并没有做什么,只是留了一个接口而已,你可以定义所以model的基本方法。

b、BaseModel代码如下:

/** * @Description 数据模型基础类 * @Author ydc * @CreateDate 2016/11/2 * @Version 1.0 */ public abstract class BaseModel implements Imodel { /** * @description 返回服务接口对象实例 * @author ydc * @createDate * @version 1.0 */ public <T> T createService(final Class<T> clazz) { validateServiceInterface(clazz); return (T) RxService.RETROFIT.createRetrofit().create(clazz); } /** * @description 校验接口合法性 * @author ydc * @createDate * @version 1.0 */ public <T> void validateServiceInterface(Class<T> service) { if (service == null) { //AppToast.ShowToast("服务接口不能为空!"); } if (!service.isInterface()) { throw new IllegalArgumentException("API declarations must be interfaces."); } if (service.getInterfaces().length > 0) { throw new IllegalArgumentException("API interfaces must not extend other interfaces."); } } } 1234567891011121314151617181920212223242526272829303132333435363738

这个类我是用它来做Retrofit的初始化以及网络请求的工作,当然我要进一步包装Retrofit,所以这里只看到一个方法调用。

c、Model代码如下:

/**ydc 获取数据的逻辑模块协议,也可以是接口,提供给P调用,在callback中更新V * Created by Administrator on 2017/7/6. */ public abstract class Model extends BaseModel { public abstract Observable<NewsRequestModel> loadNews(String url, int type); } 123456789

既然公共BaseModel的职责任命为整个网络调用的工作,那么我就要在抽象一个Model抽象类来定义新闻数据处理逻辑模块协议,提供给P调用。

d、NewsModel代码如下:

/**ydc 新闻数据处理协议 * Created by Administrator on 2017/7/6. */ public class NewsModel extends Model { private INewService service=createService(INewService.class); @Override public Observable<NewsRequestModel> loadNews(String url, int type) { Map<String, String> map = new HashMap<>(); //map.put("type", type+""); return service.getNewList(url,map); } } 123456789101112131415

这个类实现了Model作为具体的新闻列表数据处理层。

e、网络接口

/**网络接口 * Created by Administrator on 2017/7/6. */ public interface INewService { @GET Observable<NewsRequestModel> getNewList(@Url String url, @QueryMap Map<String, String> params); } 1234567891011

MVP总结:

当用户进入到NewListActivity界面之后,界面需要展示新闻列表信息给用户。首先NewListActivity会调用NewsPresenter的loadNews方法,NewsPresenter 的loadNews方法中又会调用NewsModel中的loadNews方法。NewsModel中的loadNews方法中就是加载数据的核心,通过Retrofit请求服务器接口获取数据,无论数据获取成功与否,都会通过ApiCallBack回调给NewsPresenter 。如果获取成功,NewsPresenter 会调用NewsView的addNews方法将获取的新闻列表信息展示到RecyclerView。如果获取失败,则调用NewsView的showLoadFialMsg方法向用户提示失败信息。

RxJava 与 Retrofit 的结合简单介绍

Retrofit 是 Square 的一个著名的网络请求库,是okHTTP的升级版,目前公认的最好的网络请求框架。

响应式编程RxJava就更不用说,它的强大之处只有用过的人才会体会得到。

Retrofit 除了提供了传统的 Callback 形式的 API,还有 RxJava 版本的 Observable 形式 API。下面我用对比的方式来介绍 Retrofit 的 RxJava 版 API 和传统版本的区别。

以获取一个 User 对象的接口作为例子。使用Retrofit 的传统 API,你可以用这样的方式来定义请求:

@GET("/user") public void getUser(@Query("userId") String userId, Callback<User> callback); 12

在程序的构建过程中, Retrofit 会把自动把方法实现并生成代码,然后开发者就可以利用下面的方法来获取特定用户并处理响应:

getUser(userId, new Callback<User>() { @Override public void success(User user) { userView.setUser(user); } @Override public void failure(RetrofitError error) { // Error handling ... } }; 123456789101112

其实 Retrofit传统的API调用与okHTTP功能和使用上没有什么本质的区别,它的强大之处在于与RxJava结合使用。

而使用 RxJava 形式的 API,定义同样的请求是这样的:

@GET("/user") public Observable<User> getUser(@Query("userId") String userId); 12

使用的时候是这样的:

getUser(userId) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onNext(User user) { userView.setUser(user); } @Override public void onCompleted() { } @Override public void onError(Throwable error) { // Error handling ... } }); 123456789101112131415161718

看到区别了吗?

当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。

对比来看, Callback 形式和 Observable 形式长得不太一样,但本质都差不多,而且在细节上 Observable 形式似乎还比 Callback 形式要差点。那 Retrofit 为什么还要提供 RxJava 的支持呢?

单个请求体现不出它的优势所在,但是情景复杂起来, Callback 形式马上就会开始让人头疼。

假设 /user 接口并不能直接访问,而需要填入一个在线获取的 token ,代码应该怎么写?

Callback 方式,可以使用嵌套的 Callback:

GET("/token") public void getToken(Callback<String> callback); @GET("/user") public void getUser(@Query("token") String token, @Query("userId") String userId, Callback<User> callback); ... getToken(new Callback<String>() { @Override public void success(String token) { getUser(token, userId, new Callback<User>() { @Override public void success(User user) { userView.setUser(user); } @Override public void failure(RetrofitError error) { // Error handling ... } }; } @Override public void failure(RetrofitError error) { // Error handling ... } }); 12345678910111213141516171819202122232425262728293031

倒是没有什么性能问题,可是迷之缩进而且充满了无穷无尽的回调,这种后果你懂我也懂,做过大项目的人应该更懂。

而使用 RxJava 的话,代码是这样的:

@GET("/token") public Observable<String> getToken(); @GET("/user") public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId); ... getToken() .flatMap(new Func1<String, Observable<User>>() { @Override public Observable<User> onNext(String token) { return getUser(token, userId); }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onNext(User user) { userView.setUser(user); } @Override public void onCompleted() { } @Override public void onError(Throwable error) { // Error handling ... } }); 12345678910111213141516171819202122232425262728293031

用一个 flatMap() 就搞定了逻辑,整个请求都在一条链当中。读者看到这里应该明白我为什么选择RxJava 与 Retrofit 的结合来处理网络请求。其实RxJava有两个比较核心的功能就是数据转换和线程调度,当然它还有其它的强大之处,只是我们用的最多的是这两个而已。

RxJava 与 Retrofit 的结合在本项目中的应用

1、RxService类代码如下:

/** * @Description RX&Retrofit * @Author ydc * @CreateDate 2016/10/31 * @Version 1.0 */ public enum RxService { RETROFIT; private Retrofit mRetrofit; private static final int READ_TIMEOUT = 60;//读取超时时间,单位秒 private static final int CONN_TIMEOUT = 50;//连接超时时间,单位秒 /** * @description Head信息拦截 * @author ydc * @createDate * @version 1.0 */ private Interceptor mHeadInterceptor = new Interceptor() {//头信息 @Override public Response intercept(Chain chain) throws IOException { //这个chain里面包含了request和response,所以你要什么都可以从这里拿 Request request = chain.request(); long t1 = System.nanoTime();//请求发起的时间 String method = request.method(); if ("POST".equals(method)) { StringBuilder sb = new StringBuilder(); if (request.body() instanceof FormBody) { FormBody body = (FormBody) request.body(); for (int i = 0; i < body.size(); i++) { sb.append(body.encodedName(i) + "=" + body.encodedValue(i) + ","); } sb.delete(sb.length() - 1, sb.length()); Log.d("NET_RELATIVE",String.format("发送请求 %s on %s %n%s %nRequestParams:{%s}", request.url(), chain.connection(), request.headers(), sb.toString())); } } else { Log.d("NET_RELATIVE",String.format("发送请求 %s on %s%n%s", request.url(), chain.connection(), request.headers())); } Response response = chain.proceed(initHead(null,chain)); long t2 = System.nanoTime();//收到响应的时间 //这里不能直接使用response.body().string()的方式输出日志 //因为response.body().string()之后,response中的流会被关闭,程序会报错,我们需要创建出一 //个新的response给应用层处理 ResponseBody responseBody = response.peekBody(1024 * 1024); Log.d("NET_RELATIVE", String.format("接收响应: [%s] %n返回json:【%s】 %.1fms %n%s", response.request().url(), responseBody.string(), (t2 - t1) / 1e6d, response.headers() )); return response; } }; /** * @description 创建Retrofit对象 * @author ydc * @createDate * @version 1.0 */ public Retrofit createRetrofit() { if(mRetrofit == null){ OkHttpClient client = new OkHttpClient.Builder()//初始化一个client,不然retrofit会自己默认添加一个 .addInterceptor(mHeadInterceptor) .connectTimeout(CONN_TIMEOUT, TimeUnit.MINUTES)//设置连接时间为50s .readTimeout(READ_TIMEOUT, TimeUnit.MINUTES)//设置读取时间为一分钟 .build(); mRetrofit = new Retrofit.Builder() .client(client) .baseUrl(URLRoot.API_PATH) .addConverterFactory(GsonConverterFactory.create())//返回值为Gson的支持(以实体类返回) .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//返回值为Oservable<T>的支持 .build(); } return mRetrofit; } /** * @description 头信息初始化 * @author ydc * @createDate * @version 1.0 */ public Request initHead(File file, Interceptor.Chain chain) { Request.Builder mBuilder = chain.request().newBuilder(); Map<String, String> map = new HashMap<String, String>(); //HeaderUtil.setHeader(map);//ydc 移植注释 if (file != null) { //String mSize = MD5Util.MD5(file.length() + ""); String mSize ="10"; //SharedUtil.setFileSize(mSize);//andy.fang 文件大小MD5保存头信息,方便后台校验 map.put("filesize", mSize);//上传下载文件的MD5值 不需要每个地方都加,在需要的地方加 } Set keys = map.keySet(); if (keys != null) { Iterator iterator = keys.iterator(); while (iterator.hasNext()) { String key = (String) iterator.next(); String value = (String) map.get(key); /*if(StrUtil.isNotBlank(key)&& StrUtil.isNotBlank(value)){ mBuilder.addHeader(key, value); }*/ } } //系统级请求参数 //mBuilder.addHeader("token", SharedUtil.getPreferStr("TOKEN"));//ydc 移植注释 // mBuilder.addHeader("v", "1.0"); // mBuilder.addHeader("format", "JSON"); // mBuilder.addHeader("appKey", "00001"); Request mRequest = mBuilder.build(); return mRequest; } /** * @description 返回服务接口对象实例 * @author ydc * @createDate * @version 1.0 */ public <T> T createService(final Class<T> service) { validateServiceInterface(service); return (T) RxService.RETROFIT.createRetrofit().create(service); } /** * @description 校验接口合法性 * @author ydc * @createDate * @version 1.0 */ public <T> void validateServiceInterface(Class<T> service) { if (service == null) { //Toast.ShowToast("服务接口不能为空!"); } if (!service.isInterface()) { throw new IllegalArgumentException("API declarations must be interfaces."); } if (service.getInterfaces().length > 0) { throw new IllegalArgumentException("API interfaces must not extend other interfaces."); } } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152

可以看出RxService类主要用来初始化Retrofit以及添加头部和系统参数,NewsModel初始化时,顺带完成了以上工作。

2、INewService接口代码如下:

/**网络接口 * Created by Administrator on 2017/7/6. */ public interface INewService { @GET Observable<NewsRequestModel> getNewList(@Url String url, @QueryMap Map<String, String> params); } 1234567891011

这个类是来定义新闻列表网络接口

3、管理被观察者和观察者

统一添加订阅关联被观察者和观察者

protected SubscriptionList mSubscriptions;//rx注册中心 /** * @description 统一添加订阅关联被观察者和观察者 * @author ydc * @createDate * @version 1.0 */ public void addSubscription(Observable observable, Subscriber subscriber) { if( observable!=null && subscriber!=null ){ if (mSubscriptions == null) { mSubscriptions = new SubscriptionList(); } mSubscriptions.clear(); mSubscriptions.add(observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber)); } } 123456789101112131415161718192021

RX取消订阅代码如下:

@Override public void unsubscribe(){ if(mSubscriptions!=null){ mSubscriptions.clear(); } } 123456

以上两段代码是在BasePresenter抽象类中。

4、把请求添加到rx注册中心SubscriptionLis中

5、ApiCallBack代码如下:

/** * @Description 处理网络数据处理完成后的回调响应(观察者) * @Author ydc * @CreateDate 2016/10/28 * @Version 1.0 */ public abstract class ApiCallBack<M> extends Subscriber<M> { /** * @description 成功接口回调,提供给View处理页面问题 * @author ydc * @createDate * @version 1.0 */ public abstract void onSuccess(M modelBean); /** * @description 失败接口回调,提供给View处理页面问题 * @author ydc * @createDate * @version 1.0 */ public abstract void onFailure(String errorMsg); /** * @description 请求结束,提供给View处理页面问题 * @author ydc * @createDate * @version 1.0 */ public abstract void onFinished(); @Override public void onNext(M modelBean) { if(modelBean!=null){ BaseFeed feed = (BaseFeed) modelBean; String status = feed.getStatus(); status="S";//由于是演示所以手动赋值,正在开发中和服务器协商好 if("S".equalsIgnoreCase(status)){ onSuccess(modelBean); }else { onFailure(feed.getMessage()); } } } @Override public void onStart() { super.onStart(); } @Override public void onCompleted() { onFinished(); } /** * @description 統一处理异常的回调 * @author Andy.fang * @createDate * @version 1.0 */ @Override public void onError(Throwable e) { e.printStackTrace(); if (e instanceof HttpException) { HttpException httpException = (HttpException) e; int exceptionCode = httpException.code(); String msg = httpException.getMessage(); if (exceptionCode == 401) { msg = "用户名密码错误,请重新登录!"; } if (exceptionCode == 403 || exceptionCode == 404 || exceptionCode == 407 || exceptionCode == 408) { msg = "网络链接超时,请稍后再试!"; } if (exceptionCode == 501 || exceptionCode == 502 || exceptionCode == 504) { msg = "服务器无响应,请稍后再试!"; } onFailure(msg); } else { onFailure(e.getMessage()); } onFinished(); } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485

ApiCallBack是一个抽象类,处理网络数据处理完成后的回调响应(即RxJava的观察者),把它和被观察者作为参数一起传入到addSubscription方法中:

6、NewsRequestModel代码如下

public class NewsRequestModel extends BaseFeed { public List<NewsBean> getT1348647909107() { return T1348647909107; } public void setT1348647909107(List<NewsBean> t1348647909107) { T1348647909107 = t1348647909107; } private List<NewsBean> T1348647909107; } 123456789101112

该类继承自BaseFeed,作为新闻列表接口返回实体映射,这个需要和后台api接口开发人员协商好再定义。

7、BaseFeed代码如下:

/** * @Description 返回接口的基类 * @Author ydc * @CreateDate 2016/11/15 * @Version 1.0 */ public class BaseFeed { private String token; private String status; private String message; private String solution; private int totalSize; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public int getTotalSize() { return totalSize; } public void setTotalSize(int totalSize) { this.totalSize = totalSize; } public String getSolution() { return solution; } public void setSolution(String solution) { this.solution = solution; } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455

作为所以接口返回实体映射基类,这个也需要和后台api开发人员协商好,至少我是这么做的。

8、NewsBean(新闻实体)代码如下:

public class NewsBean implements Serializable { /** * docid */ private String docid; /** * 标题 */ private String title; /** * 小内容 */ private String digest; /** * 图片地址 */ private String imgsrc; /** * 来源 */ private String source; /** * 时间 */ private String ptime; /** * TAG */ private String tag; public String getDocid() { return docid; } public void setDocid(String docid) { this.docid = docid; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDigest() { return digest; } public void setDigest(String digest) { this.digest = digest; } public String getImgsrc() { return imgsrc; } public void setImgsrc(String imgsrc) { this.imgsrc = imgsrc; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } public String getPtime() { return ptime; } public void setPtime(String ptime) { this.ptime = ptime; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788

RxJava 与 Retrofit 的结合小结

使用NewListActivity中onResume()方法调用 NewsPresenter中的loadNews(0,0),接着NewsPresenter再调用NewsModel()中的loadNews方法发起网络请求,同时把请求中的被观察者和观察者添加到Rx注册中心,注册中心统一管理所有网络请求。接下来NewsModel()初始化Retrofit以及各种基本参数添加,同时调用INewService网络协议真正发起网络请求。接下来网络请求被我们的ApiCallBack观察者所接收,然后网络数据又被观察者回调到NewsPresenter类中的观察者回调函数onSuccess中,再把所获得的网络数据使用getMvpView().addNews(modelBean.getT1348647909107())回发到NewListActivity中的addNews(List newsList)方法中,然后显示到UI列表中,最后退出时在onDestroy()中把Rx注册中心的当前请求清除。

效果图:

Demo现在地址: http://download.csdn.net/download/xinanheishao/9892674

如果对你有所帮助的话,赏我1元奶粉钱吧,多谢!

转载请注明原文地址: https://www.6miu.com/read-2627655.html

最新回复(0)