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

[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声明模式的建议:

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

因此,我们必须使用 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容