程序员Android知识

一篇文章读懂Retrofit2.0,Retrofit2.0更新分

2017-10-05  本文已影响231人  MiracleSoul

[toc]

该文章为,国外博客的翻译,英文比较烂(主要靠Google),可能有很多翻译得不到位的地方,主要还是想给自己做一个笔记。

原文连接
https://inthecheesefactory.com/blog/retrofit-2.0/en

由于其简单性和与其他网络框架相比的卓越性能,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.gradledependencies部分。

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中提取结果。

请注意:onResponseonFailure将会在主线程中调用。

我建议你使用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声明模式的建议:

例如

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上已经不能这么做了。

因此,我们必须使用 OkHttpInterceptor。首先,你必须像这样创建一个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)

相关知识
移动安全之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

上一篇下一篇

猜你喜欢

热点阅读