Retrofit介绍以及使用

Posted by 阿呆 on 2019-01-06

前言

之前一直用的OkHttp的请求框架,最近由于接触到RxJava,由于Retrofit对RxJava的扩展很好,所以,有必要先学习一下Retrofit的基本用法

什么是Retrofit

Android是如何进行网络请求的,OkHttp是什么以及它与Retrofit的关系,这篇CSDN博客讲的很好Retrofit介绍;

Retrofit 2.0 入门

这个部分的内容,转载自一篇官方文档的中译版本,戳这里查看原文.

介绍

Retrofit可以将你的HTTP API转化未接口的形式,For example:

1
2
3
4
5
public interface GitHubServic
{
@Get("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}

这里Call中的T的类型对应于ResponseBody对应的bean,注意,是整个ResponseBody,如果有code和message的话,应该包含在内。此处 List的json源可以点击 这里 查看

而 Retrofit 类能够生成对应接口的实现,例如

1
2
3
4
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
GitHubService service = retrofit.create(GitHubService.class);

每一个由接口返回的Call对象都可以与远程web服务端进行同步或者异步的HTTP请求通信

1
Call<List<Repo>> repos = service.listRepos("PassedBy");

那么上面的这个Call是个什么东西,我们知道它肯定不是对应的实体类,它对应于OkHttp里面的Call对象吧,所以,最终的请求还是回到了 OkHttp 的方式哈,后续的请求过程代码如下:

1
2
3
4
5
6
7
8
9
10
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
//处理请求成功
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
//处理请求失败
}
});

Retrofit使用注解来描述HTTP请求:
1.URL参数的替换和query参数的支持
2.对象转换为请求体(如:JSON,protocol buffers等)
3.多重请求体和文件上传

API说明

Retrofit需要注解接口的请求方法类型和方法的参数来表明该请求需要怎么样的处理

1.请求方法
有五种内置的注解方式: GET、POST、PUT、DELETE以及HEAD

资源的相对url(基于BaseUrl)需要在注解里明确给出

1
2
3
@GET("users/list")
当然,也可以直接将query参数直接写在URL里:
@GET("users/list?sort=desc")

2.URL操作

GET {无QuereParam和有QuereParam{写成单个或者Map}}

@GET(“group/{id}/users”)
Call<List> groupList(@Path(“id”) int groupId);
Query参数也能同时添加。
@GET(“group/{id}/users”)
Call<List> groupList(@Path(“id”) int groupId, @Query(“sort”) String sort);
复杂的query参数可以用Map来构建
@GET(“group/{id}/users”)
Call<List> groupList(@Path(“id”) int groupId, @QueryMap Map<String, String> options);

这篇GET与POST的区别写的很生动有趣,但是注意,文中说它们本质上是一个东西是有问题的,它们是在应用层面的区别,抛开这个强行说二者本质相同时错误的,建议评论区的内容也一起看看。最好是遵循 HTTP 相关规范

POST提交数据的四种方式也应该一并回顾一下,写的很清楚明白

POST {四种形式}

3.请求主体
能够通过@Body注解来指定一个方法作为HTTP请求主体

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

这个参数对象会被Retrofit实例化中的converter进行转换,如果没有给Retrofit实例添加任何converter的话则只有RequestBody可以作为参数使用

4.form encode 和 multipart
方法也可以通过声明来发送form-encoded的数据,每个键值对要用@Field来注解键名,随后的对象需要提供值

1
2
3
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name")String last);

也可以通过@Multipart注解方法来发送Multipart请求,每个部分需要使用@Part来注解

1
2
3
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

多个请求部分需要使用Retrofit的converter或者自己实现RequestBody来处理自己内部的数据序列化

对RxJava的支持

我们将 interface GitHubService 稍作改变

1
2
3
4
5
6
7
public interface GitHubService {
// @GET("users/{user}")
// Call<GitHubUser> getUserInfo(@Path("user") String user);

@GET("users/{user}")
Observable<GitHubUser> getUserInfo(@Path("user") String user);
}

前提是你要添加

1
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

下一步,我们将retrofit于service封装起来,并提供API调用接口,这里我并没有将Observer作为参数传入开放API中,因为我觉得把订阅过程放在外部比较合适,比较符合思维习性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class HttpMethods {
private Retrofit gitRetrofit;
private GitHubService gitService;
private OkHttpClient client;
private static HttpMethods instance=null;

private HttpMethods(){
client = new OkHttpClient.Builder()
.connectTimeout(2000,TimeUnit.MILLISECONDS)
.build();
gitRetrofit = new Retrofit.Builder()
.baseUrl(Constant.gitBaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
gitService = gitRetrofit.create(GitHubService.class);
}

public static HttpMethods getInstance(){
if(instance==null){
instance = new HttpMethods();
}

return instance;
}

public Observable<GitHubUser> getUserInfo(String userName){
return gitService.getUserInfo(userName);
}
}

然后我们就可以在Activity里面调用 getUserInfo(String userName)得到一个 Observable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public void requestUserInfo(){
Observer<GitHubUser> observer = new Observer<GitHubUser>() {
@Override
public void onSubscribe(Disposable d) {

}

@Override
public void onNext(GitHubUser value) {
user = value;
logTv.setText(user.toString()+"\n");
}

@Override
public void onError(Throwable e) {

}

@Override
public void onComplete() {
Log.d(TAG, "onComplete: ");
}
};

httpMethods.getUserInfo("PassedBy")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}

逻辑非常的清晰,这就是Retrofit强大的地方,对于RxJava的支持!

一些补充

有些时候,我们希望把所有的操作写在Retrofit中,基于不同的BaseUrl,有的甚至没有baseurl

多BaseUrl

下面的几篇文章有不错的参考价值
动态改变BaseUrl

没有BaseUrl,例如是一个独立的下载地址

可以使用@Url来替换baseUrl

1
2
3
4
5
6
7
8
//-------Base Api ----------------
//any baseurl you want,just use @Url to replace it.Then you can download files imediatly.
apiRetrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://passedby.github.io")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
apiService = apiRetrofit.create(BaseApiService.class);

接下来是一个调用链,由于我们需要一个链式调用,将上面的例子进行了一下扩展
注意到这里由于无法将事件进行直接的变化,所以采取Flatmap的方式,因为Retrofit对于Observable的支持,当然,这里你也可以不用flatMap,而是map,但是会很麻烦,你要手动处理这个转换的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
httpMethods.getUserInfo("PassedBy")
.flatMap(new Function<GitHubUser, ObservableSource<ResponseBody>>() {
@Override
public ObservableSource<ResponseBody> apply(GitHubUser gitHubUser) throws Exception {
String avatarUrl = gitHubUser.avatar_url;
return httpMethods.downloadImg(avatarUrl);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(ResponseBody responseBody) throws Exception {
avatarIv.setImageBitmap(BitmapFactory.decodeStream(responseBody.byteStream()));
}
});