一篇文章读懂Retrofit2.0,Retrofit2.0更新分
[toc]
该文章为,国外博客的翻译,英文比较烂(主要靠Google),可能有很多翻译得不到位的地方,主要还是想给自己做一个笔记。
由于其简单性和与其他网络框架相比的卓越性能,Retrofit是Android的最流行的HTTP客户端库之一。
无论如何,它的弱点是在Retrofit 1.x中,没有任何直接的方式取消正在进行的网络请求。如果你想这样做,你必须在Thread上调用它,并手动杀死该Thread,这是一种很难管理的方式。
Square几年前承诺,此功能将在Retrofit 2.0上推出,但过去几年,仍然没有更新的新闻。
直到上周(译者注:该博客发表时间为 2015-11-06 12:33),Retrofit 2.0才刚刚通过了Beta 1的发布阶段,并已被公开发布给大家。尝试之后,我必须说,我对它的新模式和新功能印象深刻。有很多往好的方向的变化。我将在本文中描述。让我们开始吧 !
一、新版本导入
如果要将Retrofit 2.0导入到项目中,请将此行添加到你 build.gradle
的dependencies
部分。
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
截至2017-10-3 最新版为:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
同步你的 gradle 文件,你现在就可以使用Retrofit 2.0了 =)
正如你所看到的,Retrofit 2包名称与以前的版本不同。现在是 com.squareup.retrofit2
二、新的Service声明方式,同步和异步方法不再被区分。
关于Retrofit 1.9中的Service接口声明,如果要声明一个同步请求,则必须像这样声明:
/* Synchronous in Retrofit 1.9 */
public interface APIService {
@POST("/list")
Repo loadRepo();
}
如果你需要一个异步请求,如下所示:
/* Asynchronous in Retrofit 1.9 */
public interface APIService {
@POST("/list")
void loadRepo(Callback<Repo> cb);
}
但是在Retrofit 2.0中,它更简单,因为你只能使用单个模式进行声明。
import retrofit.Call;
/* Retrofit 2.0 */
public interface APIService {
@POST("/list")
Call<Repo> loadRepo();
}
调用创建Service的方式也改变为与OkHttp相同的模式。
要调用是同步请求,只需调用execute
要调用是同步请求,只需调用enqueue
同步请求
// Synchronous Call in Retrofit 2.0
Call<Repo> call = service.loadRepo();
Repo repo = call.execute();
上面的源代码将阻塞线程,所以你不能在Android的主线程中调用它,否则你将面临NetworkOnMainThreadException
。如果你想调用execute
方法,你必须在后台线程上执行。
异步请求
// Asynchronous 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) {
}
});
上述代码将在后台线程中发出请求,并将结果以Object的形式取回,你可以通过response.body()
方法从response中提取结果。
请注意:onResponse
和onFailure
将会在主线程中调用。
我建议你使用enqueue
,因为它最适合Android。
三、取消正在进行的事务
Service声明方式转变Call
的原因,是使得正在进行的事务能够被取消。若要取消事务,只需调用call.cancel()
call.cancel();
调用该方法之后,事务将会很快的被取消。很轻松吧?= D
四、在新Service的构建中,转换器(Converter)现在被从Retrofit包中去除
在Retrofit 1.9中,GsonConverter包含在包中,并在RestAdapter
创建时自动初始化。因此,服务器返回的json将自动解析为定义的 数据访问对象 (DAO)。
但在Retrofit 2.0中,转换器(Converter)不再包含在包中。你需要自己添加一个转换器(Converter),否则Retrofit将只能接受String的结果。因此,Retrofit 2.0不再依赖于Gson。
如果要接受json结果并将其解析成DAO,则必须将Gson Converter作为单独的依赖项。
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
并通过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)。
我支持这种新模式。它使得Retrofit更专注于处理网络连接(原文:It makes Retrofit more clear what it actually does.)。
五、自定义Gson对象
如果你需要在json中调整某些格式,例如Date Format。你可以通过创建一个Gson对象,并将其作为参数传递给 GsonConverterFactory.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解析方式。与<a href>的相同
Retrofit 2.0附带了新的URL解析方式。Base URL 和 @Url 不只是简单地组合在一起,而是以和<a href="...">
相同的方式进行解析。
请查看下面的示例以进行说明。
以下是我对Retrofit 2.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);
}
上面代码中的loadUsers()
将从http://api.nuuneoi.com/base/user/list
获取数据
此外,在Retrofit 2.0中,我们还可以在@Url
中声明一个完整的URL:
public interface APIService {
@POST("http://api.nuuneoi.com/special/user/list")
Call<Users> loadSpecialUsers();
}
在这种情况下,Base URL将被忽略。
可以看到,URL解析有重大变化。它与以前的版本完全不同。如果要将现有代码升级到Retrofit 2.0,请不要忘记修复这些URL部分的代码。
七、OkHttp现在为必用的
OkHttp在“Retrofit 1.9”中为可选项。如果要让Retrofit使用OkHttp作为HTTP连接接口,那么你必须自己手动将okhttp
添加至依赖项。
但是在Retrofit 2.0中,必须使用OkHttp,并且会自动添加为依赖。下面的代码是从Retrofit 2.0的pom文件中获取的。你没有必要做任何事情。
<dependencies>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
</dependency>
...
</dependencies>
在Retrofit 2.0中自动使用Okhttp作为HTTP接口,目的是使用OkHttp的Call
方式,将网络请求变成上文中描述的那样。
八、即使返回值有问题(主要指无法解析),onResponse仍然会被调用
在Retrofit 1.9中,如果获取的返回值无法解析到定义好的对象之中,failure
将被调用。但是在Retrofit 2.0中,无论返回值是否能够解析,onResponse
都会被调用。但是在结果无法解析为Object的情况下,response.body()
将返回为null。注意:不要忘记处理这一种情况。
如果返回值有问题,例如404 Not Found。onResponse
也将被调用。您可以response.errorBody().string()
中检索错误信息
Response / Failure的逻辑与Retrofit 1.9完全不同。如果你决定将代码升级到Retrofit 2.0,请小心处理这些情况。
九、缺少INTERNET权限会导致抛出SecurityException异常
在Retrofit 1.9中,如果您忘记在AndroidManifest.xml
文件中添加 INTERNET
权限。异步请求将立即进入failure
的回调函数中,并带着错误信息:PERMISSION DENIED。不会额外抛出异常。
但在Retrofit2.0之中,当你调用call.enqueue
或者call.execute
时,会立刻抛出SecurityException
。如果你没有使用try-catch来处理该情况,很可能导致应用崩溃。
这种情况和,当手动调用HttpURLConnection
是一样的
无论如何,这个问题不是一件大事,因为只要把INTERNET权限添加到AndroidManifest.xml之中,这个问题就不存在了。
十、使用OkHttp的拦截器
在Retrofit 1.9中,您可以使用 RequestInterceptor
拦截请求,但是由于HTTP连接层已被移动到 OkHttp,所以在Retrofit 2.0上已经不能这么做了。
因此,我们必须使用 OkHttp 的Interceptor
。首先,你必须像这样创建一个OkHttpClient
对象并创建一个拦截器
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();
That's all.
要了解OkHttp拦截器的更多信息,请浏览 OkHttp拦截器。
附:感谢汉之风云的中文翻译版
十一、证书锁定(Certificate Pinning)
与拦截器一样,如果要使连接应用的证书锁定,也需要先创建OkHttpClient实例。以下是示例代码段。
首先,定义具有证书锁定信息的OkHttpClient实例:
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(new CertificatePinner.Builder()
.add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
.add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
.add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
.add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
.build())
.build();
在Retrofit的Builder的链式调度之中,传入刚创建好的 OkhttpClient。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.nuuneoi.com/base/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
有关证书锁定的sha1哈希算法的更多信息,Google会帮助你(必应国际版也挺好用)
十二、RxJava与CallAdapter集成
除了以Call<T>
的形式声明接口之外,我们也可以声明自己的类型,例如 MyCall<T>
。这些方式被称为“ CallAdapter
”,它可以在Retrofit 2.0上使用
有一些可以从Retrofit团队获得的即用型CallAdapter模块。最受欢迎的模块之一可能是RxJava的CallAdapter,它将返回 Observable<T>
。要使用它,必须添加以下两个依赖项。
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
compile 'io.reactivex:rxandroid:1.0.1'
同步Gradle并添加 addCallAdapterFactory
到Retrofit Builder的链式调度中,如下所示:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.nuuneoi.com/base/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
你的Service接口现在可以就返回 Observable<T>
了!
public interface APIService {
@POST("list")
Call<DessertItemCollectionDao> loadDessertList();
@POST("list")
Observable<DessertItemCollectionDao> loadDessertListRx();
}
您可以用和RxJava完全相同的方式来使用它。另外,如果要让订阅部分的代码在主线程上调用,observeOn(AndroidSchedulers.mainThread())
需要添加到Observable的链式调度中。
Observable<DessertItemCollectionDao> observable = service.loadDessertListRx();
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.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
十三、结论
还有一些其他变化,您可以查看更改日志 了解更多信息。
请注意,Retrofit 1.9官方文档已从Square github网站中删除。我建议你现在开始学习Retrofit 2.0,并考虑在不久的将来升级到最新版本。= D