Retrofit的使用解析

为什么要用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中添加网络权限


(2)创建一个用于接收服务器返回数据的实体

public class MyResponse {
    String name;
    int age;
    String address;
}

(3)创建一个用于描述网络请求的接口

public interface MyInterface {
    
    @GET(".../...")
    Call> 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> call = myInterface.getCall();//发送请求的封装(基于OkHttp中的Call)

(6)发送网络请求(同步&&异步)

/**
* 发送网络请求(异步)
*/
call.enqueue(new Callback>() {
     @Override
     public void onResponse(Call> call, Response> response) {

      }

     @Override
     public void onFailure(Call> call, Throwable t) {

      }
});
/**
* 发送网络请求(同步)
*/
try {
   Response> response = call.execute();
} catch (IOException e) {
   e.printStackTrace();
}

(7)处理返回数据,此处的callback已经切换到UI线程

call.enqueue(new Callback>() {
     @Override
     public void onResponse(Call> call, Response> response) {
            //请求处理,输出结果
            System.out.println(response.body());
      }

     @Override
     public void onFailure(Call> call, Throwable t) {
          //请求处理,输出结果
          System.out.println("失败");
      }
});
/**
* 发送网络请求(同步)
*/
try {
   Response> 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> listRepos(@Path("user") String user);
}

在GET注解中包含了{user},它对应@Path注解中的“user”,而用来替换{user}的则正是需要传入的“String user”的值。

(2)动态指定查询条件:@Query,参数拼在url后面

public interface GitHubService {
  @GET("users/{user}/repos")
  Call> groupList(@Path("id") int groupId, @Query("sort") String sort);
}

(3)动态指定查询条件组:@QueryMap,参数拼在url后面

public interface GitHubService {
  @GET("users/{user}/repos")
  Call> groupList(@Path("id") int groupId, @QueryMap Map options);
}

在网络请求中一般我们需要传入很多查询参数,用@Query会比较麻烦(你想想要传很多参数,写很多个@Query),这时我们可以采用@QueryMap,将所有的参数集成在一个Map中统一传递。

  • 常用HTTP请求方法注解 —— POST,及标记类和参数类注解
    (1)传输数据类型为键值对:@Field,参数放在请求体中
public interface GitHubService {
  @FormUrlEncoded
  @POST("user/edit")
  Call updateUser(@Field("first_name") String first, @Field("last_name") String last);
}

FormUrlEncoded这一标记类注解来标明这是一个表单请求,然后在方法参数中使用@Field注解来表示所对应的String类参数的键,从而组成一组键值对进行传递

(2)传输数据类型JSON字符串:@Body

public interface GitHubService {
 @POST("users/new")
 Call createUser(@Body User user);
}

用@Body这个注解标识参数对象,Retrofit会将User对象转换为字符串

(3)单个文件上传:@Part

public interface MyInterface {

    @Multipart
    @POST("user/photo")
    Call 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 call = myInterface.updateUser(photo,desBody,photonameBody);

(4)多个文件上传:@PartMap

@Multipart
    @POST("user/photo")
    Call updateUser(@PartMap Map photos, @Part("desc") RequestBody desc);

多文件上传使用了Map封装了文件,其他与单个文件上传类似

  • 消息报头——Header
    (1)静态添加消息报头(单个或者多个),请求头不会相互覆盖,具有相同名的请求头都会被包含到请求中
@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);

(2)动态添加请求头(单个或者多个)

@GET("user")
Call getUser(@Header("Authorization") String authorization)
@GET("user")
Call getUser(@HeaderMap Map headers)

(3)1和2添加我们可以发现都是在单独的接口中进行的,如果我们需要在每个请求中添加相同的请求头呢,这时候需要使用OkHttp的拦截器
举个例子,定义一个拦截器如下:

public class CommonHeaderIntercepter implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        HashMap 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的更深入的介绍
另外这里有一篇注解比较全面的介绍

参考:
retrofit官方
这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)

你可能感兴趣的:(Retrofit的使用解析)