Retrofit
是Square公司开发的针对Java和Android的HTTP开源库。
Retrofit
底层默认是使用OKHttp处理HTTP请求的。
Retrofit
的作者有两个:Bob Lee
和JakeWharton
。
Bob Lee
曾是Square的第13名员工, 在2010年从Google
离职加入Square担任Android
应用开发工作,一年后升任CTO
。 2014年05月19日,Bob Lee
从Square离职。
Bob Lee
在Google
工作的时候,开发了鼎鼎有名的轻量级JEE IOC
框架Guice
, 你如果看过Guice
的源码的话,你就会知道, 他对Java中的范型和反射、注解、设计模式运用的炉火纯青。似乎他对这些都情有独钟,而Retrofit
框架也是利用了这些技术, 所以Retrofit
使用起来非常优雅。
JakeWharton
是绝对牛逼的技术大神,项目主要集中在Android
版本兼容,ViewPager
及开发工具上。
JakeWharton
的GitHub地址:https://github.com/JakeWharton
Retrofit
官网:http://square.github.io/retrofit
Retrofit
在GitHub上的网址:https://github.com/square/retrofit
compile 'com.squareup.retrofit2:retrofit:2.2.0'
具体的版本号,请查看Retrofit在GitHub上的网址
Retrofit
没有公开的构造方法。必须使用Retrofit.Builder
进行构造。
示例:
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)//设置超时时间
.readTimeout(10, TimeUnit.SECONDS) //设置读取超时时间
.writeTimeout(10, TimeUnit.SECONDS) //设置写入超时时间
.retryOnConnectionFailure(true) //设置失败后重试
.cache(new Cache(context.getCacheDir(), 10 * 1024 * 1024))//设置缓存目录和缓存大小:10M
.addInterceptor(interceptor)
.authenticator(new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
Log.i(TAG, "authenticate()");
String username = "";
String password = "";
String credential = Credentials.basic(username, password);
return response.request().newBuilder().header("Authorization", credential).build();
}
})
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
我们通常不使用默认的OkHttpClient
对象,而是自己设置一个,用来设置3种超时时间、失败后的重试、打印日志、Cookie
的处理等。
这里使用了HttpLoggingInterceptor
,必须在build.gradle
中加入如下依赖:
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
这里使用了GsonConverterFactory
,必须在build.gradle
中加入如下依赖:
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
retrofit2.Call<T>
类似于okhttp3.Call
,只是,此类支持范型。
@GET
表示GET
请求。
@GET
的源码如下:
package retrofit2.http;
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
String value() default "";
}
从@GET
的定义可以看出,@GET
只能用在方法上并且可以设置一个字符串值参数,参数可省略。
假设,我们有如下的HTTP
接口:
GET /updateApp HTTP/1.1
响应体如下:
{
"businessId": "updateApp",
"statusCode": 0,
"message": "success",
"data": {
"versionName": "1.1.0",
"text": "修改了啥就是不告诉你",
"apkUrl": "http://blog.fpliu.com/xx.apk",
"force": true
}
}
我们根据响应体使用AndroidStudio
的插件GsonFormat
自动生成实体类,如下:
public class UpdateAppBean {
private String businessId;
private int statusCode;
private String message;
private DataBean data;
public String getBusinessId() {
return businessId;
}
public void setBusinessId(String businessId) {
this.businessId = businessId;
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public DataBean getData() {
return data;
}
public void setData(DataBean data) {
this.data = data;
}
public static class DataBean {
private String versionName;
private String text;
private String apkUrl;
private boolean force;
public String getVersionName() {
return versionName;
}
public void setVersionName(String versionName) {
this.versionName = versionName;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getApkUrl() {
return apkUrl;
}
public void setApkUrl(String apkUrl) {
this.apkUrl = apkUrl;
}
public boolean isForce() {
return force;
}
public void setForce(boolean force) {
this.force = force;
}
}
}
定义一个Java
接口与HTTP
接口对应,如下:
public interface HttpAPI {
@GET("/updateApp")
retrofit2.Call<UpdateAppBean> updateApp();
}
要注意的是方法的返回值必须是retrofit2.Call<T>
,不要与okhttp3.Call
混淆了。
@GET("/updateApp")
表示这是一个GET
请求的URL
的path
是updateApp
。
接下来就是请求了,如下:
RetrofitRequest.getRetrofit("http://192.168.1.101").create(HttpAPI.class).updateApp().enqueue(new retrofit2.Callback<UpdateAppBean>() {
@Override
public void onResponse(retrofit2.Call<UpdateAppBean> call, retrofit2.Response<UpdateAppBean> response) {
UpdateAppBean updateAppBean = response.body();
//TODO
}
@Override
public void onFailure(retrofit2.Call<UpdateAppBean> call, Throwable t) {
Log.e(TAG, "onFailure()", t);
}
});
你会发现,使用Retrofit
竟然是如此的优雅,他将HTTP
接口与Java
接口对应起来了。 这主要是使用了JDK1.4
中加入的动态代理(Proxy
、InvocationHandler
) 和JDK1.5
中加入的注解(Annotation
)这两个特性实现的。JDK1.4
中加入的动态代理只能代理接口(interface
),不能代理类(class
), 所以,我们每一个HTTP API
必须对应一个Java interface
。 这个HTTP API
的其他内容通过接口方法的注解获得。
这些分别表示对应的HTTP
请求方法,与@GET
用法完全一样。
@GET
等注解有一个参数,实际上,这个参数可以使用默认的,然后动态的传入,就是通过此注解实现的。
@Url
的源码如下:
package retrofit2.http;
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Url {
}
从@Url
的定义可以看出,@Url
只能用在方法的参数上,你可以这么理解他,他就是一个标志, 说明这个参数是请求的URL
。
示例:
public interface HttpAPI {
@GET()
retrofit2.Call<UpdateAppResultBean> updateApp(@Url String url);
}
@Path
的源码如下:
package retrofit2.http;
@Documented
@Retention(RUNTIME)
@Target(PARAMETER)
public @interface Path {
String value();
boolean encoded() default false;
}
从@Path
的定义可以看出,@Path
只能用在方法的参数上,而且value
参数必须要设置, 这个意思就是说,指定的参数是Path的value值。
示例:
public interface HttpAPI {
@GET("/user/{id}")
retrofit2.Call<UpdateAppResultBean> updateApp(@Path("id") String id);
}
注意URL
中的变量用{}
扩起来。
@Query
的用法与@Path
差不多,只是他不需要在URL
中设置变量了。@Query
是传统的使用方式,而@Path
用来实现现在非常流行的Restful API
。
举例:GET /user/1
和GET /user?id=1
这就是两者的不同。
示例:
public interface HttpAPI {
@GET("/user")
retrofit2.Call<UpdateAppResultBean> updateApp(@Query("id") String id);
}
@Body
的源码如下:
package retrofit2.http;
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Body {
}
从@Body
的定义可以看出,@Body
只能用在方法的参数上,你可以这么理解他,他就是一个标志, 说明这个参数是请求体。而且被他标志的参数必须是自定义Java
类、或者是RequestBody
类。 如果是自定义Java
类,是通过new Retrofit.Builder().addConverterFactory()
这是的转换器转换成对应的格式。
示例:
public interface HttpAPI {
@POST("/login")
retrofit2.Call<LoginBean> login(@Body User user);
@POST("/login")
retrofit2.Call<LoginBean> login(@Body RequestBody requestBody);
}
也就是说,@Body
能够处理实体类,让转换器自动转换为相应的数据结构,它也允许我们自己构造RequestBody
,这样的灵活性大大增加。
下面是构造RequestBody
的示例:
RequestBody body = RequestBody.create(MediaType.parse("application/json;charset=UTF-8"), "xxxxx");
@FormUrlEncoded
的源码如下:
package retrofit2.http;
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface FormUrlEncoded {
}
从@FormUrlEncoded
的定义可以看出,@FormUrlEncoded
只能用在方法上,你可以这么理解他,他就是一个标志, 说明这个请求的请求体是Form
。
@Field
的源码如下:
package retrofit2.http;
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {
String value();
boolean encoded() default false;
}
从@Field
的定义可以看出,@Field
只能用在方法的参数上。
示例:
public interface HttpAPI {
@POST("/login")
@FormUrlEncoded
retrofit2.Call<LoginResultBean> login(@Field("username") String username, @Field("password") String password);
}
@Multipart
的源码如下:
package retrofit2.http;
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface Multipart {
}
从@Multipart
的定义可以看出,@Multipart
只能用在方法上,你可以这么理解他,他就是一个标志, 说明这个请求的请求体是Multipart
。
@Part
的源码如下:
package retrofit2.http;
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Part {
/**
* The name of the part. Required for all parameter types except
* {@link okhttp3.MultipartBody.Part}.
*/
String value() default "";
/** The {@code Content-Transfer-Encoding} of this part. */
String encoding() default "binary";
}
从@Part
的定义可以看出,@Part
只能用在方法的参数上。
示例:
public interface HttpAPI {
@POST("/upload")
@Multipart
retrofit2.Call<UploadResultBean> upload(@Part("file") MultipartBody.Part filePart, @Part("meta") MultipartBody.Part metaPart);
}
@Header
的源码如下:
package retrofit2.http;
@Documented
@Retention(RUNTIME)
@Target(PARAMETER)
public @interface Header {
String value();
}
从@Header
的定义可以看出,@Header
只能用在方法的参数上,说明这个参数是指定的请求头的值。
示例:
public interface HttpAPI {
@GET("/")
retrofit2.Call<IndexResultBean> index(@Header("Authorization") String authorization);
}
1、从上面的使用来看,Retrofit
大大简化了OKHttp的使用, 并且更优雅了,不过,他仍然有一个死结,就是回掉函数运行在工作线程中,而我们在回掉函数中通常会更新UI
, 所以,我们通常会配合上RxJava进行使用,这样就完美了。
2、Retrofit
是Restful API
友好的。对于POST
一些简单的JSON
数据, 还需要创建一个Java
类,然后通过@Body
注解使用,这有点小题大做了。你想想,为了一两个属性而去造一个类, 最关键的是,这个类的存在就是为了给Retrofit
传递数据的,如果,这样的接口有很多,我们的开发效率必然很低。因为对于这种简单的JSON
数据,我们使用的时候,直接传入基本数据类型的字段,让程序自己组装出符合JSON
的数据即可了。这样使用起来才方便嘛。
关于Retrofit
支持简单JSON
,我已经发起了Pull Request
,请看https://github.com/square/retrofit/pull/2346/commits/28f71b68587ea53d7065fc71165f7991b86dfc6a
我修改后的源码在:https://github.com/leleliu008/retrofit
使用方法:
public interface HttpAPI {
@POST("/login")
@SimpleJSON
retrofit2.Call<LoginBean> login(@SimpleJSONField("username") String username, @SimpleJSONField("password") String password);
}
这样,不用为了发送username
和password
而创建一个类了。