RxJava+Retrofit+OkHttp 网络框架封装(一)
- 前言
春节过后是找工作的高峰期,我也不例外想找一个比较是自己满意的工作。在面试了几家公司每家居然都问RxJava,Retrofit,OkHttp的实现原理,趁离职期间还是把这个好好总结下。就先对他们做下简单的介绍。
RxJava+Retrofit+OkHttp 网络框架封装(一)
RxJava+Retrofit+OkHttp 网络框架封装(二)
首先简单介绍Retrofit、OKHttp和RxJava之间的关系:
- Retrofit:Retrofit是Square公司开发的一款针对Android 网络请求的框架(底层默认是基于OkHttp 实现)。
- OkHttp:也是Square公司的一款开源的网络请求库。
- RxJava :"a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。RxJava使异步操作变得非常简单。
各自职责:Retrofit 负责 请求的数据 和 请求的结果,使用 接口的方式 呈现,OkHttp 负责请求的过程,RxJava 负责异步,各种线程之间的切换。
Retrofit 创建过程和参数的讲解
一、引入Retrofit的包,在build.gradle文件中添加如下配置:
-
在这里我们先了解一下Android的项目架构,便于不熟悉Android项目架构的读者加以了解:
对于一个Android工程而言,有两个Gradle配置文件,一类是项目的Gradle配置,一类是工程的Gradle配置。每个项目(或称为模块)都有属于自己Gradle配置,整个工程有一个通用的Gradle配置。
1)、项目中的Gradle配置主要管理项目的属性和依赖;
2)、工程中的Gradle配置主要用于管理全部项目的通用属性和工程的建构,如工程构建脚本(buildscript),库的下载地址(jcenter)等。 -
工程中最基本的Gradle配置分为三部分,即 buildscript、allprojects、task clean。
1) buildscript 表示构建脚本,依赖库来源于 Jcenter,依赖于Gradle的Build Tools 工具包;
2) allprojects 表示全部项目的通用配置,依赖库的来源于 Jcenter;
3) task clean 表示清除命令(gradle clean)脚本,删除工程根目录的build文件。 -
当开发小的工程时, 开发人员很少会修改工程的Gradle配置,而是更关注项目的Gradle配置。在Gradle中,一个重要的部分就是添加第三方的依赖(dependencies)。使用第三方库,可以避免重复创建轮子,加快开发速度,提升代码的稳定性。
compile 'com.squareup.retrofit2:retrofit:2.3.0'//导入retrofit
compile 'com.google.code.gson:gson:2.6.2'//Gson 库
//下面两个是RxJava 和 RxAndroid
compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
compile 'io.reactivex.rxjava2:rxjava:2.x.y'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'//转换器,请求结果转换成Model
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//配合Rxjava 使用
二、定义数据Bean结构
public class DataChildBean{
private String source;
private String unit;
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
}
三、设计api接口
使用 POST,GET 请求方式时,只需要更改方法定义的标签,用 @POST ,@GET标签,参数标签用 @Field 或者 @Body 或者@ FieldMap
public interface MovieService {
//比值标题栏列表
@GET("rest/startFigure/tradingHelper")
Call<DataChildBean> getMeasureQuotations(@Query("start") int start , @Query("count") int count);
@FormUrlEncoded
@POST("rest/measure/getLast")
Call<DataChildBean> getMeasureInstruction(@Field("start") int start , @Field("count") int count);
@POST("rest/options/exerviseprice")
Call<BaseModel<ArrayList<String>>> getExervisePrice(@Body RequestBody body);
}
网络请求方法和网络请求参数标签的讲解
- 网络请求方法
1、@HTTP
作用:替换@GET、@POST、@PUT、@DELETE、@HEAD注解的作用 及 更多功能拓展
具体使用:通过属性method、path、hasBody进行设置
public interface MovieService {
/**
* method:网络请求的方法(区分大小写)
* path:网络请求地址路径
* hasBody:是否有请求体
*/
@HTTP(method = "GET", path = "blog/{id}", hasBody = false)
Call<ResponseBody> getCall(@Path("id") int id);
// {id} 表示是一个变量
// Call 接受返回对象
// method 的值 retrofit 不会做处理,所以要自行保证准确
}
2、@GET、@POST 这里就不介绍了
-
标记类
1、@FormUrlEncoded
作用:表示发送form-encoded的数据
2、@Multipart
作用:表示发送form-encoded的数据(适用于 有文件 上传的场景)
public interface MovieService {
/**
*表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* <code>Field("username")</code> 表示将后面的 <code>String name</code> 中name的取值作为 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
/**
* {@link Part} 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型
* 除 {@link okhttp3.MultipartBody.Part} 以外,其它类型都必须带上表单字段({@link okhttp3.MultipartBody.Part} 中已经包含了表单字段的信息),
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);
}
- 网络请求参数
1、@Header & @Headers
作用:添加请求头 &添加不固定的请求头
// @Header
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call<User> getUser()
// 以上的效果是一致的。
// 区别在于使用场景和使用方式
// 1. 使用场景:@Header用于添加不固定的请求头,@Headers用于添加固定的请求头
// 2. 使用方式:@Header作用于方法的参数;@Headers作用于方法
2、 @Body
作用:以 Post方式 传递 自定义数据类型 给服务器,可以传输json文件
特别注意:如果提交的是一个Map,那么作用相当于 @Field
//POST 网络请求, RequestBody来实现传输JSON文件
@POST("rest/basis/modifyCrossMonthSubtractionContract")
Call<ResponseBody> modifyCrossMonthSubtractionContract(@Body RequestBody body);
//具体实现
AddOrEditMonth addOrEditMonth = new AddOrEditMonth();
addOrEditMonth.setLeftContract(mContractOne);
addOrEditMonth.setLeftName(mNameOne);
addOrEditMonth.setOldLetfContract(oldLetfContract);
String d = GsonUtil.toJson(addOrEditMonth);
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),GsonUtil.toJson(addOrEditMonth));
Call<ResponseBody> call2 = service.modifyCrossMonthSubtractionContract(body );
3、@Field & @FieldMap
作用:发送 Post请求 时提交请求的表单字段
具体使用:与 @FormUrlEncoded 注解配合使用
public interface MovieService {
/**
*表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* <code>Field("username")</code> 表示将后面的 <code>String name</code> 中name的取值作为 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
/**
* Map的key作为表单的键
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded2(@FieldMap Map<String, Object> map);
}
// 具体使用
// @Field
Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
// @FieldMap
// 实现的效果与上面相同,但要传入Map
Map<String, Object> map = new HashMap<>();
map.put("username", "Carson");
map.put("age", 24);
Call<ResponseBody> call2 = service.testFormUrlEncoded2(map);
4、@Url
作用:直接传入一个请求的 URL变量 用于URL设置
@GET
Call<ResponseBody> testUrl@Url String ur);
// 当有URL注解时,@GET传入的URL就可以省略
// 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供
//用法
String url = "/sso/checkregist/" + etLoginName.getText().toString();
Call<ResponseBody> call2 = service.testUrl(map);
5、 @Path
作用:URL地址的缺省值
@GET("users/{user}/repos")
Call<ResponseBody> testPath(@Path("user") String user );
// 访问的API是:https://api.github.com/users/{user}/repos
// 在发起请求时, {user} 会被替换为方法的第一个参数 user(被@Path注解作用)
6、@Part & @PartMap
作用:发送 Post请求 时提交请求的表单字段与@Field的区别:功能相同,但携带的参数类型更加丰富,包括数据流,所以适用于 有文件上传 的场景
具体使用:与 @Multipart 注解配合使用
public interface MovieService {
//上传单个文件
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);
//上传单个文件
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload2(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file);
//上传多个文件
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload3(@PartMap Map<String, RequestBody> args);
}
// 具体使用
MediaType textType = MediaType.parse("text/plain");
RequestBody name = RequestBody.create(textType, "Carson");
RequestBody age = RequestBody.create(textType, "24");
RequestBody file = RequestBody.create(MediaType.parse("application/octet-stream"), "这里是模拟文件的内容");
// @Part
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
Call<ResponseBody> call3 = service.testFileUpload1(name, age, filePart);
ResponseBodyPrinter.printResponseBody(call3);
// @PartMap
// 实现和上面同样的效果
Map<String, RequestBody> fileUpload2Args = new HashMap<>();
fileUpload2Args.put("name", name);
fileUpload2Args.put("age", age);
//这里并不会被当成文件,因为没有文件名(包含在Content-Disposition请求头中),但上面的 filePart 有
//fileUpload2Args.put("file", file);
Call<ResponseBody> call4 = service.testFileUpload2(fileUpload2Args, filePart); //单独处理文件
ResponseBodyPrinter.printResponseBody(call4);
7、@Query和@QueryMap
作用:用于 @GET 方法的查询参数(Query = Url 中 ‘?’ 后面的 key-value)
具体使用:配置时只需要在接口方法中增加一个参数即可:
@GET("/")
Call<String> cate(@Query("cate") String cate);
@GET("News")
Call<NewsBean> getItem(@QueryMap Map<String, String> map);
// 其使用方式同 @Field与@FieldMap
四、创建一个Retrofit 实例,并且完成相关的配置:
配置了接口的 URL 和一个 converter , GsonConverterFactory 是默认提供的 Gson转换器。
public static final String URL = "https://liveapp.shmet.com/mapi/";
//在创建Retrofit实例时通过.baseUrl()设置
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL) //设置网络请求的Url地址
.addConverterFactory(GsonConverterFactory.create())
.build();
- 网络请求的完整 Url =在创建Retrofit实例时通过.baseUrl()设置 +网络请求接口的注解设置(下面称 “path“ )
- 具体整合的规则如下:
1、path = 完整的url
Url = "http://host:port/a/path"
path = "http://host:port/a/path"
baseUrl = 不设置
(即:接口中的Url是一个完整的网址,在Retrofit的实例中可以不设置URL)
2、path = 绝对路径
Url = "http://host:port/a/path"
path = "/path"
baseUrl = "http://host:port/a"
3、path = 相对路径 baseUrl = 目录形式
Url = "http://host:port/a/path"
path = "path"
baseUrl = "http://host:port/a/"
4、path = 相对路径 baseUrl = 文件形式
Url = "http://host:port/a/path"
path = "path"
baseUrl = "http://host:port/a"
建议采用第三种方式来配置,并尽量使用同一种路径形式。
五、获得api接口的代理对象(即创建网络请求接口实例):
// 创建 网络请求接口 的实例
MovieService request = retrofit.create(MovieService.class);
//对 发送请求 进行封装
Call<Reception> call = request.getCall();
六、发送网络请求(异步 / 同步),处理返回数据
通过response类的 body()对返回的数据进行处理
//发送网络请求(异步)
call.enqueue(new Callback<Translation>() {
//请求成功时回调
@Override
public void onResponse(Call<Translation> call, Response<Translation> response) {
//请求处理,输出结果
response.body().show();
}
//请求失败时候的回调
@Override
public void onFailure(Call<Translation> call, Throwable throwable) {
System.out.println("连接失败");
}
});
// 发送网络请求(同步)
Response<Reception> response = call.execute();
// 对返回数据进行处理
response.body().show();
- Retrofit 的介绍先简单的介绍这些,以后在更加详细的补充。