Retrofit简介
Rrotrofit是一个RESTful设计风格的对HTTP框架的封装。Retrofit网络请求本质上是Okhttp完成的,Retrofit仅负责网络请求接口的封装。Retrofit接口层封装了请求参数、Header、Url等信息,后续的请求工作由OkHttp来完成。OkHttp将返回结果交给Retrofit,Retrofit根据用户的需求对结果进行解析。
public interface GetRequest_Interface {
@GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json")
Call<Translation> getCall();
// @GET注解的作用:采用Get方法发送网络请求
// getCall() = 接收网络请求结果数据的方法
// 其中返回类型为Call<*>,*是接收数据的类(即上面定义的Translation类)
// 如果想直接获得Responsebody中的内容,可以定义网络请求返回值为Call<ResponseBody>
}
Retrofit与其他框架的对比
Retrofit中URL组成
Url由 BaseUrl 和 path组成。BaseUrl在 Rotrofit 实例化时进行设置。path在接口描述中设置。
Url = “http://host:port/aaa/pathname”;
BaseUrl = “http://host:port/aaa/”;
path = “pathname”;
Retrofit注解类型
1.注解---请求方法
2.注解---标记类
3.注解---请求参数
Retrofit注解详解
1.请求方法---注解
1.1@HTTP注解的作用
@HTTP注解可以替换@GET、@POST、@PUT、@DELETE、@HEAD注解,及更多功能扩展。
public interface GetRequest_Interface {
/**
* method:网络请求的方法(区分大小写)
* path:网络请求地址路径
* hasBody:是否有请求体
*/
@HTTP(method = "GET", path = "blog/{id}", hasBody = false)
Call<ResponseBody> getCall(@Path("id") int id);
// {id} 表示是一个变量
// method的值,retrofit不会做检查处理,所以要自行保证准确
}
2.标记类---注解
2.1@FormUrlEncode
@FormUrlEncode表示请求体是一个Form表单,每个键值对需要用@Filed来注解,随后的对象需要提供键值对的值。它不能用于Get请求,因为Get请求中没有请求体。
public interface GetRequest_Interface {
/**
* @FormUrlEncoded表明是一个表单格式的请求(application/x-www-form-urlencoded)
* Field("username") 表示将后面的String name 中name的取值作为 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded(
@Field("username") String name,
@Field("age") int age
);
}
// 具体使用
GetRequest_Interface service = retrofit.create(GetRequest_Interface.class);
Call<ResponseBody> call1 = service.testFormUrlEncoded("Carson", 24);
2.2@Multipart
@Multipart表示请求体是一个文件上传的Form表单,每个键值对需要用@Part来注解,随后的对象需要提供键值对的值。
public interface GetRequest_Interface {
/**
* @Part后面支持三种类型:RequestBody、okhttp3.MultipartBody.Part、任意类型;
* 除了okhttp3.MultipartBody.Part类型以外,其它类型都必须带上表单字段,
* okhttp3.MultipartBody.Part中已经包含了表单字段的信息。
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload(
@Part("name") RequestBody name,
@Part("age") RequestBody age,
@Part MultipartBody.Part file
);
}
//具体使用
GetRequest_Interface mService = retrofit.create(GetRequest_Interface.class);
RequestBody name = RequestBody.create(textType, "Carson");
RequestBody age = RequestBody.create(textType, "24");
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
Call<ResponseBody> call3 = mService.testFileUpload(name, age, filePart);
3.请求参数---注解
3.1 @Headers
@Headers 用于在描述接口中静态添加请求头
public interface GetRequest_Interface {
@Headers(
"Authorization: authorization”,
"Accept: application/vnd.yourapi.v1.full+json",
"User-Agent: Your-App-Name"
)
@GET("user")
Call<User> getUser()
}
3.2 @Header
@Header 用于动态添加请求头,及需要添加的请求头,会作为参数传入
public interface GetRequest_Interface {
@GET("user")
Call<User> getUser(
@Header("Authorization") String authorization
)
}
3.3 @Body
@Body用于非表单请求体,并以Post方式提交自定义的数据类型。但是如果提交的是一个Map集合,那么@Body的作用就相当于@Field,不过此时的map需要被FormBody.Builder处理成符合Okhttp的表单。
public interface GetRequest_Interface {
@FormUrlEncoded
@POST("book/reviews")
Call<String> addReviews(@Body Reviews reviews);
}
public class Reviews {
public String book;
public String title;
public String content;
public String rating;
}
//具体使用
Reviews reviews = new Reviews();
reviews.setBook(“百科全书”);
reviews.setTitle(“标题”);
reviews.setContent(“描述内容”);
reviews.setRating(“hello!”);
Call<ResponseBody> call = service.addReviews(reviews);
//Map处理过程
FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");
3.4 @Field & @FieldMap
@Field和FieldMap与标记类@FormUrlEncode配合使用 ,作为发送Post请求时提交请求参数的表单字段。
public interface GetRequest_Interface {
/**
* 表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* Field("username")表示将后面的String name中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);
3.5 @Part & @PartMap
@Part和@PartMap与标记类@Multipart配合使用 ,作为发送Post请求时提交请求参数的表单字段。适用于文件上传的场景。
public interface GetRequest_Interface {
/**
* @Part后面支持三种类型:RequestBody、 okhttp3.MultipartBody.Part、任意类型:
* 除了okhttp3.MultipartBody.Part以外,其它类型都必须带上表单字段,
* okhttp3.MultipartBody.Part中已经包含了表单字段的信息。
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload1(
@Part("name") RequestBody name,
@Part("age") RequestBody age,
@Part MultipartBody.Part file
);
/**
* PartMap 注解支持一个Map作为参数,支持RequestBody类型,
* 如果有其它的类型,会被retrofit2.Converter转换,如后面会介绍的,
* 使用com.google.gson.Gson的retrofit2.converter.gson.GsonRequestBodyConverter
* 所以MultipartBody.Part就不适用了,所以文件只能用@Part MultipartBody.Part
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload2(
@PartMap Map<String, RequestBody> args,
@Part MultipartBody.Part file
);
}
// 具体使用
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"), "这里是模拟文件的内容");
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
// @Part
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);
Call<ResponseBody> call4 = service.testFileUpload2(fileUpload2Args, filePart);
// 单独处理文件
ResponseBodyPrinter.printResponseBody(call4);
3.6 @Query & @QueryMap
@Query和@QueryMap用于@Get方法的查询参数 (Query = Url中?后面的 key-value)
public interface GetRequest_Interface {
@GET("book/search")
Call<ResponseBody> cate1(
@Query("username") String username,
@Query(“age”) int age
);
@GET("book/search")
Call<ResponseBody> cate2(
@QueryMap Map<String, Object> map
);
Call<ResponseBody> cate3(
@Query("list") List<String> strList,
);
}
//具体使用
// @Field
Call<ResponseBody> call1 = service.cate1("Carson");
// @FieldMap
// 实现的效果与上面相同,但要传入Map
Map<String, Object> map = new HashMap<>();
map.put("username", "Carson");
map.put("age", 24);
Call<ResponseBody> call2 = service.cate2(map);
3.7 @Path
@Path 用作Url地址的缺省值 。Post、Get、Put、Delete中都可以使用
public interface GetRequest_Interface {
@GET("users/{user}/repos")
Call<ResponseBody> getBlog(@Path("user") String user );
// 访问的API是:https://api.github.com/users/{user}/repos
// 在发起请求时, {user} 会被替换为方法的第一个参数 user(被@Path注解作用)
}
//具体使用
Call<ResponseBody> call = service.getBlog(“carson”);
//则描述的接口为:“users/carson/repos”。
3.8 @Url
@Url 用于在网络请求的时候,直接传入一个请求的Url变量,用于Url设置。
public interface GetRequest_Interface {
@GET
Call<ResponseBody> testUrlAndQuery(
@Url String url,
@Query("showAll") boolean showAll
);
// 当@HTTP、@POST、@GET中设置了path时,@GET传入的URL就可以省略
// 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供
}
//具体使用
Url url = “http://www.baidu.com/abc”
Call<ResponseBody> call = service.testUrlAndQuery(url, true);
//则描述的接口为:“http://www.baidu.com/abc?showAll=true”。
自定义Interceptor拦截器
创建一个自定义的Interceptor拦截器,然后在创建Retrofit实例的地方通过addInterceptor()进行添加,通过它我们可以实现一些拦截的操作,比如下面:我们想要拦截每一次请求,添加一个公共的请求参数。
//自定义拦截器——添加公共参数
public class CustomInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl httpUrl = request.url().newBuilder()
.addQueryParameter("token", "tokenValue")
.build();
request = request.newBuilder().url(httpUrl).build();
return chain.proceed(request);
}
}
//自定义拦截器—添加 header
//添加header参数,Request提供了两个方法,一个是header(key, value),
//另一个是.addHeader(key, value),两者的区别是,header()如果有重名的将会覆盖,
//而addHeader()允许相同key值的header存在
public class RequestInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("User-Agent", "Your-App-Name")
.header("Accept", "application/vnd.yourapi.v1.full+json")
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
}
//添加拦截器Interceptor
private static OkHttpClient getNewClient(){
return new OkHttpClient.Builder()
.addInterceptor(new CustomInterceptor())
.addInterceptor(new RequestInterceptor())
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
}
Retrofit官方提供了一个方便查看日志的Interceptor拦截器,方便的控制打印信息的类型。HttpLoggingInterceptor提供4种控制打印信息类型的等级,分别是:NONE、BASIC、HEADERS、BODY。
//引入依赖
implement 'com.squareup.okhttp3:logging-interceptor:3.4.1’
//具体使用
private static OkHttpClient getNewClient(){
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
return new OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
}
Retrofit解析器与适配器
1.数据解析器 Converter,Retrofit支持多种解析器
2.适配器 CallAdapter,Retrofit支持多种网络请求适配器
Retrofit中的API
Call<ResponseBody> call = servers.lookBookDetail(“参数”);
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("连接失败");
}
});
1.异步请求
call.enqueue();
2.同步请求
call.execute();
3.取消请求
call.cancel();
4.对数据进行处理
respose.body().show();
Retrofit的使用
1.在app模块的build.gradle文件中引入Retrofit框架。
implement 'com.squareup.retrofit2:retrofit:2.0.2’
implement 'com.squareup.retrofit2:converter-gson:2.0.2'
2.自定义数据接收实体类
3.创建用于描述网络请求的接口
4.创建Retrofit对象
5.创建网络请求接口实例
6.发送网络请求
7.处理返回数据、
//创建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(""http://fanyi.youdao.com/"")
.addConverterFactory(ProtoConverterFactory.create()) // 支持Prototocobuff解析
.addConverterFactory(GsonConverterFactory.create()) // 支持Gson解析
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava
.build();
//创建 网络请求接口 的实例
PostRequest_Interface request = retrofit.create(PostRequest_Interface.class);
//对网络请求进行封装
Call<Translation1> call = request.getCall("I love you”);
//发送网络请求(异步)
call.enqueue(new Callback<Translation1>() {
//请求成功时回调
@Override
public void onResponse(Call<Translation1> call, Response<Translation1> response) {
// 步骤7:处理返回的数据结果:输出翻译的内容
System.out.println(response.body().getTranslateResult().get(0).get(0).getTgt());
}
//请求失败时回调
@Override
public void onFailure(Call<Translation1> call, Throwable throwable) {
System.out.println("请求失败");
System.out.println(throwable.getMessage());
}
});
Retrofit实现文件上传
public interface FileUploadService {
// 上传单个文件
@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(
@Part("description") RequestBody description,
@Part MultipartBody.Part file);
// 上传多个文件
@Multipart
@POST("upload")
Call<ResponseBody> uploadMultipleFiles(
@Part("description") RequestBody description,
@Part MultipartBody.Part file1,
@Part MultipartBody.Part file2);
}
Retrofit上传文件的工具类
public class retrofitFileUtil{
public static final String MULTIPART_FORM_DATA = "multipart/form-data”;
private RequestBody createPartFromString(String descriptionString) {
return RequestBody.create(
MediaType.parse(MULTIPART_FORM_DATA), descriptionString);
}
private MultipartBody.Part prepareFilePart(String partName, Uri fileUri) {
File file = FileUtils.getFile(this, fileUri);
// 为file建立RequestBody实例
RequestBody rf = RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), file);
// MultipartBody.Part借助文件名完成最终的上传
return MultipartBody.Part.createFormData(partName, file.getName(), rf);
}
}
Uri file1Uri = ... // 从文件选择器或者摄像头中获取
Uri file2Uri = …
// 创建上传的service实例
FileUploadService service = ServiceGenerator.createService(FileUploadService.class);
// 创建文件的part (photo, video, ...)
MultipartBody.Part body1 = prepareFilePart("video", file1Uri);
MultipartBody.Part body2 = prepareFilePart("thumbnail", file2Uri);
// 添加其他的part
RequestBody description = createPartFromString("hello, this is description speaking");
// 最后执行异步请求操作
Call<ResponseBody> call = service.uploadMultipleFiles(description, body1, body2);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.v("Upload", "success");
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("Upload error:", t.getMessage());
}
});