安卓开发进阶之RxJava在实际项目中使用--第二篇
来源:互联网 发布:淘宝vr眼镜效果怎么样 编辑:程序博客网 时间:2024/06/08 04:12
关于RxJava原理分析,请参考仍物线写的文章—-给 Android 开发者的 RxJava 详解。本文不对原理作过多的分析,从最快上手的角度,让开发者使用起来,当我们有实践经验后回过头来看原理分析会更清晰。
本系列共有三篇文章,分别关于Rxjava的基础使用(最快,最实用),Retrofit使用(Github上star达22k+,安卓领域排名第一),最后是RxCache缓存(大部分app都支持离线查看功能)。
安卓开发进阶之RxJava在实际项目中使用–第一篇
这是第二篇,网络请求框架Retrofit的使用,包括封装,日志打印,添加头部信息,Get和Post。本来想把RxCache作为第三篇文章,但是想想没必要,就作为第二篇的一部分,所以这个系列暂时就只有这两篇文章。
首先添加引用
compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.squareup.retrofit2:retrofit:2.2.0'compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' compile 'com.squareup.okhttp3:logging-interceptor:3.6.0'
第三个将服务器返回的数据用Gson转换,第五个是日志拦截器,用来打印日志。
因为Retrofit是基于Okhttp的,所以先获取OkHttp,
private OkHttpClient getClient() { HttpLoggingInterceptor loggingInterceptor=new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { Log.d("resHttp",message);//打印服务器返回的内容 } }); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);//BODY表示显示服务器返回的内容体,还有其他级别比如Header OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request().newBuilder().cacheControl(CacheControl.FORCE_NETWORK) .addHeader("X-Requested-With", "XMLHttpRequest")//添加头部信息,比如session// .addHeader("Cookie", "JSESSIONID="+ ZxlVar.mySession) .build(); return chain.proceed(request);}}) .addInterceptor(loggingInterceptor).build(); return httpClient; }
然后通过单例模式获取Retrofit对象,其中传入上面的httpClient
public Retrofit getRetrofit(String hostUrl) { if (mRetrofit==null) mRetrofit=new Retrofit.Builder().baseUrl(hostUrl).client(getClient()) .addConverterFactory(GsonConverterFactory.create())//使用Gson来解析数据,这个可以自定义,但是一般不需要 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build(); return mRetrofit; }
使用之前,先新建类用来存放所有的调用接口,并指明调用方式(Get/Post),声明参数类型及个数,以及要解析的格式,比如
@GET("/jt/getApkVersion.htm") Observable<ApkUpdateEntity> getApkVersson(@Query("apkCode") String apkCode);
其对应的完整URL为:
http://app.xxx.com00/jt/getApkVersion.htm?apkCode=OperationTeminal
Post请求格式如下:
@POST("/jt/icCardManager/uploadIcCardRecord") Observable<CardUpResEntity> upLoadCardData(@Query("terminalCode") String terminalCode , @Query("icCardData") String data);
其对应的完整URL为:
http://app.xxx.com00/jt/icCardManager/uploadIcCardRecord?terminalCode=xxx&icCardData=xxx
服务器返回数据后,解析成ApkUpdateEntity格式,然后就可以调用里面的内容。涉及到私密性,这里使用网上公用接口
https://api.github.com/
不需要传参数,调用代码如下
MyApiProvider myApiProvider= MyApplication.getInstance().getRetrofit("https://api.github.com/")//一定以 / 结尾 .create(MyApiProvider.class); myApiProvider.getApkVersson().subscribeOn(Schedulers.io())//指定在子线程进行耗时处理 .observeOn(AndroidSchedulers.mainThread())//指定在UI线程更新UI .subscribe(new Consumer<TestEntity>() {//指定按照TestEntity解析 @Override public void accept(TestEntity testEntity) throws Exception {//TestEntity,这里是解析后的结果 //处理返回的结果 Toast.makeText(MainActivity.this,testEntity.getAuthorizations_url(),Toast.LENGTH_LONG).show(); } });
接口声明为:
@GET("/") Observable<TestEntity> getApkVersson();
如果将@GET(“/”) 换成@GET(“”)会报错 Missing either @GET URL or @Url parameter.
运行效果
至此就可以从服务器拿到相应的数据并做处理了。我们再看另外一个重要的话题–缓存。缓存至少有三个好处,一是减少网络请求次数,以降低对服务器的压力,二是缩短网络请求时间,从本地取数据肯定比从服务器取数据要快,三是网络情况差甚至断网后还可以查看数据,你现在还能看到断网了不能使用的app吗,很少了。既然缓存这么重要,那么一般是怎么做的呢?现在一般使用二级缓存,即内存缓存和硬盘缓存。内存没有数据,就去硬盘取,还没有,就从服务器取数据。注意:内存缓存会造成堆内存泄露,所有一级缓存通常要严格控制缓存的大小,一般控制在系统内存的1/4。这两种缓存方式分别对应ASimpleCache和DiskLruCache。大家可以去网上搜索,实现起来也不复杂,但是刚上手的人可能也要花不少时间。这都不重要,我要说的是,有了Retrofit支持的RxCache,前面那些东西都不要了,对,Retrofit已经帮我们实现了二级缓存,不用费老大的劲去自己实现,效果还不一定有人家的好。下面进入主题使用RxCache+Retrofit+RxJava实现二级缓存。
RxCache地址
先添加依赖
compile "com.github.VictorAlbertos.RxCache:runtime:1.8.0-2.x" compile 'com.github.VictorAlbertos.Jolyglot:gson:0.0.3'
然后获取RxCache对象
public CacheProvider getCacheProvider() { if (mCacheProvider==null) { mCacheProvider = new RxCache.Builder().persistence(MyRetrofitUtil.getCacheDir(getApplicationContext()), new GsonSpeaker()).using(CacheProvider.class); } return mCacheProvider; }
其中CacheProvider类用于存放需要缓存的接口,
public interface CacheProvider { @Expirable(false)//false表示内存不足系统回收时永远不回收 @LifeCache(duration = 60,timeUnit = TimeUnit.MINUTES)//60分钟有效时间 Observable<TestEntity> getApkVersson(Observable<TestEntity> obs);}
注意CacheProvider中的接口名getApkVersson要与MyApiProvider 中的一致。
调用:
MyApiProvider iGetData= MyApplication.getInstance().getRetrofit("https://api.github.com/").create(MyApiProvider.class); CacheProvider cache=MyApplication.getInstance().getCacheProvider();//获取CacheProvider对象 Observable<TestEntity> observable; observable= cache.getApkVersson( iGetData.getApkVersson());//调用接口(带缓存功能) observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<TestEntity>() { @Override public void accept(@NonNull TestEntity testEntity) throws Exception { Toast.makeText(MainActivity.this,"RxCache:"+testEntity.getAuthorizations_url(),Toast.LENGTH_LONG).show(); } });
运行效果
从演示中可以看到,关闭网络后依然可以拿到缓存数据展示。至此缓存功能已经实现,还有两个参数需要说明一下,
Observable<TestEntity> getApkVersson2(Observable<TestEntity> obs,EvictProvider evictProvider, DynamicKey dynamicKey);//EvictProvider表示是否强制刷新,下拉刷新时就需要强制刷新,DynamicKey表示要缓存第几页数据,当不传此参数时默认缓存第一页
传值方式为 new EvictProvider(true/false),new DynamicKey(page)
。
还有一点就是为什么使用Gson来解析,因为有GsonFormate工具。使用非常方便, 开发必备,还没使用的赶紧去上手。操作效果如下:
当然,上面介绍的内容不复杂,但很实用,还有很多额外的东西没有介绍,我之所以没有介绍,是因为我项目中没有用到,项目中用到的知识点都介绍了。我想,如果我不加区分的把所有东西都介绍一遍,不仅文章会显得又臭又长,还让人抓不住重点,那样做又与官方的API文档有什么分别呢?这也是很多人写博客的一个通病,其实我也有,但我会有意识的避免。
过程中遇到的问题1:在没有网的情况下点击第一个按钮会报错并且崩溃
resHttp: <-- HTTP FAILED: java.net.UnknownHostException: Unable to resolve host "api.github.com": No address associated with hostnameio.reactivex.exceptions.OnErrorNotImplementedException: Unable to resolve host "api.github.com": No address associated with hostname```
而且配置文件已经配置网络权限<uses-permission android:name="android.permission.INTERNET" />
,上网搜了一下也没解决,不是说没有配置权限,就是说模拟器有问题,重启模拟器,还有的说是服务器有问题,还是只能靠自己。仔细看错误内容,其中OnErrorNotImplementedException提示说该异常主要是指OnError方法没有实现,OnError方法是哪里的呢。依稀记得重写观察者Observer时要重写OnNext(),OnComplete(),OnError()等方法,对,就是这个OnError()。实现它就不报错了。
MyApiProvider myApiProvider= MyApplication.getInstance().getRetrofit("https://api.github.com/")//一定以 / 结尾 .create(MyApiProvider.class); myApiProvider.getApkVersson().subscribeOn(Schedulers.io())//指定在子线程进行耗时处理 .observeOn(AndroidSchedulers.mainThread())//指定在UI线程更新UI .subscribe(new Consumer<TestEntity>() {//指定按照TestEntity解析 @Override public void accept(TestEntity testEntity) throws Exception {//TestEntity,这里是解析后的结果 //处理返回的结果 Toast.makeText(MainActivity.this,testEntity.getAuthorizations_url(),Toast.LENGTH_LONG).show(); } });
修改后的代码为
MyApiProvider myApiProvider= MyApplication.getInstance().getRetrofit("http://api.github.com/")//一定以 / 结尾 .create(MyApiProvider.class); myApiProvider.getApkVersson().subscribeOn(Schedulers.io())//指定在子线程进行耗时处理 .observeOn(AndroidSchedulers.mainThread())//指定在UI线程更新UI .subscribe(new Observer<TestEntity>() {//new Observer()得到观察者,并订阅被观察者,调用时顺序相反 @Override public void onSubscribe(Disposable d) { } @Override public void onNext(TestEntity testEntity) { Toast.makeText(MainActivity.this,"NoCache:"+testEntity.getAuthorizations_url(),Toast.LENGTH_LONG).show(); } @Override public void onError(Throwable e) { Toast.makeText(MainActivity.this,"NoCacheError:"+e.toString(),Toast.LENGTH_LONG).show(); } @Override public void onComplete() { } });
也就是将new Consumer<TestEntity>()
后面的内容替换掉,不能偷懒只重写OnNext()方法,如果要考虑异常的话,还有重写OnError()方法。
过程中遇到的问题2:在没有网的情况下,第一次启动app,先点击第二个按钮会报错并且崩溃,如果点击了第一个按钮,再点击第二个按钮则没问题。
其实这与问题1是同一个问题但是表现不一样,没有缓存时点击缓存按钮则去网络请求,此时没有网,就出现问题1的场景,而先点击按钮1后,数据被缓存了,再点击缓存按钮就去取缓存没有去网络请求,所以不报错。嗯,就是这样。
修改后的运行效果
源码下载地址
- 安卓开发进阶之RxJava在实际项目中使用--第二篇
- 安卓开发进阶之RxJava在实际项目中使用--第一篇
- 如何在实际项目开发中使用LinQ技术
- 安卓RxJava第二弹之 操作符
- 安卓项目搭建以及实际开发经验
- Spark性能调优之——在实际项目中使用Kryo序列化
- Spark性能调优之——在实际项目中,使用fastutil优化数据格式
- 在java项目的实际开发和应用中
- 在安卓开发中使用正则表达式,高效率开发
- 安卓JNI/NDK开发3(进阶 在C代码中调用java方法)
- RxJava,Retrofit,OkHttp3在项目中结合使用
- 在项目中实际使用log4j日志功能
- iOS-SQLite在项目中实际使用(Objective-C)
- iOS-SQLite在项目中实际使用(Swift3)
- iOS-SQLite在项目中实际使用(Swift3)
- 在安卓项目中使用FontAwesome图标
- RxJava zip操作符在Android中的实际使用场景
- RxJava zip操作符在Android中的实际使用场景
- 如何监测Tableview loaddata结束
- Handler原理
- 一九八四
- DES 算法 Verilog 课设
- 【算法】图的应用之Dijkstra算法--单源最短路径的求解
- 安卓开发进阶之RxJava在实际项目中使用--第二篇
- Tomcat总结+问题积累(1)
- spring: Failed to read candidate component class异常
- Elasticsearch集群安装
- add path command
- 【JavaSE系列-基础篇6】——为什么使用泛型?
- 叉乘的一种符号表达
- OSG学习:多重纹理映射
- Android 实现沉浸式通知栏