版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuehuayous/article/details/78058170
前言:现在网络访问已经基本都是Retrofit + RxJava了,只不过有一些还是使用的RxJava1,比如我们目前的项目。为毛不升级为RxJava2,项目还是比较庞大的,改起来还是要费时费力,还要QA全覆盖区测试等等,所以暂时没有升级的想法。扯远了,我们这里主要来封装Retrofit + RxJava2,至于JSON解析是用Gson、FastJson是Jeckson还是LoganSquare等,这个就仁者见仁智者见智了。其实也很简单,只是一行代码的配置。
一、基本使用
1. 添加依赖
添加如下依赖,建议使用前去github看下,使用最新版本。
dependencies { // ... ... compile 'io.reactivex.rxjava2:rxjava:2.1.2' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.okhttp3:logging-interceptor:3.8.1' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' compile 'com.google.code.gson:gson:2.8.2' compile 'com.squareup.retrofit2:converter-gson:2.3.0' }
2. 编写实体类
这里以
http://123.57.31.11/androidnet/getArticleList接口为例,数据格式为:
{ "status": "OK", "msg": "获取成功!", "data": { "list": [ { "id": 0, "name": "十月秋花,人生几度", "author": "心怡", "category": "经典美文", "time": "2016-12-04 14:47:05", "point": 4953, "summary": "一梦红尘烟雨,窗外流云几许。", "content": "一梦红尘烟雨,窗外流云几许。云烟深处,水雾茫茫。阴无情,再好的花开也敌不过季节的流转。" } ] } }
将其抽象为ArticleResult:
public class ArticleResult { public String status; public String msg; public List<Article> list; public static class Article { public int id; public String name; // 文章名称 public String author; // 作者 public String category;// 分类 public String time; // 时间 public int point; // 点击量 public String summary; // 摘要 public String content; // 内容 } }
3. 编写接口
这里采用GET和POST两种方式。
public interface APIService { @GET("getArticleList") Observable<ArticleResult> getArticleList1( @Query("pageSize") int pageSize, @Query("page") int page); @FormUrlEncoded @POST("getArticleList") Observable<ArticleResult> getArticleList2( @Field("pageSize") int pageSize, @Field("page") int page); }
4. 初始化Retrofit
Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl("http://123.57.31.11/androidnet/") .build(); service = retrofit.create(APIService.class);
5. 接口调用
service.getArticleList1(10, 1) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<ArticleResult>() { @Override public void accept(ArticleResult articleResult) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } });
二、封装优化
1. 实体类抽象
由于返回的JSON数据有公共的状态标识:
{ "status": "OK", "msg": "获取成功!", "data": { // ... ... } }
这里采取将其抽象为HttpResult实体,内部的数据以泛型T标识,对应实体对象:
public class HttpResult<T> { public String status; public String msg; public T data; }
内部Article对象如下:
public class ArticleListResult { public List<Article> list; public static class Article { public int id; public String name; // 文章名称 public String author; // 作者 public String category;// 分类 public String time; // 时间 public int point; // 点击量 public String summary; // 摘要 public String content; // 内容 } }
API接口修改如下:
public interface APIService { @GET("getArticleList") Observable<HttpResult<ArticleListResult>> getArticleList1( @Query("pageSize") int pageSize, @Query("page") int page); @FormUrlEncoded @POST("getArticleList") Observable<HttpResult<ArticleListResult>> getArticleList2( @Field("pageSize") int pageSize, @Field("page") int page); }
接口调用如下:
service.getArticleList1(10, 1) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<HttpResult<ArticleListResult>>() { @Override public void accept(HttpResult<ArticleListResult> articleList) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } });
2. 实体数据剥壳
可以看到我们处理返回数据这里返回的数据为HttpResult<ArticleListResult>,那么能不能直接是ArticleListResult呢?熟悉Rx操作符的都知道map可以满足我们的需要。service.getArticleList1(10, 1) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) // 对返回数据进行剥壳 .map(new Function<HttpResult<ArticleListResult>, ArticleListResult>() { @Override public ArticleListResult apply(@NonNull HttpResult<ArticleListResult> articleListResult) throws Exception { return articleListResult.data; } }) .subscribe(new Consumer<ArticleListResult>() { @Override public void accept(ArticleListResult articleList) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } });
但是这样有一个问题,就是HttpResult中定义的status为"ERROR"时data为null,我们再取data就不合适了。
public class HttpResult<T> { public String status; public String msg; public T data; }将剥壳部分改为ResultTransform类,只有返回状态为“OK”的时候返回数据,这里可以根据自己项目中进行灵活判断。
public class ResultTransform<T> implements Function<HttpResult<T>, T> { @Override public T apply(@NonNull HttpResult<T> httpResult) throws Exception { if (!"OK".equals(httpResult.status)) { throw new APIException(httpResult.status, httpResult.msg); } return httpResult.data; } }APIException为RuntimeException的简单封装,其中的字段为返回JSON数据中标志状态信息。
public class APIException extends RuntimeException { private String errStatus; private String errMessage; public APIException(String errStatus, String errMessage) { this.errStatus = errStatus; this.errMessage = errMessage; } public String getErrorStatus() { return errStatus; } public String getErrorMessage() { return errMessage; } }为了避免ResultTransform每次访问网络都创建一次,把ResultTransform作为单例来调用。
public class ResultTransform<T> implements Function<HttpResult<T>, T> { private ResultTransform() { } public static ResultTransform getInstance() { return Holder.INSTANCE; } private static class Holder { private static final ResultTransform INSTANCE = new ResultTransform(); } @Override public T apply(@NonNull HttpResult<T> httpResult) throws Exception { if (!"OK".equals(httpResult.status)) { throw new APIException(httpResult.status, httpResult.msg); } return httpResult.data; } }接口调用就变为了这样:
service.getArticleList1(10, 1) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(ResultTransform.getInstance()) .subscribe(new Consumer<ArticleListResult>() { @Override public void accept(ArticleListResult articleList) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } });
3. 日志输出
在开发中经常需要查看server端返回的数据是否正常,或者你给server端的同学说xx接口返回不正确,他多半会说你把参数发我下我看看。这些数据最好是在开发期间都打印到控制台比较好。
这里我们使用JakeWharton大神的
Timber进行日志的输出。
添加以下依赖:
compile 'com.jakewharton.timber:timber:4.5.1'
对Timber不熟悉的可以参考下
《Timber的使用与源码解析》
在项目的Application中初始化Timber,这里只种一棵Debug树:
public class DomoApplication extends Application { @Override public void onCreate() { super.onCreate(); if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } } }在初始化Retrofit时添加日志拦截器:
HttpLoggingInterceptor loggerInterceptor = new HttpLoggingInterceptor( new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { Timber.tag("HttpLogging").i(message); } } ); loggerInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(loggerInterceptor) .build(); Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl("http://123.57.31.11/androidnet/") .build();OK,再访问接口的时候就会在我们的控制台下输入网络日志啦。
I/HttpLogging: --> POST http://123.57.31.11/androidnet/getArticleList http/1.1 I/HttpLogging: Content-Type: application/x-www-form-urlencoded I/HttpLogging: Content-Length: 17 I/HttpLogging: pageSize=1&page=1 I/HttpLogging: --> END POST (17-byte body) I/HttpLogging: <-- 200 OK http://123.57.31.11/androidnet/getArticleList (56ms) I/HttpLogging: Server: nginx I/HttpLogging: Date: Wed, 27 Sep 2017 10:22:21 GMT I/HttpLogging: Content-Type: application/json;charset=UTF-8 I/HttpLogging: Transfer-Encoding: chunked I/HttpLogging: Connection: keep-alive I/HttpLogging: Vary: Accept-Encoding I/HttpLogging: {"status": "OK","msg": "获取成功!","data": {"list": [{"id": 0,"name": "十月秋花,人生几度","author": "心怡","category": "经典美文","time": "2016-12-04 14:47:05","point": 4953,"summary": "一梦红尘烟雨,窗外流云几许。","content": "一梦红尘烟雨,窗外流云几许。云烟深处,水雾茫茫。阴无情,再好的花开也敌不过季节的流转。"}]}} I/HttpLogging: <-- END HTTP (9909-byte body)这样,请求方式、请求参数、响应码、数据就都打印出来啦,有一点就是返回的JSON数据格式不良好,能格式化下就好了。
在ResultTransform中添加日志输出,这里使用的是Gson,当然使用其他json转换工具的同学也可以使用对于的工具进行替换。
public class ResultTransform<T> implements Function<HttpResult<T>, T> { private static final String TAG = "ResultTransform"; private static final Gson gson = new GsonBuilder() .setPrettyPrinting() .create(); private ResultTransform() { } public static ResultTransform getInstance() { return Holder.INSTANCE; } private static class Holder { private static final ResultTransform INSTANCE = new ResultTransform(); } @Override public T apply(@NonNull HttpResult<T> httpResult) throws Exception { if (!"OK".equals(httpResult.status)) { throw new APIException(httpResult.status, httpResult.msg); } try { String json = gson.toJson(httpResult); Timber.tag("Response").i(json); } catch (Exception e) { Timber.tag(TAG).w(e, "Error when serialize %s", httpResult.toString()); } return httpResult.data; } }访问网络的时候日志输出就很清爽啦。
I/HttpLogging: --> POST http://123.57.31.11/androidnet/getArticleList http/1.1 I/HttpLogging: Content-Type: application/x-www-form-urlencoded I/HttpLogging: Content-Length: 17 I/HttpLogging: pageSize=1&page=1 I/HttpLogging: --> END POST (17-byte body) I/HttpLogging: <-- 200 OK http://123.57.31.11/androidnet/getArticleList (56ms) I/HttpLogging: Server: nginx I/HttpLogging: Date: Wed, 27 Sep 2017 10:22:21 GMT I/HttpLogging: Content-Type: application/json;charset=UTF-8 I/HttpLogging: Transfer-Encoding: chunked I/HttpLogging: Connection: keep-alive I/HttpLogging: Vary: Accept-Encoding I/HttpLogging: {"status": "OK","msg": "获取成功!","data": {"list": [{"id": 0,"name": "十月秋花,人生几度","author": "心怡","category": "经典美文","time": "2016-12-04 14:47:05","point": 4953,"summary": "一梦红尘烟雨,窗外流云几许。","content": "一梦红尘烟雨,窗外流云几许。云烟深处,水雾茫茫。阴无情,再好的花开也敌不过季节的流转。"}]}} I/HttpLogging: <-- END HTTP (9909-byte body) I/Response: { "status": "OK", "msg": "获取成功!", "data": { "list": [ { "id": 0, "name": "十月秋花,人生几度", "author": "心怡", "category": "经典美文", "time": "2016-12-04 14:47:05", "point": 4953, "summary": "一梦红尘烟雨,窗外流云几许。", "content": "一梦红尘烟雨,窗外流云几许。云烟深处,水雾茫茫。阴无情,再好的花开也敌不过季节的流转。" } ] } }
3. 全局配置
细心的同学会发现,在ResultTransform中,直接写的json格式化,这时是没有区分是否要输出日志的,尽管Timber在Release时不会输出。即没有输出时也进行了json的格式化,这是洁癖的我们不能忍受的。那就进行下全局配置参数的封装吧。
@Override public T apply(@NonNull HttpResult<T> httpResult) throws Exception { if (!"OK".equals(httpResult.status)) { throw new APIException(httpResult.status, httpResult.msg); } try { String json = gson.toJson(httpResult); Timber.tag("Response").i(json); } catch (Exception e) { Timber.tag(TAG).w(e, "Error when serialize %s", httpResult.toString()); } return httpResult.data; }
首先创建一个Configurator类,该类用于存放配置信息,只有一个key和value都为Object的HashMap类型的成员变量,并将其作为单例。
public final class Configurator { private static final HashMap<Object, Object> CONFIGS = new HashMap<>(); private Configurator() { } static Configurator getInstance() { return Holder.INSTANCE; } private static class Holder { private static final Configurator INSTANCE = new Configurator(); } }创建ConfigKeys类,作为配置信息的Key。这里先配置全局Context、APIHost、是否Release,ConfigReady用于标识配置完成。
public enum ConfigKeys { APPLICATION_CONTEXT, API_HOST, IS_RELEASED, CONFIG_READY }Configurator中添加对应的配置方法。
public final class Configurator { private static final HashMap<Object, Object> CONFIGS = new HashMap<>(); private Configurator() { CONFIGS.put(ConfigKeys.CONFIG_READY, false); } static Configurator getInstance() { return Holder.INSTANCE; } private static class Holder { private static final Configurator INSTANCE = new Configurator(); } public final void configure() { CONFIGS.put(ConfigKeys.CONFIG_READY, true); } public final Configurator withContext(Context context) { CONFIGS.put(ConfigKeys.APPLICATION_CONTEXT, context.getApplicationContext()); return this; } public final Configurator withApiHost(String host) { CONFIGS.put(ConfigKeys.API_HOST, host); return this; } public final Configurator withIsReleased(boolean released) { CONFIGS.put(ConfigKeys.IS_RELEASED, released); return this; } private void checkConfiguration() { final boolean isReady = (boolean) CONFIGS.get(ConfigKeys.CONFIG_READY); if (!isReady) { throw new RuntimeException("Configuration is not ready,call configure"); } } final <T> T getConfiguration(Object key) { checkConfiguration(); final Object value = CONFIGS.get(key); if (value == null) { throw new NullPointerException(key.toString() + " IS NULL"); } return (T) value; } }添加一个对外的操作类:
public class GlobalConfig { public static Configurator init(Context context) { Configurator configurator = Configurator.getInstance() .withContext(context); return configurator; } public static Configurator getConfigurator() { return Configurator.getInstance(); } public static <T> T getConfiguration(Object key) { return getConfigurator().getConfiguration(key); } public static Context getApplicationContext() { return getConfiguration(ConfigKeys.APPLICATION_CONTEXT); } }通GlobalConfig、Configurator、ConfigKeys,三个类,就可以方便优雅地控制全局的配置啦,当然根据项目的需要也可以灵活的添加。
在Application中配置:
public class DomoApplication extends Application { @Override public void onCreate() { super.onCreate(); GlobalConfig.init(this) .withApiHost("http://123.57.31.11/androidnet/") .withIsReleased(false) .configure(); Timber.plant(new Timber.DebugTree()); } }
4. Retrofit初始化封装
由于之前Retrofit初始化是在使用时进行的,如果多个地方都要调用接口,那都要初始化一遍显然是不合适的,之前的调用方式如下,而且没有区分Debug还是Release版本都进行了网络日志输出配置。可以通过读取配置,只在Debug时添加日志拦截器。
HttpLoggingInterceptor loggerInterceptor = new HttpLoggingInterceptor( new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { Timber.tag("HttpLogging").i(message); } } ); loggerInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(loggerInterceptor) .build(); Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl("http://123.57.31.11/androidnet/") .build(); service = retrofit.create(APIService.class);新建一个类HttpBuilder进行网络访问相关的初始化,在构建全局Retrofit客户端时使用的GsonConverterFactory,如果使用FastJson、Jackson等其他Json解析工具的同学可以灵活配置。
public class HttpBuilder { /** * 构建OkHttp */ private static final class OKHttpHolder { private static final int TIME_OUT = 30; private static final OkHttpClient.Builder BUILDER = new OkHttpClient.Builder(); private static OkHttpClient.Builder addInterceptor() { boolean isReleased = GlobalConfig.getConfiguration(ConfigKeys.IS_RELEASED); if (!isReleased) { HttpLoggingInterceptor loggerInterceptor = new HttpLoggingInterceptor( new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { Timber.tag("HttpLogging").i(message); } } ); loggerInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); BUILDER.addInterceptor(loggerInterceptor); } return BUILDER; } private static final OkHttpClient OK_HTTP_CLIENT = addInterceptor() .connectTimeout(TIME_OUT, TimeUnit.SECONDS) .build(); } /** * 构建全局Retrofit客户端 */ private static final class RetrofitHolder { private static final String BASE_URL = GlobalConfig.getConfiguration(ConfigKeys.API_HOST); private static final Retrofit RETROFIT_CLIENT = new Retrofit.Builder() .baseUrl(BASE_URL) .client(OKHttpHolder.OK_HTTP_CLIENT) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); }
/** * Service接口 */ private static final class ServiceHolder { private static final APIService API_SERVICE = RetrofitHolder.RETROFIT_CLIENT.create(APIService.class); } public static APIService getAPIService() { return ServiceHolder.API_SERVICE; }} 另外,这里只有一个接口APIServce,如果有多个网络接口类的可以一并初始化,如下:
/** * Service接口 */ private static final class ServiceHolder { private static final APIService API_SERVICE = RetrofitHolder.RETROFIT_CLIENT.create(APIService.class); private static final APIService API_SERVICE_1 = RetrofitHolder.RETROFIT_CLIENT.create(APIService1.class); } public static APIService getAPIService() { return ServiceHolder.API_SERVICE; } public static APIService getAPIService1() { return ServiceHolder.API_SERVICE_1; }接口调用时修改为:
HttpBuilder.getAPIService().getArticleList2(10, 1) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(ResultTransform.getInstance()) .subscribe(new Consumer<ArticleListResult>() { @Override public void accept(ArticleListResult articleList) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } });
5. 内存泄漏
在访问网络的时候如果关闭界面,这时网络模块会持有Activity的引用,造成内存泄漏。就要求我们在关闭界面时清除网络访问。
在接口使用时修改如下:
public class MainActivity extends AppCompatActivity { CompositeDisposable mSubscriptions; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSubscriptions = new CompositeDisposable(); } public void click(View view) { Disposable disposable = HttpBuilder.getAPIService().getArticleList2(10, 1) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(ResultTransform.getInstance()) .subscribe(new Consumer<ArticleListResult>() { @Override public void accept(ArticleListResult articleList) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } }); mSubscriptions.add(disposable); } @Override protected void onDestroy() { super.onDestroy(); mSubscriptions.clear(); } }
来看一下接口的使用:
Disposable disposable = HttpBuilder.getAPIService().getArticleList2(10, 1) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(ResultTransform.getInstance()) .subscribe(new Consumer<ArticleListResult>() { @Override public void accept(ArticleListResult articleList) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } }); mSubscriptions.add(disposable);可以看到还是不够简洁,那再对访问的部分封装下:
public class HttpHelper { public static <T> Observable<T> request(final CompositeDisposable subscriptions, final Observable<HttpResult<T>> observable) { final RequestDisposable disposableWrap = new RequestDisposable(); return Observable.create(new ObservableOnSubscribe<T>() { @Override public void subscribe(@NonNull final ObservableEmitter<T> e) throws Exception { disposableWrap.disposable = observable .map(ResultTransform.getInstance()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .onErrorReturn(ErrorReturnHolder.ERROR_RETURN) .subscribe( new Consumer<Object>() { @Override public void accept(Object o) throws Exception { if (o instanceof Throwable) { e.onError((Throwable) o); Timber.tag("HttpHelper.request() ==> onNext()").w((Throwable) o); } else { e.onNext((T) o); e.onComplete(); } } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { if (null != disposableWrap.disposable) { subscriptions.remove(disposableWrap.disposable); } Timber.tag("HttpHelper.request() ==> onError()").w(throwable); e.onError(throwable); } }, new Action() { @Override public void run() throws Exception { if (null != disposableWrap.disposable) { subscriptions.remove(disposableWrap.disposable); } } }); subscriptions.add(disposableWrap.disposable); } }); } private static class RequestDisposable { Disposable disposable; } private static final class ErrorReturnHolder { private static final Function ERROR_RETURN = new Function<Throwable, Throwable>() { @Override public Throwable apply(@NonNull Throwable throwable) throws Exception { return throwable; } }; } }这样再使用的时候就精简了不少:
HttpHelper.request(mSubscriptions, HttpBuilder.getAPIService().getArticleList2(10, 1)) .subscribe(new Consumer<ArticleListResult>() { @Override public void accept(ArticleListResult articleListResult) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } });
到此,对于网络的封装告一段落,有些同学可能觉得有点晕,我把代码放到github上,在
step_1分支。
$ git clone https://github.com/xuehuayous/RetrofitRxjavaDemo.git
$ cd RetrofitRxjavaDemo
$ git checkout step_1
三、数据层封装
通过以上的封装,在使用的时候已经非常方便了,但是在使用的时候你会发现一个问题,就是在Activity或者Fragment中直接调用,那就是直接把Model暴露给View层了,其实View层根本不关心你的数据来自哪里,View层会说,我管你是从网络、本地文件、还是内存缓存获取的,我要数据实体展示我该展示的就可以了。其实View层直接调用Model层也是不合适的,熟悉MVVM或MVP的同学都知道操作Model的应该是VM或P层。这里暂且为了封装Model层的方便直接在View层调用,后续会演变到MVVM上来。HttpHelper.request(mSubscriptions, HttpBuilder.getAPIService().getArticleList2(10, 1)) .subscribe(new Consumer<ArticleListResult>() { @Override public void accept(ArticleListResult articleListResult) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } });的确,在View层我们不想看到关于网络的任何事情,特别是带着明显标志的HttpHelper、HttpBuilder等。那就修改为如下图的模式:
之前封装的网络访问只是APIClient → API Server这一条线,接下要添加一个数据池,View层想要数据就在池子里面取就可以了,这个池子再去处理到底是在哪获取数据。
添加DataRepository基类,这个基类只有一个参数,而且只含有一个参数的构造方法,强制子类去实现。
public abstract class DataRepository { protected CompositeDisposable mSubscriptions; public DataRepository(CompositeDisposable subscriptions) { this.mSubscriptions = subscriptions; } }然后创建ArticleRepository继承DataRepository,getArticleList只是对网络层的封装,后续会进行复杂的,比如先获取缓存,设置缓存一天有效,如果过期则从网络获取并写入缓存。
public class ArticleRepository extends DataRepository { public ArticleRepository(CompositeDisposable subscriptions) { super(subscriptions); } public Observable<ArticleListResult> getArticleList1(int pageSize, int page) { return HttpHelper.request(mSubscriptions, HttpBuilder.getArticleService().getArticleList1(pageSize, page)); } public Observable<ArticleListResult> getArticleList2(int pageSize, int page) { return HttpHelper.request(mSubscriptions, HttpBuilder.getArticleService().getArticleList2(pageSize, page)); } }在onCreate中初始化ArticleRepository
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.mTvContent = (TextView) this.findViewById(R.id.tv_content); mSubscriptions = new CompositeDisposable(); mArticleRepository = new ArticleRepository(mSubscriptions); }调用如下,这样是不是在View层就看不到网络层的东西了,说实话只看下面的还真不知道是在网络获取的数据呢,这样我们的目的就达到啦。
mArticleRepository.getArticleList1(10, 1) .subscribe(new Consumer<ArticleListResult>() { @Override public void accept(ArticleListResult articleListResult) { // 处理返回数据 } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { // 处理错误数据 } });