您的位置:首页 > 理论基础 > 计算机网络

Retrofit 2.0: 最好的Android HTTP客户端库的一次重大改变

2015-10-30 17:29 549 查看

Retrofit 2.0: 最好的Android HTTP客户端库的一次重大改变

声明:本文为原创翻译,下面会附上原文链接,欢迎转载,转载请注明出处,谢谢!

查看原文

  Retrofit是最流行http客户端开源库之一,和其他的库相比,其简单的使用方式和强大的性能表现使

其更加受到广大开发者的青睐。然而在Retrofit1.x版本上一个比较明显的缺点是没有任何一个方法可以取消将要执行的任务。如果你想要取消它,你必须是在一个线程中执行call方法并且手动的去杀死它,这是非常难以控制的事情。

  Square在一年前曾经承诺这个问题将在Retrofit2.0版本上得到解决,但是一年过去了,仍然没有新的更新的消息。直到上周,Retrofit2.0刚刚发布了它的2.0 beat1版本,当我尝试了一下之后,我必须要说,它新的模式和新的特性真的是令人印象深刻!它有了很多好的改变,我将要在这篇文章中去讲解这些改变,让我们开始吧!

更新老的库到新的版本  

  如果你想要导入Retrofit2.0到你的项目中,首先你要做的是添加上下面的这句代码到你的build.gradle文件中的dependencies部分。

  
  compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'

  


  同步一下你的gradle文件你就可以使用Retrofit2.0了。

新的Service的声明方式,不再有Synchornous和Asynchronous的区分

  在Retrofit1.9版本中,关于service接口的声明方式是这样的,如果你想要声明一个synchronous函数,你必须想这样去声明它:

/* Synchronous in Retrofit 1.9 */

public interface APIService {

@POST("/list")
Repo loadRepo();

}


如果要声明一个asynchronous的service则需要如下声明方式:

/* Asynchronous in Retrofit 1.9 */

public interface APIService {

@POST("/list")
void loadRepo(Callback<Repo> cb);

}


  但是到了Retrofit2.0就要简单的多了,你只需要以一种方式进行声明就可以了

import retrofit.Call;

/* Retrofit 2.0 */

public interface APIService {

@POST("/list")
Call<Repo> loadRepo();

}


  创建service对象的方式也发生了变化,变成了和OkHttp一样的方式。当需要以synchronous方式调用call方法时,只需要调用execute(),而需要以asynchronous请求时,则使用enqueue()方法。

同步请求

// Synchronous Call in Retrofit 2.0

Call<Repo> call = service.loadRepo();
Repo repo = call.execute();


  上面的代码会造成线程阻塞,所以在Android中,你不能在主线程中执行这样调用,否则你将要面对的是NetworkOnMainThreadException. 如果你想要执行execute方法,你必须在后台线程中去调用。

异步请求

// Synchronous Call in Retrofit 2.0

Call<Repo> call = service.loadRepo();
call.enqueue(new Callback<Repo>() {
@Override
public void onResponse(Response<Repo> response) {
// Get result Repo from response.body()
}

@Override
public void onFailure(Throwable t) {

}
});


 

  上面的代码将会在后台线程中去执行网络请求并且得到返回的结果,并将结果直接以一个Java Object的形式返回,你可以通过response.body()方法从返回的response中得到。需要注意的是onResponse() 和onFailure()方法将在主线程中被调用。 我建议你使用enqueue方法,它更加的适合Android的操作系统。

将要执行的请求的删除操作

  service执行方式的改变背后的原因是为了使得将要执行的请求可以被取消,如果你想要取消,只需要简单的执行call.cancel();
call.cancel();


当执行了这句代码,未执行的事物将很快被取消,是不是非常简单?

新的Servce创建方式。Converter已经被从Retrofit中移除

  在Retrofit1.9版本中,GsonConverter是被包含在这个库中的,并且在RestAdapter的创建过程中会被自动的初始化。结果是从服务器返回的json数据将会自动的被解析成之前定义好的对象(DAO)。但是在Retrofit2.0版本中,Converter不在被包含在这个库中了,你需要自己去添加这个插件,否则Retrofit将只能返回String形式的结果。因此,Retrofit2.0将不再依赖于Gson。

  如果你想要将返回的结果解析到DAO中,你需要添加Gson Converter作为一个单独的依赖部分。

添加支持库

compile 'com.squareup.retrofit:converter-gson:2.0.0-beta1'


然后通过addConverterFactory去添加这个插件,注意:之前的RestAdapter现在已经变成了Retrofit。代码如下:

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.nuuneoi.com/base/")
.addConverterFactory(GsonConverterFactory.create())
.build();

service = retrofit.create(APIService.class);


这里列出了一些Square官方提供的Converter类型。你可以选择其中最合适你的去使用。

Gson: com.squareup.retrofit:converter-gson
Jackson: com.squareup.retrofit:converter-jackson
Moshi: com.squareup.retrofit:converter-moshi
Protobuf: com.squareup.retrofit:converter-protobuf
Wire: com.squareup.retrofit:converter-wire
Simple XML: com.squareup.retrofit:converter-simplexml


你当然也可以通过实现Converter.Factory这个接口去自定义一个Converter。

我这里提供一个自定义的Converter形式,它使得Retrofit使用起来更加的清晰。

自定义一个Gson对象

假如你需要从一个json格式的数据中取出其中的一些特定格式的数据,例如日期格式的对象。你可以通过创建一个Gson对象并且把它作为参数传递给GsonConveterFactory.create();方法,代码如下:

Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.nuuneoi.com/base/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();

service = retrofit.create(APIService.class);


搞定!

新的URL解析规则

Retrofit2.0使用了一种新的URL解析规则。Base URL 和@Url不再仅仅是简单的组合在一起,而是使用和HTML中标签一样的解析方式。为了做一个清晰的理解,请看一下下面给的几个例子。







关于Retrofit2.0中这种新的URL解析规则,我的建议是:

-Base URL: 总是以/结尾

-Url:不要以/开头

举个栗子:

public interface APIService {

@POST("user/list")
Call<Users> loadUsers();

}

public void doSomething() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.nuuneoi.com/base/")
.addConverterFactory(GsonConverterFactory.create())
.build();

APIService service = retrofit.create(APIService.class);
}


上面的代码解析的URL地址是:http://api.nuuneoi.com/base/user/list

此外,我们还可以在@Url中直接定义完整的URL地址:

public interface APIService {

@POST("http://api.nuuneoi.com/special/user/list")
Call<Users> loadSpecialUsers();

}


在这种情况下base url将会被忽略

我们可以看到在URL的解析上有了一个很大的改变,和之前的版本完全不同,如果你想要将你的代码更新到Retrofit2.0版本,不要忘了去修改一下这一部分的代码。

OkHttp现在是必须的了

在Retrofit1.9版本中,OkHttp是一个可选项,如果你想要在Retrofit中使用OkHttp作为HTTP连接的接口,你必须手动的去将OKHttp做为支持库包含进来。但是到了2.0版本,OkHttp已经是必须的了,并且它会自动的作为一个支持库。下面的代码是Retrofit2.0的pom文件中的截图。你不再需要做任何事情。

<dependencies>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
</dependency>

...
</dependencies>


OkHttp已经自动的作为Http接口在Retrofit2.0中,这样做的目的是可以使用OkHttp的call方式就像上面介绍的那样。

即使在回调的过程中出现了问题,onResponse()回调方法仍然会被执行

在Retrofit1.9中如果连接的返回结果不能被解析成之前定义好的对象,failure回调方法就会被执行。但是在Retrofit2.0中,不论返回结果是否可以被解析到对象中,onResponse()回调方法总是会被执行。但是在返回失败的情况下,response.body()方法将会返回null! 不要忘了处理这种情况。

如果在链接的过程中发生其他的错误,比如,404错误,onResponse()方法依然会被执行。你可以通过response.errorBody().string();方法拿到错误的信息。



返回结果的成功和失败处理逻辑已经完全和之前不一样了,小心处理这一部分代码

如果在清单文件中少了INTERNET权限,将会抛出 SecurityException

在Retrofit1.9版本中,如果你忘记了在清单文件中添加INTERNET Permision。异步的网络请求将会马上失败,并执行failure()的回调函数,并返回 PERMISION DENIED 的错误。不会有任何的其他异常抛出。但是到了Retrofit2.0版本,当你执行call.enqueue()或者是call.executeI()方法时,SecurityException将会马上被抛出,并且会造成程序崩溃,如果你没有执行try-catch操作的话。



这种情况的错误和你调用HttpURLConnection时是一样的,然而只要你添加了INTERNET权限在清单文件中,这种问题就不用担心了。

在OkHttp中使用interceptor

在Retrofit1.9 中你可以使用RequestInterceptor去拦截一个请求,但是在Retrofit2.0中,它已经被移除了,以为他的HTTP连接层已经使用了okHttp。因此,我们必须将interceptor转换成okHttp中的Interceptor。首先,你必须创建一个OkHttpcClietn对象来回去interceptor 就像这样:

OkHttpClient client = new OkHttpClient();
client.interceptors().add(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());

// Do anything with response here

return response;
}
});


并且将创建出来的client对象传递给Retrofit的builder链条中。

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.nuuneoi.com/base/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();


这样就搞定了!

如果想了解更多的关于OkHttp Interceptor的原理,点击这里查看

通过CallAdapter集成了RxJava

在Retrofit2.0版本中,除了使用Call这种形式去声明一个接口,我们还可以声明我们自己定义的类型,例如,MyCall,我们把它叫做CallAdapter机制。

Retrofit团队为我们准备了一些已经可以使用的模型,其中最受欢迎的也许就是对RxJava进行支持的了,它可以返回一个Observable对象。如果想要这样使用,相关的两个支持项目要先引用进来。

compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
compile 'io.reactivex:rxandroid:1.0.1'


然后同步下gradle并且执行addCallAddapterFactory方法在Retrofit的builder链条中:

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.nuuneoi.com/base/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();


这样,你的service接口将会把Observable作为返回的对象了!

public interface APIService {

@POST("list")
Call<DessertItemCollectionDao> loadDessertList();

@POST("list")
Observable<DessertItemCollectionDao> loadDessertListRx();

}


这样你就可以使用它去执行RxJava一样的操作了。另外, 如果你想要使返回结果的操作在主线程中执行,只需要在RxJava的逻辑链条中添加上observerOn(AndroidSchedulers.mainThread());

Observable observable = service.loadDessertListRx();

observable.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<DessertItemCollectionDao>() {
@Override
public void onCompleted() {
Toast.makeText(getApplicationContext(),
"Completed",
Toast.LENGTH_SHORT)
.show();
}

@Override
public void onError(Throwable e) {
Toast.makeText(getApplicationContext(),
e.getMessage(),
Toast.LENGTH_SHORT)
.show();
}

@Override
public void onNext(DessertItemCollectionDao dessertItemCollectionDao) {
Toast.makeText(getApplicationContext(),
dessertItemCollectionDao.getData().get(0).getName(),
Toast.LENGTH_SHORT)
.show();
}
});


这样就全部搞定了!是不是非常牛逼!,我相信此时RxJava的粉丝已经露出了满意的微笑 =D

总结

除了这些变化之外,还有一些其他的变化,你可以查看官方文档的Change Log来查看更多的细节。我相信我已经把所有主要的变化都覆盖到了。你也许非常想知道是否需要将代码更新到Retrofit2.0版本了。毕竟它还只是一个beta版本。但是,Retrofit2.0版本已经工作的非常完美了,并且在我的测试中,它没有出现任何的bug。

  需要注意的是,Retrofit1.9版本的官方文档已经被从Square的github页面上删除了。我建议你开始学习Retrofit2.0并且在不久的将来把代码跟新到最新的版本上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: