RxJava与Retrofit的简单使用

Retrofit

现在Android开发中,主流的网络请求库有Retrofit、OkHttp、Volley等,最热门的就是Retrofit了。

Retrofit官方的介绍是:A type-safe REST client for Android and Java(类型安全的REST客户端for 安卓&Java)

RESTful可以参考这篇文章,本文只做Retrofit的介绍以及配合RxJava使用上的简单介绍,本文中的Retrofit指retrofit:2.1.0版本;

1 Retrofit的简单使用

添加依赖:

1
2
3
4
5
6
7
//reteofit最新版本为2.1.0
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.google.code.gson:gson:2.6.2'

1.1 发起一次请求

  1. 创建Retrofit实例

    1
    2
    3
    4
    5
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    //json转换器,retrofit支持多种json转换器(Gson、Jackson、Moshi),需要添加不同的依赖
    .addConverterFactory(GsonConverterFactory.create())
    .build();

    注意:Retrofit的实例其实相当于一个http client,其底层实现默认使用的是OkHttp。因此在实际开发中,只需要存在一个实例即可,应当对其进行一次单例封装;baseUrl中的字符串必须要以“/”作为结尾,且其中不能含有如“?”之类的字符,否则会抛出**java.long.IllegalArgumentException:baseUrl must end in /"**

  2. 创建请求接口
    在Retrofit中,所有的请求应当按照要求声明在一个interface中,如下所示:

    1
    2
    3
    4
    public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user); //@Path注解的参数会添加到url中
    }

    其中**@GET**注解中的内容会与被添加到Retrofit实例中的baseUrl拼接,形成完整的Url;

  3. 发起请求

    1
    2
    3
    4
    5
    6
    7
    8
    GitHubService service = retrofit.create(GitHubService.class);  //获取请求接口实例
    Call<List<Repo>> call = service.listRepos("octocat"); //创建请求对象
    //同步请求
    try {
    List<Repo> repos = call.execute();
    } catch (IOException e) {
    e.printStackTrace();
    }
1
2
3
4
5
6
7
8
9
10
11
 //异步请求
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) {
//请求出错
}
});
  1. 取消请求
    1
    call.cancel();

1.2 请求接口

URL MANIPULATION (Url操作)

A request URL can be updated dynamically using replacement blocks and parameters on the method. A replacement block is an alphanumeric string surrounded by { and }. A corresponding parameter must be annotated with @Path using the same string.
一个请求的Url可以通过使用占位符与请求接口中方法的参数来动态更新,占位符通过字符**{}包括。占位符中的字符必须与@Path**注解中的字符相同。

1
2
3
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);
//当参数为groupList(1)时,请求的url将为https://api.github.com/group/1/users

Query parameters can also be added.
可以同时添加查询参数,使用**@Query**注解

1
2
3
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
//当参数为groupList(1,2)时,请求的url将为https://api.github.com/group/1/users/sort/2

For complex query parameter combinations a Map can be used.
复杂的请求参数可以使用**@QueryMap** 注解,添加一个map参数

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

REQUEST BODY (请求体)

An object can be specified for use as an HTTP request body with the @Body annotation.
可以通过**@Body** 注解来将一个对象作为请求体使用在一次HTTP请求中。

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

The object will also be converted using a converter specified on the Retrofit instance. If no converter is added, only RequestBody can be used.
//这个对象将通过实例化Retrofit时声明的JSON转换器转换成JSON字符串,如果没有添加转换器,那么被**@Body** 注解的参数只可以是RequestBody对象

表单编码、多请求、以及请求头操作大家可以自行查看文档,本文不作过多介绍;

2 Retrofit与RxJava配合使用

通过我们声明的请求接口方法我们可以看出,当我们通过请求接口获取一个请求时,创建的是一个Call<T>的实例,然后通过调用call.execute();或者call.enqueue(new Callback<T>() {});来执行同步请求或者异步请求。

Retrofit天生支持RxJava,首先我们需要要添加compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'依赖。

修改创建Retrofit实例的代码为如下样式

1
2
3
4
5
6
Retrofit retrofit = new Retrofit.Builder():
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
//添加RxJava适配器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();

修改请求接口中方法的返回值为如下样式:

1
2
3
4
public interface GitHubService {
@GET("users/{user}/repos")
Observable<List<Repo>> listRepos(@Path("user") String user); //@Path注解的参数会添加到url中
}

可以看出,我们只是在创建Retrofit实例时,为其增加了RxJava的适配器,并将创建请求的返回值改成了Observable(被观察者)。如果你熟悉RxJava那么接下来的事情就顺理成章了,我们不需要关心网络请求如何完成,只需要将对List的相关操作,按照RxJava的方式书写即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
service.listRepos("octocat")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Repo>>() {
@Override
public void onCompleted() {
//执行成功
}

@Override
public void onError(Throwable e) {
//抛出异常
}

@Override
public void onNext(List<Repo> repos) {
//对发射的数据进行操作
}
});

3 简单封装

上文中我们提到了Retrofit对象只需要一个实例,同时我们的请求接口GitHubService也只需要一个实例。所以我们应当将这两个实例封装在一个单例中。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class HttpMethods {

public static final String BASE_URL = "https://api.github.com/";

private static final int DEFAULT_TIMEOUT = 5;

private Retrofit mRetrofit;
private GitHubService mGitHubService;
//构造方法私有
private HttpMethods() {
//手动创建一个OkHttpClient并设置超时时间
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
//创建唯一的retrofit实例与请求接口实例
mRetrofit = new Retrofit.Builder()
.client(builder.build())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build();

mGitHubService = mRetrofit.create(GitHubService.class);
}

//在访问HttpMethods时创建单例
private static class SingletonHolder{
private static final HttpMethods INSTANCE = new HttpMethods();
}

//获取单例
public static HttpMethods getInstance(){
return SingletonHolder.INSTANCE;
}

/**
* 对被观察者进行注册操作
* @param observable
* @param subscriber
* @param <T>
*/
private <T> void toSubscribe(Observable<T> observable, Subscriber<T> subscriber){
observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
//声明我们的请求
public void listRepos(String user,Subscriber<List<Repo>> subscriber){
Observable observable = mGitHubService.listRepos(user);
toSubscribe(observable,subscriber);
}
}

在需要发起网络请求的位置执行如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Subscriber subscriber = new Subscriber<List<Repo>>() {
@Override
public void onCompleted() {
Logger.d("complete!!!");
}

@Override
public void onError(Throwable e) {
Logger.e(e, "");
}

@Override
public void onNext(List<Repo> repos) {
Logger.json(new Gson().toJson(repos));
}

};
HttpMethods.getInstance().listRepos("octocat" , subscriber);