Retrofit和OkHttp都是出自Square,目前在Android端的网络请求框架,Retrofit+OkHttp+RxJava组合算是最流行的了。那么本篇文章就先来介绍一下Retrofit的使用方式。
Retrofit的GitHub地址:https://github.com/square/retrofit
Retrofit是一个基于RESTful API的网路请求工具,它能够将一个java网络请求接口翻译为一个Http的请求对象
,然后将该请求对象交由OkHttp来完成网络请求的处理,在OkHttp收到响应后会将结果交给Retrofit来按照用户设置的格式做进一步的解析处理。
闲话不多说,直接上代码来看Retrofit的使用,它的用法也非常的简洁。
一、入门使用
1、添加依赖
目前我所使用的是目前最新的版本:2.4.0
implementation 'com.squareup.okhttp3:okhttp:3.5.0'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
使用Retrofit同时需要添加OkHttp的依赖,因为Retrofit的网络请求是交给OkHttp来执行的,这里OkHttp的版本我选用的是3.5.0这个版本。
一般我们在网络请求到数据结果后会通过json工具将结果转换为我们需要的数据类型,Retrofit已经帮我们考虑到了一点,只需要简单的设置一下Retrofit就可以自动的来帮我做解析处理。那么还需要添加下面这个依赖,它的作用就是用来将请求数据解析为我们需要的数据类型,它使用的是GSON来进行解析的。
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
当然我们如果还想要配合上RxJava,也只需要添加一个下面这个依赖即可,Retrofit都为我们考虑到了。
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
2、定义请求接口
这里我以请求天气的接口为例来讲解如何使用Retrofit。
请求的url为:https://www.sojson.com/open/api/weather/json.shtml?city={city}
首先我们需要定义一个接口,这个接口不能继承别的接口,否则会抛出异常,如下所示。
public interface WeatherInterface {
@GET("open/api/weather/json.shtml")
Call<Weather> getWeather(@Query("city") String city);
}
我们定义的方法实际上就是我们网络请求方法,这里只需要传递一个city参数就可以获取对应城市的天气信息。这里指定了使用GET方式来请求,在GET注解中设置了请求的url地址,使用了Query注解来标识传递进来的参数是一个url中的查询参数。开头说过Retrofit符合RESTful API规范的请求框架,所以这里的url格式看起来就是RESTful规范的。至于url中的主机端口号,需要在创建Retrofit客户端的时候指定,它会和我们在GET中设置的url拼接为一个完整的url地址。
3、创建初始化Retrofit客户端
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.sojson.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
Retrofit客户端是通过Builder模式来创建的,在builder中首先设置了一个baseUrl,这个是必须要设置的,它会和接口中传递的url进行拼接为一个完整的url,而且这个baseurl必须要以”/”结尾,否则会抛出异常。接着设置了衣蛾GsonConverterFactory,它 作用是用来对请求结果进行解析为我们需要的数据格式,比如我们这里请求天气需要最终解析为Weather这个结构,它们这个Converter的作用就是使用gson解析出一个Weather对象出来,所以我们在接口定义中返回的类型中就是Call。
如果Retrofit需要和RxJava配合使用,那么这里还需要增加下面这一行:
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
它的作用就是会将一个call请求转换为一个Observable对象,这样就可以使用RxJava来处理请求了。
最后通过build()方法将Retrofit客户端创建出来了。
这里的baseUrl一定要设置,否则在创建retrofit对象的时候会抛出异常。
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
...
}
}
4、创建接口对象
这也是我们的最后一步了,只需要通过Retrofit客户端创建出一个接口对象就可以了。在上面我们定义的请求接口当然不能直接使用,它什么具体实现都没有,这时候Retrofit的作用就体现出来了,只需要简单的一行代码,就可以使用这个接口了。
WeatherInterface weatherInterface = retrofit.create(WeatherInterface.class);
通过create()方法,我们就创建了一个接口对象出来了,这样我们就可以直接调用接口里面的方法来进行网络请求了。
//通过接口获取一个call对象
Call<Weather> callSZ = weatherInterface.getWeather(cityEdit.getText().toString().trim());
//异步执行该call请求
callSZ.enqueue(new Callback<Weather>() {
@Override
public void onResponse(Call<Weather> call, Response<Weather> response) {
weatherInfo.setText(response.body().toString());
}
@Override
public void onFailure(Call<Weather> call, Throwable t) {
weatherInfo.setText("请求失败");
}
});
如果熟悉OkHttp的用法的话,应该对call不陌生,这里除了可以异步执行请求外,还可以同步执行请求,只需要调用execute()即可。
Response<Weather> response = callSZ.execute();
weatherInfo.setText(response.body().toString());
通过上面这些内容,我们已经清楚了Retrofit的简单用法,下面我们继续来详细的介绍Retrofit中的内容。
二、Retrofit中的注解说明
通过上面的接口定义,我们大概也清楚了Retrofit在设置请求的参数的时候,都是通过注解的形式来设置的,比如请求方法、请求url、请求参数、请求头信息、表单信息等都是通过注解的形式来设置的。这样我们也就不用写一堆麻烦枯燥的代码,这样简洁明了、清晰易读。
Retrofit总共定义了如下这些注解:
1、请求方法注解
Retrofit包含如何几个请求方法注解,它们分别对应了HTTP协议中的请求方法:
GET、POST、PUT、DELETE、HEAD、OPTIONS、PATCH、HTTP
这里的HTTP注解比较特殊,它可以替代前面7种注解,通过HTTP注解同样可以指定请求方法,请求url地址。
@Target(METHOD)
@Retention(RUNTIME)
public @interface HTTP {
String method();
String path() default "";
boolean hasBody() default false;
}
这几个注解都是使用在方法上面的。
2、标记类注解
Retrofit提供了3种注解用于标记,它们不需要额外的参数,只是起到一个标记作用,也是作用于方法上面。
FormUrlEncoded、Multipart和Streaming。其中FormUrlEncoded和Multipart用于设置表单提交的数据编码格式,分别对应着application/x-www-form-urlencoded和multipart/form-data,而Streaming则表示获取到响应后直接返回流的形式。Retrofit默认情况下会将响应的数据全部加载到内存中,如果在做文件下载这种请求数据比较大的情况下,就需要使用Streaming这种方法了,防止内存溢出。
3、请求参数类注解
这一类注解主要是用于设置请求中需要的参数。Retrofit定义了如下参数类型的注解:
Body、Field、FieldMap、Header、HeaderMap、Headers、Part、PartMap、Path、Query、QueryMap、QueryName、Url。
这一类型的注解比较多,不过仔细看还是会发现就那么几种。
设置请求头:Header、HeaderMap、Headers
设置请求体:Body、Field、FieldMap、Part、PartMap
设置Url中的参数:Path、Query、QueryMap、QueryName、Url
Header
Header这个注解作用于接口方法参数上面,它的作用是将设置的一个参数转换为一个请求头,例如:@POST("user/login") Call<String> login(@Header("cookie") String cookie, String name, String pwd);
这里会将传递的cookie参数设置为一个请求头,请求头的名称为cookie。
Headers
Headers和Header的作用是一样的,都是为了设置请求头,Headers一次性可以设置多个请求头,只是它们的使用方式有一些区别而已,Headers作用于方法上面。例如:@Headers({"cookie:123456789","content-length:20"}) @GET("user/login") Call<String> login(String name, String pwd);
HeaderMap
使用HeaderMap来设置请求头的话相比上面两种应该说更灵活强大,它作用于请求参数,可以将一个map集合转换为请求头,例如:Map<String,String> map = new HashMap<>(); map.put("Accept-Encoding","gzip,deflate"); map.put("Connection","keep-alive"); @GET("user/login") Call<String> login(@HeaderMap Map<String,String> headers, String name, String pwd);
Body
该注解用于标识请求的参数作为请求体的一部分,在POST、PUT请求方法中使用,例如:@POST("user/login") Call<String> login(@Body String name, @Body String pwd);
Field
标识用于表单提交的时候将参数设置为表单数据,主要是key:value这种数据。需要和FormUrlEncoded注解配合使用,例如:@POST("user/login") @FormUrlEncoded Call<String> login(@Body String name,@Body String pwd,@Field("nickName") String nickName, @Field("age") int age);
nickName和age将会被用作表单数据提交。
FieldMap
FieldMap类似于HeaderMap,将一个map集合的数据用于表单提交,同样需要和FormUrlEncoded注解配合使用。例如:Map<String,Object> map = new HashMap<>(); map.put("nickName","zhangsan"); map.put("age",16); @POST("user/login") @FormUrlEncoded Call<String> login(@Body String name, @Body String pwd, @FieldMap Map<String,Object> map);
Part
Part也是用于表单提交表单提交,它和Field功能类似,不过它能提交的数据更加的丰富,比如文件上传等,它需要和Multipart注解配合使用。例如:@POST("user/login") @Multipart Call<String> login(@Part("name") RequestBody name, @Part("password") RequestBody pwd, @Part MultipartBody.Part photo);
Part注解的使用需要有几点注意:
- 1、如果参数类型是RequestBody,则内容将会直接使用,Part注解中需要设置表单字段的名称
- 2、如果参数类型是MultipartBody.Part,内容将被直接使用,Part注解不需要写上表单字段名称,因为在实际创建MultipartBody.Part的时候就会指定名称
- 3、其他任意类型,Part注解必须要设置表单字段,Converter对将该参数转换为合适的表单内容。
- PartMap
与FieldMap类似,可以标识一个map集合的元素作为表单内容,用法就不说了,和FieldMap类似。 Path
Path注解可以动态替换URL中path部分的字段参数,举个例子就明白了,比如这里用户登录的时候,url请求中的id是需要根据不同用户动态替换的,那么就可以使用@Path,我们只需要在url中将需要动态替换的部分使用{}包起来,这样Retrofit就会自动的在参数中使用@Path注解标识的参数来动态替换。@GET("user/{id}/login") Call<String> login(@Path("id") String id, String name, String pwd);
Query
Query注解用来设置GET请求url地址中查询部分的参数,比如还是以登录为例,使用GET方式,name和password参数将会出现在url的查询部分,即?后面key&value这种形式,使用方式如下:@GET("user/login") Call<String> login(@Query("name") String name, @Query("password") String pwd);
- QueryMap
QueryMap和上面的FieldMap类似,它会将map中所有的key-value设置为url的查询参数,具体的用法就不说了。如果我们的查询参数特别多的话,那么就可以使用这种方式来处理。 QueryName
QueryName目前还不太清楚它有什么神奇的作用,它和Query的区别是query会将参数作为key&value中的value来处理,而QueryName则是将它作为key来处理,没有value值,看下这里的源码就知道了。//Query static final class Query<T> extends ParameterHandler<T> { ... @Override void apply(RequestBuilder builder, @Nullable T value) throws IOException { if (value == null) return; String queryValue = valueConverter.convert(value); if (queryValue == null) return; 这里的name则是Query注解中设置的name值,value则是参数值 builder.addQueryParam(name, queryValue, encoded); } } //QueryName static final class QueryName<T> extends ParameterHandler<T> { ... @Override void apply(RequestBuilder builder, @Nullable T value) throws IOException { if (value == null) return; //这里的value则是参数值,这里将参数值放在了name的位置上,value则为null builder.addQueryParam(nameConverter.convert(value), null, encoded); } }
Url
Url的作用就是设置一个Url地址。这样我们可以不用在请求方法中来设置url,而是通过参数的形式来动态的设置url@GET Call<String> login(@Url String url, String name, String pwd);
三、扩展
1、理解callAdapter和Converter
这里有两个地方需要单独拿出来说明,CallAdapter和Converter到底是做什么的。Retrofit的牛逼之处不仅仅只是上面讲的那些可以通过接口和注解的方式来处理网络请求。应该说CallAdapter和Converter才让Retrofit更加的完美。如果是仅仅是通过注解的话这并没有什么稀奇的地方,反而还会因为反射对性能有一定的影响,当然了Retrofit还是做了缓存的,仅仅是在第一次使用的话才会有点消耗性能。
CallAdapter的作用可以将Call请求转换为另外一种请求方式。Retrofit内置的默认请求Call是OkHttpCall,如果我们只是使用Retrofit+OkHttp的话,这个CallAdapter其实没有什么作用,但是如果需要再配合其他的一些框架的话这个CallAdapter的作用就非常大了。在本文开头说过目前Android端最火的网络请求框架是Retrofit+OkHttp+RxJava这种组合,CallAdapter的作用在这里就体现了出来,可以将call转换为一个Observable对象了,这样我们就可以使用RxJava的那一套api风格来编写我们的代码来处理网络请求了。是不是感觉非常棒。
当然Retrofit不仅仅只支持RxJava,它还支持其他几种。
如果还需要支持其他几种库的话可以添加对应的依赖进行设置,这里就不细说了。
Converter的作用就比较好理解了,它会将数据转换为需要的类型,比如我们设置的请求体参数会转换为RequestBody,将响应结果转换为我们需要的数据类型。省去了我们自己去编写解析数据的代码。所以Retrofit帮我们省去了一堆枯燥的代码编写。
2、配置OkHttp
其实很多时候我们还需要配置一些额外的东西。我们在单独使用OkHttp的时候也不会简单的用Builder来创建出OkHttpClient对象就完了,必定会根据不用的需求来设置一堆东西出来。一个特别常见的情况就是如果我们和服务器通信使用的是https的话该怎么配置?很多时候服务器使用的是自定义证书,为了保证安全我们就需要我们在APP端做针对于证书做一些校验了。使用OkHttp当然可以处理了,但是Retrofit却不能处理,本质上Retrofit的网络请求默认情况下都是交给了OkHttp来处理的,所以针对这种情况我们还是需要配置OkHttp才行。
在构造Retrofit的时候我们可以调用Builder的client()来设置一个自定义的OkHttpClient对象,这样就满足了上面的要求了。,其实client()方法还是调用了callAdapter()方法来设置了callAdapter对象,本身OkHttpClient就实现了Call.Factory这个接口。
了解了上面这些内容后,我们对Retrofit的使用也会更加的得心应手。