概述
Retrofit是当下最热门的Android网络请求库,准确的来说Retrofit是一个RESTful的HTTP网络请求框架的封装,因为它内部网络请求的工作,本质上是通过OkHttp完成,而Retrofit仅负责网络请求接口的封装。具体是客户端通过Retrofit请求网络,实际上是通过Retrofit的接口层封装了请求参数,header,url等信息,之后由OkHttp完成后续的请求工作。然后在服务端返回数据之后,OkHttp将原始的结果传递给Retrofit,Retrofit根据客户端的相关配置,将结果进行解析回调给客户端
基本使用
一、Get请求
Retrofit在使用的过程中需要定义接口层,接口层中的每个方法标识一个独立的请求,如下定义接口层ApiService
public interface ApiService {
@GET("users/list")
Call> getUsers();
}
@Get注解作用于方法之上,表明这是一个Get请求,@Get注解的value值与Retrofit中配置的baseUrl组成完整的请求url,List
// builder模式构建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://xxx/")
.addConverterFactory(GsonConverterFactory.create())
.build();
// 创建接口层的代理对象,内部通过动态代理创建了ApiService的代理对象
ApiService api = retrofit.create(ApiService.class);
// 执行异步请求
api.getUsers().enqueue(new Callback>() {
@Override
public void onResponse(Call> call, Response> response) {
// 处理结果
}
@Override
public void onFailure(Call> call, Throwable t) {
// 处理异常
}
});
我们知道原始的请求响应结果其实是ResponseBody,那么我们这里声明了响应数据类型为List
二、动态url访问@PATH
比如我现在要访问类似这样的url https://api.example.com/user/1
那么接口层定义如下:
public interface ApiService {
@GET("user/{id}")
Call getUser(@Path("id") String id);
}
@Get注解中使用了{id}作为占位符,实际运行会通过方法中@Path(“id”)注解标注的参数进行替换,访问案例如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://xxx/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService api = retrofit.create(ApiService.class);
api.getUser("1").enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
三、带有查询参数设置@Query的Get请求
例如我们要访问这样的url https://api.example.com/users?username=zhangsan
接口层定义如下:
public interface ApiService {
@GET("users")
Call getUserByUserName(@Query("username") String username);
}
通过@Get表明了请求方式,通过@Query标注了请求的参数名,同样的也适用与Post请求,只需要将@Get注解替换为@Post即可,访问案例如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://xxx/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService api = retrofit.create(ApiService.class);
api.getUserByUserName("zhangsan").enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
四、POST请求体的方式向服务器传入json字符串@Body
在平时与服务端的接口交互中,我们一般会将请求参数直接封装成一个json字符串传递给服务端,通过Retrofit如何实现呢?接口层定义如下:
public interface ApiService {
@POST("users/add")
Call> addUser(@Body User user);
}
可以看到我们通过@Body注解标注参数对象即可,而后Retrofit内部将User对象转换了json字符串传递给服务端,访问案例如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://xxx/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService api = retrofit.create(ApiService.class);
User user = new User("zhangsan",24,"[email protected]")
api.addUser(user).enqueue(new Callback() {
@Override
public void onResponse(Call> call, Response response) {
}
@Override
public void onFailure(Call> call, Throwable t) {
}
});
Retrofit默认只能接受@Body的RequestBody类型,那么我们这里传入了一个User对象,那么Retrofit内部是如何转换的呢?其实还是利用上面说的转换器GsonConverterFactory。
五、表单的方式传递键值对@FormUrlEncoded
Web开发中表单提交很常见,那么通过Retrofit如何实现一个表单提交的请求呢?接口层定义如下:
public interface ApiService {
@POST("login")
@FormUrlEncoded
Call> login(@Field("username") String username,@Field("password") String password);
}
通过@POST指明url,添加@FormUrlEncoded,然后通过@Field添加参数即可。访问案例如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://xxx/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService api = retrofit.create(ApiService.class);
api.login("zhangsan","123456").enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
// 处理结果
}
@Override
public void onFailure(Call call, Throwable t) {
// 处理异常
}
});
六、单文件@Multipart
通过Retrofit实现单文件上传,接口层定义如下:
public interface ApiService {
@Multipart
@POST("register")
Call register(@Part MultipartBody.Part header, @Part("username") RequestBody username,@Part("password") RequestBody password);
}
这里@MultiPart的意思就是允许多个@Part了,我们这里使用了3个@Part,第一个我们准备上传个文件,使用了MultipartBody.Part类型,其余两个均为简单的键值对。访问案例如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://xxx/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService api = retrofit.create(ApiService.class);
// 创建需要上传服务端的用户头像文件
File file = new File(Environment.getExternalStorageDirectory(), "photo.png");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
MultipartBody.Part photo = MultipartBody.Part.createFormData("photo", file.getName(), photoRequestBody);
ReqeustBody usernameRequestBody = RequestBody.create(null, "zhangsan");
ReqeustBody passwordRequestBody = RequestBody.create(null, "123456");
api.registerUser(photo, usernameRequestBody,passwordRequestBody).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
// 处理结果
}
@Override
public void onFailure(Call call, Throwable t) {
// 处理异常
}
});
七、多文件上传@PartMap
Retrofit实现多文件上传,接口层定义如下:
public interface ApiService {
@Multipart
@POST("register")
Call register(@PartMap Map params, @Part("password") RequestBody password);
}
这里使用了一个新的注解@PartMap,这个注解用于标识一个Map,Map的key为String类型,代表上传的键值对的key(与服务器接受的key对应),value即为RequestBody,有点类似@Part的封装版本。访问案例如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://xxx")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService api = retrofit.create(ApiService.class);
File file = new File(Environment.getExternalStorageDirectory(), "photo.png");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png", file);
Map params = new HashMap<>();
params.put("photo\"; filename=\"photo.png", photoRequestBody);
params.put("username", RequestBody.create(null, "zhangsan"));
ReqeustBody passwordRequestBody = RequestBody.create(null, "123456");
api.registerUser(params,passwordRequestBody).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
// 处理结果
}
@Override
public void onFailure(Call call, Throwable t) {
// 处理异常
}
});
八、请求头Headers的添加
有时候,我们需要为某个请求单独添加请求头,那么如何操作呢?接口层定义如下:
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call> widgetList();
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call getUser(@Path("username") String username);
注意,@Headers添加的请求头不会相互覆盖,具有相同名称的所有标头将包含在请求中。
必须为@Header提供相应的参数,如果该值为null,则标题将被省略。否则,将对值调用toString,并使用结果
@GET("user")
Call getUser(@Header("Authorization") String authorization)
@GET("user")
Call getUser(@HeaderMap Map headers)
我们还可以使用OkHttp拦截器指定需要添加到每个请求的请求头
九、同步和异步请求
Call实例可以同步或者异步执行,每个Call实例只能使用一次,但是调用Call#clone()将创建一个可以使用的新实例。 在Android上,回调将在主线程上执行。在JVM上,回调将在执行HTTP请求的同一线程上发生。
十、Retrofit框架引入方式(Gradle)
implementation 'com.squareup.retrofit2:retrofit:(insert latest version)'
十一、Retrofit混淆配置
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and # EnclosingMethod is required to use InnerClasses. -keepattributes Signature, InnerClasses, EnclosingMethod # Retrofit does reflection on method and parameter annotations. -keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations # Retain service method parameters when optimizing. -keepclassmembers,allowshrinking,allowobfuscation interface * { @retrofit2.http.*; } # Ignore annotation used for build tooling. -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement # Ignore JSR 305 annotations for embedding nullability information. -dontwarn javax.annotation.** # Guarded by a NoClassDefFoundError try/catch and only used when on the classpath. -dontwarn kotlin.Unit # Top-level functions that can only be used by Kotlin. -dontwarn retrofit2.KotlinExtensions -dontwarn retrofit2.KotlinExtensions$* # With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy # and replaces all potential values with null. Explicitly keeping the interfaces prevents this. -if interface * { @retrofit2.http.* ; } -keep,allowobfuscation interface <1>
十二、下载文件
Retrofit实现文件下载,接口层定义如下:
public interface ApiService {
@GET("download")
Call download();
}
这里直接返回类型定义为ResponseBody,然后直接拿到其byteStream(),读流即可。访问案例如下:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://xxx")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService api = retrofit.create(ApiService.class);
api.download().enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
InputStream in = response.body().byteStream();
// 进行保存文件操作 这里是主线程!!!
}
@Override
public void onFailure(Call call, Throwable t) {
// 处理异常
}
});
可以看到,我们在回调onResponse中拿到了文件流,然后就可以进行写文件的操作了,但是注意这里是主线程,文件读写操作属于耗时操作,我们还得切到子线程操作,所以文件的下载操作直接使用OkHttp就行了。
进阶配置
一、配置callFactory
查看Retrofit构建过程得知,如果没有配置callFactory,那么Retrofit内部会创建默认的OkHttpClient对象作为callFactory
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
// callFactory为空,则创建默认的OkHttpClient
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// ...省略代码
}
如果需要对OkHttpClient进行详细的设置,我们需要构建OkHttpClient对象,然后通过Retrofit.Builder#callFactoy(okhttp3.Call.Factory factory)方法Retrofit.Builder#client(OkHttpClient client)
或者传入。
二、配置CallAdapterFactory
默认情况下,Retrofit只能支持Call
三、配置ConverterFactory
默认情况下,Retrofit只能将HTTP正文反序列化为OkHttp的ResponseBody类型,并且只能接受@Body的RequestBody类型,可以添加转换器以支持其他类型。六个成员模块采用了流行的序列化库,以方便我们使用。
Gson: com.squareup.retrofit2:converter-gson
Jackson: com.squareup.retrofit2:converter-jackson
Moshi: com.squareup.retrofit2:converter-moshi
Protobuf: com.squareup.retrofit2:converter-protobuf
Wire: com.squareup.retrofit2:converter-wire
Simple XML: com.squareup.retrofit2:converter-simplexml
JAXB: com.squareup.retrofit2:converter-jaxb
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
这是一个使用GsonConverterFactory类生成GitHubService接口的实现的示例,该接口使用Gson进行反序列化。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https:xxx")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
四、自定义ConverterFactory
如果需要与使用Retrofit不支持的现成格式(例如YAML,txt,自定义格式)的API进行通信,或者希望使用其他库来实现现有格式,则可以轻松创建 您自己的转换器。 创建一个扩展Converter.Factory类的类,并在构建适配器时传入实例。