为什么要用Retrofit
Retrofit采用了很多的设计模式,使其拥有很好的扩展性,可以和RxJava、Gson、OkHttp这些主流的库进行无缝对接,非常方便。
初识Retrofit
Retrofit是一个网络请求框架的封装,应用程序通过Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数,之后由OkHttp完成后续的请求操作,在服务端返回数据之后,OKHttp将原始的结果交给Retrofit,Retrofit根据用户的需求对结果进行解析。
用一张图来表示:
Retrofit的简单使用
(1)在build.gradle中添加Retrofit库依赖
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
在Manifest中添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>
(2)创建一个用于接收服务器返回数据的实体
public class MyResponse {
String name;
int age;
String address;
}
(3)创建一个用于描述网络请求的接口
public interface MyInterface {
@GET(".../...")
Call<List<MyResponse>> getCall();
}
Retrofit将 Http请求抽象成Java接口:采用注解描述和配置网络请求参数。
【1】用动态代理动态将该接口的注解“翻译”成一个http请求,最后再执行 http请求
【2】注:接口中的每个方法的参数都需要使用注解标注,否则会报错
(4)创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http:/xx/xx/xx/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
- a. 关于数据解析器(Converter)
Retrofit支持多种数据解析方式,使用时需要在Gradle添加依赖
数据解析器 | Gradle依赖(版本根据需要修改) |
---|---|
Gson | com.squareup.retrofit2:converter-gson:2.0.2 |
Jackson | com.squareup.retrofit2:converter-jackson:2.0.2 |
Simple XML | com.squareup.retrofit2:converter-simplexml:2.0.2 |
Protobuf | com.squareup.retrofit2:converter-protobuf:2.0.2 |
Moshi | com.squareup.retrofit2:converter-moshi:2.0.2 |
Wire | com.squareup.retrofit2:converter-wire:2.0.2 |
Scalars | com.squareup.retrofit2:converter-scalars:2.0.2 |
- b. 关于网络请求适配器(CallAdapter)
Retrofit支持多种网络请求适配器方式:guava、Java8和rxjava
使用时如使用的是 Android 默认的 CallAdapter,则不需要添加网络请求适配器的依赖,否则则需要按照需求进行添加 Retrofit 提供的 CallAdapter
虽然此处栗子中添加了网络请求适配器,但是下文举例中仍然使用的是Android默认的CallAdapter
使用时需要在Gradle添加依赖:
网络请求适配器 | Gradle依赖 |
---|---|
guava | com.squareup.retrofit2:adapter-guava:2.0.2 |
Java8 | com.squareup.retrofit2:adapter-java8:2.0.2 |
rxjava | com.squareup.retrofit2:adapter-rxjava:2.0.2 |
(5)创建网络请求接口实例
MyInterface myInterface = retrofit.create(MyInterface.class);//传入字节码创建接口实例
Call<List<MyResponse>> call = myInterface.getCall();//发送请求的封装(基于OkHttp中的Call)
(6)发送网络请求(同步&&异步)
/**
* 发送网络请求(异步)
*/
call.enqueue(new Callback<List<MyResponse>>() {
@Override
public void onResponse(Call<List<MyResponse>> call, Response<List<MyResponse>> response) {
}
@Override
public void onFailure(Call<List<MyResponse>> call, Throwable t) {
}
});
/**
* 发送网络请求(同步)
*/
try {
Response<List<MyResponse>> response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
(7)处理返回数据,此处的callback已经切换到UI线程
call.enqueue(new Callback<List<MyResponse>>() {
@Override
public void onResponse(Call<List<MyResponse>> call, Response<List<MyResponse>> response) {
//请求处理,输出结果
System.out.println(response.body());
}
@Override
public void onFailure(Call<List<MyResponse>> call, Throwable t) {
//请求处理,输出结果
System.out.println("失败");
}
});
/**
* 发送网络请求(同步)
*/
try {
Response<List<MyResponse>> response = call.execute();
//请求处理,输出结果
System.out.println(response.body());
} catch (IOException e) {
e.printStackTrace();
}
Retrofit中非常重要的注解
Retrofit的注解分为三大类,分别是HTTP请求方法注解、标记类注解和参数类注解
1)HTTP请求方法注解有8种:GET、POST、PUT、DELETE、HEAD、PATCH、OPTIONS和HTTP,前7种分别对应HTTP的请求方法,HTTP则可以替换以上7种,也可以扩展请求方法
2)标记类注解有3种:FormUrlEncoded、Multipart、Streaming。其中,Streaming代表响应的数据以流的形式返回,如果不使用它,则默认会把全部数据加载到内存,所以下载大文件时需要加上这个注解。
3)参数类注解有10多种:Header、Headers、HeaderMap、Body、Path、Field、FieldMap、Part、PartMap、Query和QueryMap等
-
常用HTTP请求方法注解 —— GET,及参数类注解
(1)动态配置URL地址:@Path
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
在GET注解中包含了{user},它对应@Path注解中的“user”,而用来替换{user}的则正是需要传入的“String user”的值。
(2)动态指定查询条件:@Query,参数拼在url后面
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
}
(3)动态指定查询条件组:@QueryMap,参数拼在url后面
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
}
在网络请求中一般我们需要传入很多查询参数,用@Query会比较麻烦(你想想要传很多参数,写很多个@Query),这时我们可以采用@QueryMap,将所有的参数集成在一个Map中统一传递。
-
常用HTTP请求方法注解 —— POST,及标记类和参数类注解
(1)传输数据类型为键值对:@Field,参数放在请求体中
public interface GitHubService {
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
}
FormUrlEncoded这一标记类注解来标明这是一个表单请求,然后在方法参数中使用@Field注解来表示所对应的String类参数的键,从而组成一组键值对进行传递
(2)传输数据类型JSON字符串:@Body
public interface GitHubService {
@POST("users/new")
Call<User> createUser(@Body User user);
}
用@Body这个注解标识参数对象,Retrofit会将User对象转换为字符串
(3)单个文件上传:@Part
public interface MyInterface {
@Multipart
@POST("user/photo")
Call<User> updateUser(@Part MultipartBody.Part photo, @Part("desc") RequestBody desc, @Part("photoname") RequestBody photoName);
}
Multipart注解表示允许多个@Part,第一个参数为要上传的图片,第二个和第三个用来传递简单的键值对(键为引号内字符串,值为用来生成RequestBody的数据),注意MultipartBody.Part和RequestBody这两种数据类型的注解方式差异
举例说明一下:
File file = new File(Environment.getExternalStorageDirectory(),"aaa.png");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"),file);
MultipartBody.Part photo = MultipartBody.Part.createFormData("photo","aaa.png",photoRequestBody);
MyInterface myInterface = retrofit.create(MyInterface.class);
//设置这个part数据的content-type为null,则此部分数据的请求头无content-type类型
RequestBody desBody = RequestBody.create(null,"photo");
RequestBody photonameBody = RequestBody.create(null,"aaa");
Call<User> call = myInterface.updateUser(photo,desBody,photonameBody);
(4)多个文件上传:@PartMap
@Multipart
@POST("user/photo")
Call<User> updateUser(@PartMap Map<String,RequestBody> photos, @Part("desc") RequestBody desc);
多文件上传使用了Map封装了文件,其他与单个文件上传类似
-
消息报头——Header
(1)静态添加消息报头(单个或者多个),请求头不会相互覆盖,具有相同名的请求头都会被包含到请求中
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
(2)动态添加请求头(单个或者多个)
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
@GET("user")
Call<User> getUser(@HeaderMap Map<String, String> headers)
(3)1和2添加我们可以发现都是在单独的接口中进行的,如果我们需要在每个请求中添加相同的请求头呢,这时候需要使用OkHttp的拦截器
举个例子,定义一个拦截器如下:
public class CommonHeaderIntercepter implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
HashMap<String,String> map = new HashMap<>();
map.put("hxid", SpUtils.getString(Constant.HX_ID));
map.put("identity",SpUtils.getString(Constant.IDENTITY));
Gson gson = new Gson();
String cookie = gson.toJson(map);
Request request = original.newBuilder()
.header("User-Agent", "qiyuan")
.header("Cookie", cookie)
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
}
使用自定义的拦截器
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new CommonHeaderIntercepter())
.build();
client再设置给Retrofit,每个请求统一添加了这里设置的头部信息,当然这里可以添加各种各样的多个拦截器
关于Retrofit的使用就先说到这里,后续还会有关于retrofit的更深入的介绍
另外这里有一篇注解比较全面的介绍
网友评论