Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android

2,438 阅读4分钟
原文链接: inthecheesefactory.com

中文版传送门Retrofit 2.0:有史以来最大的改进,译者@泡在网上编代码

Retrofit is one of the most popular HTTP Client Library for Android as a result of its simplicity and its great performance compare to the others.

Anyway its weakness is there is no any straight way to cancel the ongoing transaction in Retrofit 1.x. If you want to do that you have to call it on Thread and kill it manually which is quite hard to manage.

Square gave a promise years ago that this feature will be available on Retrofit 2.0 but years passed, there is still no updated news on this.

Until last week, Retrofit 2.0 just passed its Release Candidate stage to Beta 1 and has been publicly launched to everyone. After giving it a try, I must say that I am quite impressed on its new pattern and its new features. There are a lot of changes in the good way. I will describe those in this article. Let's get started !

If you want to import Retrofit 2.0 into your project, add this line to your build.gradle in dependencies section.

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

Sync your gradle files and you can now use Retrofit 2.0 =)

In regard to service interface declaration in Retrofit 1.9, if you want to declare a synchronous function, you have to declare like this:

/* Synchronous in Retrofit 1.9 */

public interface APIService {

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

}

And you have to declare an asynchronous one like this:

/* Asynchronous in Retrofit 1.9 */

public interface APIService {

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

}

But on Retrofit 2.0, it is far more simple since you can declare with only just a single pattern.

import retrofit.Call;

/* Retrofit 2.0 */

public interface APIService {

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

}

The way to call a created service is also changed into the same pattern as OkHttp. To call is as a synchronous request, just call execute or call enqueue to make an asynchronous request.

Synchronous Request

// Synchronous Call in Retrofit 2.0

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

The source code above will block the thread so you cannot call it on Main Thread in Android or you will face NetworkOnMainThreadException. If you want to call execute method, you have to do it on background thread.

Asynchronous Request

// Synchronous Call in Retrofit 2.0

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

    @Override
    public void onFailure(Throwable t) {

    }
});

The above code will make a request in the background thread and retreive a result as an Object which you can extract from response with response.body() method. Please note that those call methods: onResponse and onFailure will be called in Main Thread.

I suggest you to use enqueue. It fits Android OS behavior best.

The reason behind the service pattern changing to Call is to make the ongoing transaction be able to be cancelled. To do so, just simply call call.cancel()

call.cancel();

The transaction would be cancelled shortly after that. Easy, huh? =D

In Retrofit 1.9, GsonConverter is included in the package and is automatically initiated upon RestAdapter creation. As a result, the json result from server would be automatically parsed to the defined Data Access Object (DAO).

But in Retrofit 2.0, Converter is not included in the package anymore. You need to plug a Converter in yourself or Retrofit will be able to accept only the String result. As a result, Retrofit 2.0 doesn't depend on Gson anymore.

If you want to accept json result and make it parse into DAO, you have to summon Gson Converter as a separate dependency.

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

And plug it in through addConverterFactory. Please note that RestAdapter is now also renamed to Retrofit.

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

        service = retrofit.create(APIService.class);

Here is the list of official Converter modules provided by Square. Choose one that fits your requirement best.

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

You also can create a custom converter yourself by implementing a Converter.Factory interface.

I support this new pattern. It makes Retrofit more clear what it actually does.

In case you need to adjust some format in json, for example, Date Format. You can do that by creating a Gson object and pass it to 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);

Done.

Retrofit 2.0 comes with new URL resolving concept. Base URL and @Url have not just simply been combined together but have been resolved the same way as what  does instead. Please take a look for the examples below for the clarification.

查看图片

查看图片

查看图片

Here is my suggestion on the new URL declaration pattern in Retrofit 2.0:

- Base URL: always ends with /

- @Url: DO NOT start with /

for instance

public interface APIService {

    @POST("user/list")
    Call 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 from code above will fetch data from http://api.nuuneoi.com/base/user/list

Moreover we also can declare a full URL in @Url in Retrofit 2.0:

public interface APIService {

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

}

Base URL will be ignored for this case.

You will see that there is a major change on URL resolving. It is totally different from the previous version. If you want to move your code to Retrofit 2.0, don't forget to fix those URLs part of code.

OkHttp is set to optional in Retrofit 1.9. If you want to let Retrofit use OkHttp as HTTP connection interface, you have to manually include okhttp as a dependency yourself.

But in Retrofit 2.0, OkHttp is now required and is automatically set as a dependency. The code below is snapped from pom file of Retrofit 2.0. You have no need to do anything.

  
    
      com.squareup.okhttp
      okhttp
    

    ...
  

OkHttp is automatically used as a HTTP interface in Retrofit 2.0 in purpose to enabling the OkHttp's Call pattern as decribed above.

In Retrofit 1.9, if the fetched response couldn't be parsed into the defined Object, failure will be called. But in Retrofit 2.0, whether the response is be able to parse or not, onResponse will be always called. But in the case the result couldn't be parsed into the Object, response.body() will return as null. Don't forget to handle for the case.

If there is any problem on the response, for example, 404 Not Found. onResponse will also be called. You can retrieve the error body from response.errorBody().string().

查看图片

Response/Failure logic is quite different from Retrofit 1.9. Be careful on handling for all the cases if you decide to move to Retrofit 2.0.

In Retrofit 1.9, if you forget to add INTERNET permission into your AndroidManifest.xml file. Asynchronous request will immediately fall into failure callback method with PERMISSION DENIED error message. No any exception is thrown.

But in Retrofit 2.0, when you call call.enqueue or call.executeSecurityException will be immediately thrown and may cause crashing if you do not do the try-catch.

查看图片

The behavior is just like the same when you manually call HttpURLConnection. Anyway this issue is not a big deal since when INTERNET permission is added into AndroidManifest.xml, there is nothing to concern anymore.

There are also some other changes, you can check for the official Change Log for more details. Anyway I believe that I have already covered the main issues in this article.

You may be curious that is it time to move to Retrofit 2.0 yet? Since it is still in the beta stage so you may want to stay with 1.9 first except you are an early adopter like me, Retrofit 2.0 works pretty great and there is no any bug found yet based on my own experiment.

Please note that Retrofit 1.9 official document was already removed from Square github website. I suggest you to start studying for Retrofit 2.0 right now and consider moving to the latest version in very near future. =D

Author: nuuneoi (Android GDE, CTO & CEO at The Cheese Factory)

A full-stack developer with more than 6 years experience on Android Application Development and more than 12 years in Mobile Application Development industry. Also has skill in Infrastucture, Service Side, Design, UI&UX, Hardware, Optimization, Cooking, Photographing, Blogging, Training, Public Speaking and do love to share things to people in the world!