1 简介

Retrofit是Square公司开发的Android里对HTTP网络请求的框架,官网是https://square.github.io/retrofit/。其底层是基于OkHttp实现的,也就是说Retrofit就对OkHttp的进一步封装。Retrofit最大的特点就是简洁易用,它使用了大量的运行时注解的方式来提供功能。

2 快速上手

假设有一服务器接口:https://api.xx.com/url.json?id=123,其请求后的返回值是

[
   {
      "app_name": "今日头条",
      "package_name": "com.ss.android.article.news"
   },
   {
      "app_name": "腾讯新闻",
      "package_name": "com.tencent.news"
   },
   {
      "app_name": "凤凰新闻",
      "package_name": "com.ifeng.news2"
   }
]

现在我们需要在代码里对其进行网络请求,并将返回结果转化成AppInfo对象。AppInfo类代码如下所示:

AppInfo.java

public class AppInfo {
    private String app_name;
    private String package_name;

    @Override
    public String toString() {
        return "{app_name:" + app_name + ",package_name:" + package_name + "}";
    }
}

在build.gradle中添加支持库依赖:

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.6.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
}

在AndroidManifest.xml中声明网络请求权限:

<uses-permission android:name="android.permission.INTERNET"/>

于是我们使用Retrofit进行网络请求的代码可以这样:

private void getRequest() {
    Retrofit retorfit = new Retrofit.Builder()
            .baseUrl("https://api.xx.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    AppInfoService appInfoService = retorfit.create(AppInfoService.class);
    Call<List<AppInfo>> call = appInfoService.getAppInfoList(123);
    call.enqueue(new Callback<List<AppInfo>>() {
        @Override
        public void onResponse(Call<List<AppInfo>> call, Response<List<AppInfo>> response) {
            List<AppInfo> appInfos = response.body();
            Log.e("zyx", appInfos.toString());
            Log.e("zyx", "当前线程:" + Thread.currentThread().getName());
        }

        @Override
        public void onFailure(Call<List<AppInfo>> call, Throwable t) {
            Log.e("zyx", t.toString());
            Log.e("zyx", "当前线程:" + Thread.currentThread().getName());
        }
    });
}
public interface AppInfoService {
    @GET("url.json")
    Call<List<AppInfo>> getAppInfoList(@Query("id") int id);
}

在你的代码中执行getRequest方法,运行程序,便可见输出如下结果,而且回调还会在主线程中进行:

2019-12-04 19:27:23.209 30842-30842/com.zyx.myapplication E/zyx: [{app_name:今日头条,package_name:com.ss.android.article.news}, {app_name:腾讯新闻,package_name:com.tencent.news}, {app_name:凤凰新闻,package_name:com.ifeng.news2}]
2019-12-04 19:27:23.212 30842-30842/com.zyx.myapplication E/zyx: 当前线程:main

3 介绍

3.1 库的依赖和转换器

要使用Rerofit库,就必须在build.gradle中配置其依赖:

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.6.2'
}

上述示例中除添加Rerofit库外还添加了一个gson的库。其实gson库是可选的,默认情况下,Retrofit只能把HTTP响应结果反序列化成OkHttp的ResponseBody的类型并且只能接受它的RequsetBody的类型是@Body(后面介绍)。然而通过addConverterFactory方法可添加其它转换器来支持其他的类型。可以通过添加如下依赖包来支持其序列化操作。

  1. Gson: com.squareup.retrofit2:converter-gson
  2. Jackson: com.squareup.retrofit2:converter-jackson
  3. Moshi: com.squareup.retrofit2:converter-moshi
  4. Protobuf: com.squareup.retrofit2:converter-protobuf
  5. Wire: com.squareup.retrofit2:converter-wire
  6. Simple XML: com.squareup.retrofit2:converter-simplexml

当然你也可以通过自定义的方式创建自己的转换器,只要创建一个继承于Converter.Factory的类并实现responseBodyConverter 和 requestBodyConverter方法,然后使用addConverterFactory添加适配器的时候传入其一个实例就可以了。

3.2 请求方法注解

从上述示例中AppInfoService你会发现在getAppInfoList方法中就定义了一个@GET的注解,其实Rerofit里头很多是借助注解来完成。HTTP请求方法注解有8种,它们是GETPOSTPUTDELETEHEADPATCHOPTIONSHTTP。其中前面7种分别对应HTTP的请求方法;而最后一种HTTP 是通用注解,可以通过参数来替换以上 7 种。使用如:

@HTTP(method = "GET", path = " url.json", hasBody = false)
Call<List<AppInfo>> getAppInfoList();

method             表示请求的方法,注意它是区分大小写

path                  表示网络请求地址路径

hasBody           表示是否有请求体

3.3 请求头注解

请求头注解有两种:Headers 和 Header。它们区别在于,前者一般用于固件的请求头,可添加多个;而后者一般用于不固定请求头,作为方法的参数输入,使用如:

@Headers({"Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App"})
@GET("url.json")
Call<List<AppInfo>> getAppInfoList(@Header("Authorization") String authorization);

3.3 参数类注解

参数类注解有:BodyPathFieldFieldMapPartPartMapQueryQueryMapUrl 等。

3.3.1 Body

用于Post请求发送HTTP请求体,比如已添加过GsonConverterFactory,则可将类对象传入,使用如:

@POST("users/new")
Call<User> createUser(@Body User user);

3.3.2 Path

用于Url中的占位符,使用如:

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);

3.3.3 Field 和 FieldMap

用于Post请求时以表单的形式传递参数,需要结合@FromUrlEncoded使用,使用如:

@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@FieldMap Map<String, String> map);

3.3.4 Part 和 PartMap

用于Post请求时以表单的形式传递参数,与@Field区别于@Part可携带更加丰富的参数类型,比如用于文件上传时可携带数据流,需要结合@Multipart使用,使用如:

@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
@Multipart
@PUT("user/photo")
Call<User> updateUser(@PartMap Map<String, RequestBody> map);

3.3.5 Query 和 QueryMap

用于Get请求时传递参数,使用如:

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

3.3.6 Url

用于指定请求路径,使用如:

@GET
Call<List<AppInfo>> getAppInfoList(@Url String url, @Query("id") int id);

3.4 标记类注解

标记类注解有 3 种:FormUrlEncodedMultipartStreaming

3.4.1 FormUrlEncoded

用于Post请求时,请求实体是一个From表单的时,每个键值对需要使用@Field注解,使上如上面介绍Field 和 FieldMap。

3.4.2 Multipart

用于Post请求时,请求实体是丰富的参数类型,比如用于文件上传时可携带数据流,每个键值对需要使用@Part注解,使上如上面介绍Part和PartMap。

3.4.3 Streaming

一般在下载比较大的文件时,需要添加@Streaming注解,表示响应字节流的形式返回,这样就可避免大文件全部加载到内存中。

3.5 同步和异步

Call对象可以同步地或异步地进行网络请求,而且每一个对象只能使用一次,若想执行多次可以调用clone()来创建一个新对象。还要注意的是,在Android中,回调会在主线程中执行,而在JVM中,回调会在与执行HTTP请求的相同线程。

同步执行示例代码:

try {
    Response<List<AppInfo>> response = call.execute();
    List<AppInfo> appInfos = response.body();
    Log.e("zyx", appInfos.toString());
    
} catch (IOException e) {
    e.printStackTrace();
}

异步执行示例代码:

call.enqueue(new Callback<List<AppInfo>>() {
    @Override
    public void onResponse(Call<List<AppInfo>> call, Response<List<AppInfo>> response) {
        List<AppInfo> appInfos = response.body();
        Log.e("zyx", appInfos.toString());
    }

    @Override
    public void onFailure(Call<List<AppInfo>> call, Throwable t) {
        Log.e("zyx", t.toString());
    }
});

看到上述同步和异步代码后,你是不是有似曾相识的感谢。我们之前学习OkHttp中同步和异步请求也是差不多的代码,因为Rerofit框架底层本来就是使用了OkHttp的。