当RxJava遇上Retrofit
来源:互联网 发布:cet6网络课程 编辑:程序博客网 时间:2024/06/05 07:56
在项目开发过程中,我们或多或少的使用过很多网络请求库。基本点的就是原生的http请求框架,好比HttpClient以及HttpUrlConnection等,略懂android开发的估计无人不知android-async-http或者volley啥的,再往上走,你可能会接触okhttp等。今天我们将来介绍一个新的http请求框架,隆重推荐Retrofit
Retrofit是何方神圣
retrofit是Square公司出品的,为android和java提供一个类型安全的Http网络请求库,这里是官网地址。
Retrofit的优点
使用注解来描述http请求
1.URL参数的替换和query参数的支持
2.对象转化为请求体(如:JSON,protocol buffers等)
3.多重请求体和文件上传
以上都是官网描述
使用流程
- 权限
<uses-permission android:name="android.permission.INTERNET" />
- 1
这个没什么好说的,没有网络权限什么都做不了
- 导包
compile 'com.squareup.retrofit2:retrofit:2.0.0'compile 'com.squareup.retrofit2:converter-gson:2.0.0'compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'compile 'io.reactivex:rxjava:1.1.2'compile 'io.reactivex:rxandroid:1.1.0'
- 1
- 2
- 3
- 4
- 5
这里几个库的含义是:我们使用retrofit2.0去进行网络请求操作,同时我们使用gson去进行数据解析,并且结合rxjava去进行相应的代码编写
- 基本配置
new Retrofit.Builder().addCallAdapterFactory(RxJavaCallAdapterFactory.create()).addConverterFactory(GsonConverterFactory.create()).client(okhttpBuilder.build()).baseUrl(baseUrl).build();
- 1
这段就是使用RxJava,利用gson做解析(这边解析器可以设置注入Jackson之类的,甚至可以自定义),http引擎框架是okhttp
- API说明
Retrofit需要通过注解请求方法以及请求参数来表明应该如何去进行一个Http请求,目前内置了5种注解方式GET、POST、PUT、DELETE以及HEAD。同时资源的相对URL要在注解中明确的指出。比如请求方法
@Get("/a/b")
- 1
- api使用
配置都OK之后,现在就开始写URL接口了。
案例1
假设有这么一个请求
看看这个GET请求,有header也有urlParam。我们可以使用@Header对header部分进行描述,后面使用@Query去添加每一个跟随urlParam
@GET("weatherservice/cityname")Observable<WeatherModel> getWeatherModels(@Header("apikey") String apikey, @Query("cityname") String cityname);
- 1
- 2
同时如果你觉得一个一个的@Query写的有点烦,Retrofit支持使用@QueryMap,将请求的urlParam都存储在一个Map里
案例2
假设有这么一个请求,来自gankio
看看这个GET请求,跟之前的区别在于,他没有urlParam,但是参数是在url里面,这个时候我们就要采用动态替换url里面的参数的方法,如何做呢?用{}来表明url中的哪部分参数需要替换,相应的参数用@Path来注解同样的字符串
@GET("{type}/{pagenum}/{page}")public Observable<GankioModel> getGankioModels(@Path("type") String type, @Path("pagenum") int pagenum, @Path("page") int page);
- 1
- 2
案例3
假设有这么一个请求,
看看这个post请求,与之前的get请求基本类似,只不过请求参数在bodyparams里面了,这个也很简单,通过@Body注解来指定一个方法作为HTTP请求主体
@POST("shipin_kg/shipin_kg")public Observable<MovieModel> getMovieLists(@Header("apikey") String apikey, @Body MoviePostModel postModel);
- 1
- 2
案例4
我们在post请求的时候会遇到一种情况就是content-type被指定为某一种格式了
如果服务端告诉你,我们的请求一定要用x-www-form-urlencoded,那么之前说的那种@body就不起作用了,这个时候我们@FormUrlEncoded去表明这个content-type类型,同时要用@Field去处理每一个键值对
@FormUrlEncoded@POST("product_tool/tool/stagequan")Observable<ResponseModel> upload(@FieldMap Map<String, String> params);
- 1
- 2
- 3
当然一个个写@Field也很烦,可以直接用@FieldMap去统一用map来处理
案例5
上传文件时候content-type一般都是multipart/form-data,所以这边要加上 @Multipart 注解,同时每个请求部分需要使用 @Part 来注解。这边用七牛上传文件来说明
@Multipart@POST("http://upload.qiniu.com/")Call<ResponseBody> uploadImage(@PartMap Map<String, RequestBody> params);
- 1
- 2
- 3
同样使用了@PartMap
来看看RequestBody是怎么创建的
public static RequestBody create(final MediaType contentType, final File file) public static RequestBody create(MediaType contentType, String content)public static RequestBody create(final MediaType contentType, final byte[] content)
- 1
- 2
- 3
找了3个基本方法,它是为了告诉我们,你可以通过contentType以及内容组成任意一个RequestBody对象
RequestBody body = RequestBody.create(MediaType.parse("image/jpeg"), new File(Environment.getExternalStorageDirectory().getPath() + "/PictureTest/saveTemp.jpg"));params.put("file", body);params.put("token", RequestBody.create(MediaType.parse("text/plain"), token));params.put("x:jsonbody", RequestBody.create(MediaType.parse("text/plain"), "{}"));
- 1
- 2
- 3
- 4
案例6
刚才看过了上传,现在来看看下载。这边只要借鉴了小凳子提供的下载方法
一般情况下retrofit是将整个文件都读进内存里面的,这样会造成OOM,所以大文件下载需使用@Streaming,同时我们也需要使用动态地址以便于下载不同的文件,这边使用@Url来填充
@Streaming@GETCall<ResponseBody> downloadFileWithFixedUrl(@Url String url);
- 1
- 2
- 3
剩下的就是保存文件了
Response<ResponseBody> response=api.downloadFileWithFixedUrl("http://7b1g8u.com1.z0.glb.clouddn.com/app_newkey_release_8_4.apk").execute();try { if (response != null && response.isSuccessful()) { //文件总长度 long fileSize = response.body().contentLength(); long fileSizeDownloaded = 0; is = response.body().byteStream(); File file = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + "app_newkey_release_8_4.apk"); if (file.exists()) { file.delete(); } else { file.createNewFile(); } fos = new FileOutputStream(file); int count = 0; byte[] buffer = new byte[1024]; while ((count = is.read(buffer)) != -1) { fos.write(buffer, 0, count); fileSizeDownloaded += count; subscriber.onNext("file download: " + fileSizeDownloaded + " of " + fileSize); } fos.flush(); subscriber.onCompleted(); } else { subscriber.onError(new Exception("接口请求异常")); }} catch (Exception e) { subscriber.onError(e);} finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
具体使用
无论你是何种请求方式,在app上面调用的方式基本上都是差不多的,我就拿第一个天气预报的接口加以说明
WeatherApi api = Retrofit2Utils.getInstance(getApplicationContext()).enableCache(true).getRetrofit("http://apis.baidu.com/apistore/").create(WeatherApi.class);subscription = api.getWeatherModels("a7802d983b3d58ed6e70ed71bb0c7f14", "南京") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .unsubscribeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<WeatherModel>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(WeatherModel weatherModel) { if (!subscription.isUnsubscribed()) { Log.d("MainActivity", (weatherModel.getRetData().getCity() + " " + weatherModel.getRetData().getDate() + "-" + weatherModel.getRetData().getTime() + " " + weatherModel.getRetData().getWeather())); } } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
我这里使用了缓存操作,这个后面会加以说明。同时使用了Rxjava对请求的线程切换以及对返回结果进行调度
缓存
可以通过这篇文章Retrofit 源码解读之离线缓存策略的实现学习到Retrofit缓存的一些知识,真正实践时我是在这里发现如何使用的Github
public class CacheInterceptor implements Interceptor { @Override public Response intercept(Interceptor.Chain chain) throws IOException { Request request = chain.request(); //如果没有网络,则启用 FORCE_CACHE if(!isNetworkConnected()) { request = request.newBuilder() .cacheControl(CacheControl.FORCE_CACHE) .build(); } Response originalResponse = chain.proceed(request); if(isNetworkConnected()) { //有网的时候读接口上的@Headers里的配置 String cacheControl = request.cacheControl().toString(); return originalResponse.newBuilder() .header("Cache-Control", cacheControl) .removeHeader("Pragma") .build(); } else { return originalResponse.newBuilder() .header("Cache-Control", "public, only-if-cached, max-stale=3600") .removeHeader("Pragma") .build(); } } public static boolean isNetworkConnected() { Context context = Retrofit2Utils.context; if (context != null) { ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); if (mNetworkInfo != null) { return mNetworkInfo.isAvailable(); } } return false; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
本篇博文上的代码已经共享到Github上,欢迎大家多多提意见
参考文章
- Retrofit 源码解读之离线缓存策略的实现
- 【译】Retrofit 2 - 如何从服务器下载文件
- RxJava+Retrofit Samples解析
- Retrofit 2 + OkHttp 3 实现图片上传 (RxJava的方式)
- Retrofit笔记
- 使用Retrofit和Okhttp实现网络缓存。无网读缓存,有网根据过期时间重新请求
- Android Retrofit 2.0使用
http://blog.csdn.net/r17171709/article/details/51149350
- 当RxJava遇上Retrofit
- 当RxJava遇上Retrofit
- 当复仇者联盟遇上Dagger2、RxJava和Retrofit的巧妙结合
- 当复仇者联盟遇上Dagger2、RxJava和Retrofit的巧妙结合
- Retrofit+Rxjava
- Retrofit+RxJava
- Retrofit+RxJava
- Retrofit+RxJava
- Retrofit+RxJava
- RxJava+Retrofit
- rxjava+retrofit
- RxJava&Retrofit
- Retrofit+Rxjava
- Retrofit+RxJava
- RXJava & Retrofit
- Rxjava+retrofit
- rxjava+retrofit
- Rxjava & Retrofit
- 正则表达式
- 关于行为树插件Behavior Designer的学习(二)_Composites复合任务
- linux 14.04安装cuda7.5
- 编写一个程序 -9
- 使用MD5加密密码工具类
- 当RxJava遇上Retrofit
- 手风琴+ECharts
- ubuntu14 python2.7上安装scipy,numpy,matplotlib的方法
- Java序列化与反序列化
- jQuery的ready函数不执行,报错:Unexpected end of input "",原因是js有语法错误,js文件中的所有东西即变量,一点也没有创建到内存中
- 脚本输出01到15的数值之seq的用法
- 初探React框架的预备知识点笔记——随时更新
- BZOJ2462 二维hash
- mysql梳理了一些常识,先记录