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中添加网络权限

<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的更深入的介绍
另外这里有一篇注解比较全面的介绍

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容