Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Simple HTTP with Retrofit 2 (Droidcon NYC 2015)

Simple HTTP with Retrofit 2 (Droidcon NYC 2015)

Retrofit has been simplifying HTTP calls for years and this new version is no different. In addition to fixing some long-standing annoyances, there are a handful of new features which make it more powerful than ever.

This talk will focus on how the new APIs in Retrofit aid in making HTTP calls as simple as possible for your app. The integration with OkHttp and Okio APIs will be covered to ensure a full understanding of the HTTP stack. Common use-cases and design patterns through more advanced functionality will end the talk.

No prior Retrofit, OkHttp, or Okio exposure needed. The content may quickly cover or skip some introductory aspects in order to allow focus on more actionable and useful content.

Video: https://youtu.be/KIAoQbAu3eA

Jake Wharton
PRO

August 27, 2015
Tweet

More Decks by Jake Wharton

Other Decks in Programming

Transcript

  1. Retrofit Two
    Jake Wharton

    View Slide

  2. Retrofit 2 will be out
    by the end of this year
    " "
    — Naïve Man

    View Slide

  3. Retrofit 2 will be out
    by the end of this year
    " "
    — Jake Wharton

    View Slide

  4. Retrofit 2 will be out
    by the end of this year
    " "
    Droidcon NYC 2014
    — Jake Wharton

    View Slide

  5. Retrofit 2 will be out
    by the end of this year
    " "
    Droidcon NYC 2015
    — Jake Wharton

    View Slide

  6. Retrofit 1

    View Slide

  7. Retrofit 1
    • Made open source on October 13th, 2010

    View Slide

  8. Retrofit 1
    • Made open source on October 13th, 2010
    • Originally a Bob Lee joint

    View Slide

  9. Retrofit 1
    • Made open source on October 13th, 2010
    • Originally a Bob Lee joint
    • Took over / stole stewardship mid-2012

    View Slide

  10. Retrofit 1
    • Made open source on October 13th, 2010
    • Originally a Bob Lee joint
    • Took over / stole stewardship mid-2012
    • Released 1.0 on May 13th, 2013

    View Slide

  11. Retrofit 1
    • Made open source on October 13th, 2010
    • Originally a Bob Lee joint
    • Took over / stole stewardship mid-2012
    • Released 1.0 on May 13th, 2013
    • 18 releases post-1.0 with tons of features / fixes

    View Slide

  12. The Good

    View Slide

  13. The Good
    • Interface service declarations
    • Method and parameter annotations customize request

    View Slide

  14. Interface and annotations
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    List repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  15. The Good
    • Interface service declarations
    • Method and parameter annotations customize request
    • Pluggable HTTP client and serialization

    View Slide

  16. Pluggable client and serialization
    builder.setClient(new UrlConnectionClient());
    builder.setClient(new ApacheClient());
    builder.setClient(new OkClient());

    View Slide

  17. Pluggable client and serialization
    builder.setClient(new UrlConnectionClient());
    builder.setClient(new ApacheClient());
    builder.setClient(new OkClient());
    builder.setClient(new CustomClient());

    View Slide

  18. Pluggable client and serialization
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    List repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  19. Pluggable client and serialization
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    List repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    builder.setConverter(new GsonConverter());
    builder.setConverter(new JacksonConverter());

    View Slide

  20. Pluggable client and serialization
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    ContributorResponse repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  21. Pluggable client and serialization
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    ContributorResponse repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    builder.setConverter(new ProtoConverter());
    builder.setConverter(new WireConverter());

    View Slide

  22. Pluggable client and serialization
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    ContributorResponse repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    builder.setConverter(new ProtoConverter());
    builder.setConverter(new WireConverter());
    builder.setConverter(new SimpleXMLConverter());

    View Slide

  23. Pluggable client and serialization
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    ContributorResponse repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    builder.setConverter(new ProtoConverter());
    builder.setConverter(new WireConverter());
    builder.setConverter(new SimpleXMLConverter());
    builder.setConverter(new CustomConverter());

    View Slide

  24. The Good
    • Interface service declarations
    • Method and parameter annotations customize request
    • Pluggable HTTP client and serialization
    • Synchronous, asynchronous, and RxJava execution

    View Slide

  25. Sync, async, and RxJava
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    List repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    List contributors =
    gitHubService.repoContributors("square", "retrofit");

    View Slide

  26. Sync, async, and RxJava
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    void repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo,

    Callback> cb);

    }
    service.repoContributors("square", "retrofit", new Callback>() {

    @Override void success(List contributors, Response response) {

    // ...

    }


    @Override void failure(RetrofitError error) {

    // ...

    }

    });

    View Slide

  27. Sync, async, and RxJava
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    gitHubService.repoContributors("square", "retrofit")

    .subscribe(new Action1>() {

    @Override public void call(List contributors) {

    // ...

    }

    });

    View Slide

  28. The Good
    • Interface service declarations
    • Method and parameter annotations customize request
    • Pluggable HTTP client and serialization
    • Synchronous, asynchronous, and RxJava execution

    View Slide

  29. The Not-So-Good

    View Slide

  30. The Not-So-Good
    • Request/Response (and friends) model classes

    View Slide

  31. Request/Response model classes

    View Slide

  32. The Not-So-Good
    • Request/Response (and friends) model classes
    • No access to serialized body and response data

    View Slide

  33. No access to body and response
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    List repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }X

    View Slide

  34. No access to body and response
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    List repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    @GET("/repos/{owner}/{repo}/contributors")

    Response repoContributors2(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }X

    View Slide

  35. The Not-So-Good
    • Request/Response (and friends) model classes
    • No access to serialized body and response data
    • Rigid execution mechanisms

    View Slide

  36. Rigid execution mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    List repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);
    @GET("/repos/{owner}/{repo}/contributors")

    void repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo,

    Callback> cb);

    }

    View Slide

  37. Rigid execution mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  38. Rigid execution mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    ListenableFuture> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  39. Rigid execution mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    CompletableFuture> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  40. The Not-So-Good
    • Request/Response (and friends) model classes
    • No access to serialized body and response data
    • Rigid execution mechanisms
    • Inefficient converter usage

    View Slide

  41. Inefficient converter usage
    interface Converter {

    Object fromBody(TypedInput body, Type type);

    TypedOutput toBody(Object object);

    }

    View Slide

  42. Inefficient converter usage
    interface Converter {

    Object fromBody(TypedInput body, Type type);

    TypedOutput toBody(Object object);

    }
    Type
    TypeAdapter
    TypedInput/Output
    JsonAdapter
    ObjectReader/Writer
    Parser
    ProtoAdapter

    View Slide

  43. Inefficient converter usage
    interface Converter {

    Object fromBody(TypedInput body, Type type);

    TypedOutput toBody(Object object);

    }
    Type
    TypeAdapter
    TypedInput/Output
    JsonAdapter
    ObjectReader/Writer
    Parser
    ProtoAdapter

    View Slide

  44. The Not-So-Good
    • Request/Response (and friends) model classes
    • No access to serialized body and response data
    • Rigid execution mechanisms
    • Inefficient converter usage
    • Simple custom parameter types support

    View Slide

  45. Simple custom parameter types
    interface GitHubService {

    @GET("/search/repositories")

    RepositoriesResponse searchRepos(

    @Query("q") String query,

    @Query("since") Date since);

    }X

    View Slide

  46. Simple custom parameter types
    interface GitHubService {

    @GET("/search/repositories")

    RepositoriesResponse searchRepos(

    @Query("q") String query,

    @Query("since") Date since);

    }X
    /search/repositories?q=retrofit&since=2015-08-27

    View Slide

  47. Simple custom parameter types
    interface GitHubService {

    @GET("/search/repositories")

    RepositoriesResponse searchRepos(

    @Query("q") String query,

    @Query("since") Date since);

    }X
    /search/repositories?q=retrofit&since=2015-08-27
    /search/repositories?q=retrofit&since=20150827

    View Slide

  48. The Not-So-Good
    • Request/Response (and friends) model classes
    • No access to serialized body and response data
    • Rigid execution mechanisms
    • Inefficient converter usage
    • Simple custom parameter types support

    View Slide

  49. Retrofit 2

    View Slide

  50. Retrofit 2
    • Call encapsulates single request/response interaction

    View Slide

  51. Call
    • Models a single request/response pair

    View Slide

  52. Call
    • Models a single request/response pair
    • Separates request creation from response handling

    View Slide

  53. Call
    • Models a single request/response pair
    • Separates request creation from response handling
    • Each instance can only be used once...

    View Slide

  54. Call
    • Models a single request/response pair
    • Separates request creation from response handling
    • Each instance can only be used once...
    • ...instances can be cloned

    View Slide

  55. Call
    • Models a single request/response pair
    • Separates request creation from response handling
    • Each instance can only be used once...
    • ...instances can be cloned
    • Supports both synchronous and asynchronous execution.

    View Slide

  56. Call
    • Models a single request/response pair
    • Separates request creation from response handling
    • Each instance can only be used once...
    • ...instances can be cloned
    • Supports both synchronous and asynchronous execution.
    • Can be (actually) canceled

    View Slide

  57. Call
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    List repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);
    }X

    View Slide

  58. Call
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }X

    View Slide

  59. Call
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  60. Call
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    Call> call =
    gitHubService.repoContributors("square", "retrofit");

    View Slide

  61. Call
    Call> call =
    gitHubService.repoContributors("square", "retrofit");

    View Slide

  62. Call
    Call> call =
    gitHubService.repoContributors("square", "retrofit");

    response = call.execute();

    View Slide

  63. Call
    Call> call =
    gitHubService.repoContributors("square", "retrofit");

    response = call.execute();
    // This will throw IllegalStateException:

    response = call.execute();

    View Slide

  64. Call
    Call> call =
    gitHubService.repoContributors("square", "retrofit");

    response = call.execute();
    // This will throw IllegalStateException:

    response = call.execute();
    Call> call2 = call.clone();

    // This will not throw:

    response = call2.execute();

    View Slide

  65. Call
    Call> call =
    gitHubService.repoContributors("square", "retrofit");

    View Slide

  66. Call
    Call> call =
    gitHubService.repoContributors("square", "retrofit");

    call.enqueue(new Callback>() {

    @Override void onResponse(/* ... */) {

    // ...

    }


    @Override void onFailure(Throwable t) {

    // ...

    }

    });

    View Slide

  67. Call
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    call.enqueue( );
    // or...
    call.execute();

    View Slide

  68. Call
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    call.enqueue( );
    // or...
    call.execute();
    // later...
    call.cancel();

    View Slide

  69. Retrofit 2
    • Call encapsulates single request/response interaction
    • Parameterized Response object

    View Slide

  70. Response
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    response = call.execute();

    View Slide

  71. Response
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    Response> response = call.execute();

    View Slide

  72. Response
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    Response> response = call.execute();

    View Slide

  73. Response
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    call.enqueue(new Callback>() {

    @Override void onResponse(/* ... */) {

    // ...

    }X


    @Override void failure(Throwable t) {

    // ...

    }X

    });

    View Slide

  74. Response
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    call.enqueue(new Callback>() {

    @Override void onResponse(Response> response) {

    // ...

    }X


    @Override void failure(Throwable t) {

    // ...

    }X

    });

    View Slide

  75. Response
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    call.enqueue(new Callback>() {

    @Override void onResponse(Response> response) {

    // ...

    }


    @Override void failure(Throwable t) {

    // ...

    }

    });

    View Slide

  76. Response
    class Response {

    }X

    View Slide

  77. Response
    class Response {

    int code();

    String message();
    Headers headers();

    }X

    View Slide

  78. Response
    class Response {

    int code();

    String message();
    Headers headers();


    boolean isSuccess();

    }X

    View Slide

  79. Response
    class Response {

    int code();

    String message();
    Headers headers();


    boolean isSuccess();
    T body();

    ResponseBody errorBody();

    }X

    View Slide

  80. Response
    class Response {

    int code();

    String message();
    Headers headers();


    boolean isSuccess();
    T body();

    ResponseBody errorBody();
    com.squareup.okhttp.Response raw();

    }X

    View Slide

  81. Retrofit 2
    • Call encapsulates single request/response interaction
    • Parameterized Response object

    View Slide

  82. If it isn't broken...

    View Slide

  83. Fixed Query Param
    interface SomeService {

    @GET("/some/endpoint?fixed=query")

    Call someEndpoint();

    }
    someService.someEndpoint();
    // GET /some/endpoint?fixed=query HTTP/1.1

    View Slide

  84. Dynamic Query Param
    interface SomeService {

    @GET("/some/endpoint")

    Call someEndpoint(

    @Query("dynamic") String dynamic);

    }
    someService.someEndpoint("query");
    // GET /some/endpoint?dynamic=query HTTP/1.1

    View Slide

  85. Dynamic Query Param Map
    interface SomeService {

    @GET("/some/endpoint")

    Call someEndpoint(

    @QueryMap Map dynamic);

    }
    someService.someEndpoint(
    Collections.singletonMap("dynamic", "query"));
    // GET /some/endpoint?dynamic=query HTTP/1.1

    View Slide

  86. Omit Dynamic Query Param
    interface SomeService {

    @GET("/some/endpoint")

    Call someEndpoint(

    @Query("dynamic") String dynamic);

    }
    someService.someEndpoint(null);
    // GET /some/endpoint HTTP/1.1

    View Slide

  87. Fixed+Dynamic Query Params
    interface SomeService {

    @GET("/some/endpoint?fixed=query")

    Call someEndpoint(

    @Query("dynamic") String dynamic);

    }
    someService.someEndpoint("query");
    // GET /some/endpoint?fixed=query&dynamic=query HTTP/1.1

    View Slide

  88. Path Replacement
    interface SomeService {

    @GET("/some/endpoint/{foo}")

    Call someEndpoint(

    @Path("thing") String thing);

    }
    someService.someEndpoint("bar");
    // GET /some/endpoint/bar HTTP/1.1

    View Slide

  89. Fixed Header
    interface SomeService {

    @GET("/some/endpoint")
    @Headers("Accept-Encoding: application/json")

    Call someEndpoint();

    }
    someService.someEndpoint();
    // GET /some/endpoint HTTP/1.1
    // Accept-Encoding: application/json

    View Slide

  90. Dynamic Header
    interface SomeService {

    @GET("/some/endpoint")

    Call someEndpoint(
    @Header("Location") String location);

    }
    someService.someEndpoint("Droidcon NYC 2015");
    // GET /some/endpoint HTTP/1.1
    // Location: Droidcon NYC 2015

    View Slide

  91. Omit Dynamic Header
    interface SomeService {

    @GET("/some/endpoint")

    Call someEndpoint(
    @Header("Location") String location);

    }
    someService.someEndpoint(null);
    // GET /some/endpoint HTTP/1.1

    View Slide

  92. Fixed+Dynamic Header
    interface SomeService {

    @GET("/some/endpoint")
    @Headers("Accept-Encoding: application/json")

    Call someEndpoint(
    @Header("Location") String location);

    }
    someService.someEndpoint("Droidcon NYC 2015");
    // GET /some/endpoint HTTP/1.1
    // Accept-Encoding: application/json
    // Location: Droidcon NYC 2015

    View Slide

  93. Post Without Body
    interface SomeService {

    @POST("/some/endpoint")

    Call someEndpoint();

    }
    someService.someEndpoint();
    // POST /some/endpoint?fixed=query HTTP/1.1
    // Content-Length: 0

    View Slide

  94. Post With Body
    interface SomeService {

    @POST("/some/endpoint")

    Call someEndpoint(
    @Body SomeRequest body);

    }
    someService.someEndpoint();
    // POST /some/endpoint HTTP/1.1
    // Content-Length: 3
    // Content-Type: greeting
    //
    // Hi!

    View Slide

  95. Form Encoded Fields
    interface SomeService {

    @FormUrlEncoded
    @POST("/some/endpoint")

    Call someEndpoint(
    @Field("name1") String name1,
    @Field("name2") String name2);

    }
    someService.someEndpoint("value1", "value2");
    // POST /some/endpoint HTTP/1.1
    // Content-Length: 25
    // Content-Type: application/x-www-form-urlencoded
    //
    // name1=value1&name2=value2

    View Slide

  96. Omit Form Encoded Field
    interface SomeService {

    @FormUrlEncoded
    @POST("/some/endpoint")

    Call someEndpoint(
    @Field("name1") String name1,
    @Field("name2") String name2);

    }
    someService.someEndpoint("value1", null);
    // POST /some/endpoint HTTP/1.1
    // Content-Length: 12
    // Content-Type: application/x-www-form-urlencoded
    //
    // name1=value1

    View Slide

  97. Form Encoded Field Map
    interface SomeService {

    @FormUrlEncoded
    @POST("/some/endpoint")

    Call someEndpoint(
    @FieldMap Map names);

    }
    someService.someEndpoint(
    ImmutableMap.of("name1", "value1", "name2", "value2"));
    // POST /some/endpoint HTTP/1.1
    // Content-Length: 25
    // Content-Type: application/x-www-form-urlencoded
    //
    // name1=value1&name2=value2

    View Slide

  98. Multipart Parts
    interface SomeService {

    @Multipart
    @POST("/some/endpoint")

    Call someEndpoint(
    @Part("name1") String name1,
    @Part("name2") String name2);

    }
    someService.someEndpoint("value1", "value2");
    // POST /some/endpoint HTTP/1.1
    // Content-Length: 102
    // Content-Type: application/form-data
    //
    // ...

    View Slide

  99. Omit Multipart Part
    interface SomeService {

    @Multipart
    @POST("/some/endpoint")

    Call someEndpoint(
    @Part("name1") String name1,
    @Part("name2") String name2);

    }
    someService.someEndpoint("value1", null);
    // POST /some/endpoint HTTP/1.1
    // Content-Length: 56
    // Content-Type: application/form-data
    //
    // ...

    View Slide

  100. Multipart Part Map
    interface SomeService {

    @Multipart
    @POST("/some/endpoint")

    Call someEndpoint(
    @PartMap Map names);

    }
    someService.someEndpoint(
    ImmutableMap.of("name1", "value1", "name2", "value2"));
    // POST /some/endpoint HTTP/1.1
    // Content-Length: 102
    // Content-Type: application/form-data
    //
    // ...

    View Slide

  101. If it isn't broken...

    View Slide

  102. Retrofit 2
    • Call encapsulates single request/response interaction
    • Parameterized Response object
    • Dynamic URL parameter

    View Slide

  103. Dynamic URL
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  104. Dynamic URL
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    Response> response = call.execute();

    View Slide

  105. Dynamic URL
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    Call> call =
    gitHubService.repoContributors("square", "retrofit");
    Response> response = call.execute();
    // HTTP/1.1 200 OK
    // Link: page=2>; rel="next", contributors?page=3>; rel="last"
    // ...

    View Slide

  106. Dynamic URL
    Response> response = call.execute();
    // HTTP/1.1 200 OK
    // Link: page=2>; rel="next", contributors?page=3>; rel="last"
    // ...

    View Slide

  107. Dynamic URL
    Response> response = call.execute();
    // HTTP/1.1 200 OK
    // Link: page=2>; rel="next", contributors?page=3>; rel="last"
    // ...
    String links = response.headers().get("Link");

    View Slide

  108. Dynamic URL
    Response> response = call.execute();
    // HTTP/1.1 200 OK
    // Link: page=2>; rel="next", contributors?page=3>; rel="last"
    // ...
    String links = response.headers().get("Link");
    String nextLink = nextFromGitHubLinks(links);

    View Slide

  109. Dynamic URL
    Response> response = call.execute();
    // HTTP/1.1 200 OK
    // Link: page=2>; rel="next", contributors?page=3>; rel="last"
    // ...
    String links = response.headers().get("Link");
    String nextLink = nextFromGitHubLinks(links);
    // https://api.github.com/repositories/892275/contributors?page=2

    View Slide

  110. Dynamic URL
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }X

    View Slide

  111. Dynamic URL
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    @GET

    Call> repoContributorsPaginate(

    @Url String url);

    }X

    View Slide

  112. Dynamic URL
    String nextLink = nextFromGitHubLinks(links);
    // https://api.github.com/repositories/892275/contributors?page=2

    View Slide

  113. Dynamic URL
    String nextLink = nextFromGitHubLinks(links);
    // https://api.github.com/repositories/892275/contributors?page=2
    Call> nextCall =
    gitHubService.repoContributorsPaginate(nextLink);

    View Slide

  114. Retrofit 2
    • Call encapsulates single request/response interaction
    • Parameterized Response object
    • Dynamic URL parameter
    • Multiple, efficient converters

    View Slide

  115. Converters
    interface SomeProtoService {

    @GET("/some/proto/endpoint")

    Call someProtoEndpoint();
    }X
    interface SomeJsonService {

    @GET("/some/json/endpoint")

    Call someJsonEndpoint();

    }X

    View Slide

  116. Converters
    interface SomeService {

    @GET("/some/proto/endpoint")

    Call someProtoEndpoint();

    @GET("/some/json/endpoint")

    Call someJsonEndpoint();

    }X


    SomeProtoResponse

    View Slide

  117. Converters
    interface SomeService {

    @GET("/some/proto/endpoint")

    Call someProtoEndpoint();

    @GET("/some/json/endpoint")

    Call someJsonEndpoint();

    }X
    SomeProtoResponse

    View Slide

  118. Converters
    interface SomeService {

    @GET("/some/proto/endpoint")

    Call someProtoEndpoint();

    @GET("/some/json/endpoint")

    Call someJsonEndpoint();

    }X
    SomeProtoResponse Proto? Yes!

    View Slide

  119. Converters
    interface SomeService {

    @GET("/some/proto/endpoint")

    Call someProtoEndpoint();

    @GET("/some/json/endpoint")

    Call someJsonEndpoint();

    }X
    SomeJsonResponse

    View Slide

  120. Converters
    interface SomeService {

    @GET("/some/proto/endpoint")

    Call someProtoEndpoint();

    @GET("/some/json/endpoint")

    Call someJsonEndpoint();

    }X
    SomeJsonResponse Proto? No!

    View Slide

  121. Converters
    interface SomeService {

    @GET("/some/proto/endpoint")

    Call someProtoEndpoint();

    @GET("/some/json/endpoint")

    Call someJsonEndpoint();

    }X
    SomeJsonResponse Proto? No! JSON? Yes!

    View Slide

  122. Retrofit 2
    • Call encapsulates single request/response interaction
    • Parameterized Response object
    • Dynamic URL parameter
    • Multiple, efficient converters
    • Multiple, pluggable execution mechanisms

    View Slide

  123. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }X

    View Slide

  124. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(

    @Path("owner") String owner,

    @Path("repo") String repo);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }X

    View Slide

  125. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Call

    View Slide

  126. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Call

    View Slide

  127. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Call RxJava? No!

    View Slide

  128. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Call RxJava? No! Call? Yes!

    View Slide

  129. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Observable

    View Slide

  130. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Observable RxJava? Yes!

    View Slide

  131. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Future

    View Slide

  132. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Future RxJava? No!

    View Slide

  133. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Future RxJava? No! Call? No!

    View Slide

  134. Execution Mechanisms
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Future> repoContributors3(..);

    }X
    Future RxJava? No! Call? No! Throw!

    View Slide

  135. Retrofit 2
    • Call encapsulates single request/response interaction
    • Parameterized Response object
    • Dynamic URL parameter
    • Multiple, efficient converters
    • Multiple, pluggable execution mechanisms

    View Slide

  136. Powered by OkHttp

    View Slide

  137. Powered by OkHttp
    • In 2012 we needed client abstractions.

    View Slide

  138. Powered by OkHttp
    • In 2012 we needed client abstractions.
    • In 2012 we needed request/response abstractions.

    View Slide

  139. Powered by OkHttp
    • In 2012 we needed client abstractions.
    • In 2012 we needed request/response abstractions.
    • In 2012 we needed header abstractions.

    View Slide

  140. Powered by OkHttp
    • In 2012 we needed client abstractions.
    • In 2012 we needed request/response abstractions.
    • In 2012 we needed header abstractions.
    • It's 2015. OkHttp is small, lean, focused, and full-featured.

    View Slide

  141. Powered by OkHttp

    View Slide

  142. Powered by OkHttp & Okio
    bit.ly/ok-libs

    View Slide

  143. Retrofit

    View Slide

  144. Retrofit OkHttp

    View Slide

  145. Retrofit OkHttp
    Socket

    View Slide

  146. Retrofit OkHttp
    BufferedSource
    BufferedSink
    Socket

    View Slide

  147. Retrofit OkHttp
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody
    Socket

    View Slide

  148. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody
    Socket

    View Slide

  149. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody
    Socket
    Moshi

    View Slide

  150. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody
    Socket
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  151. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    RequestBody
    ResponseBody
    Socket
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  152. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  153. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  154. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  155. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  156. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  157. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  158. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  159. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  160. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  161. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  162. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  163. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  164. Retrofit OkHttp
    MoshiConverter
    BufferedSource
    BufferedSink
    RequestBody
    ResponseBody

    MyService
    RequestBody
    ResponseBody
    Socket
    execute()
    myEndpoint()
    Moshi
    BufferedSink
    BufferedSource

    View Slide

  165. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();

    View Slide

  166. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  167. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    GitHubService gitHubService = retrofit.create(GitHubService.class);

    View Slide

  168. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    // https://api.github.com/repos/square/retrofit/contributors

    View Slide

  169. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    // https://api.github.com/repos/square/retrofit/contributors
    HttpUrl

    View Slide

  170. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    // https://api.github.com/repos/square/retrofit/contributors
    HttpUrl
    .resolve()

    View Slide

  171. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  172. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/v3/")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  173. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/v3/")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  174. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/v3/")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    // https://api.github.com/repos/square/retrofit/contributors

    View Slide

  175. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/v3/")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    // https://api.github.com/repos/square/retrofit/contributors
    HttpUrl
    .resolve()

    View Slide

  176. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/v3/")
    .build();
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }X

    View Slide

  177. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/v3/")
    .build();
    interface GitHubService {

    @GET("repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }X

    View Slide

  178. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/v3/")
    .build();
    interface GitHubService {

    @GET("repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }

    View Slide

  179. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/v3/")
    .build();
    interface GitHubService {

    @GET("repos/{owner}/{repo}/contributors")

    Call> repoContributors(

    @Path("owner") String owner,

    @Path("repo") String repo);

    }
    // https://api.github.com/v3/repos/square/retrofit/contributors

    View Slide

  180. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();

    View Slide

  181. Setup
    OkHttpClient client = new OkHttpClient();
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .client(client)
    .build();

    View Slide

  182. Setup
    OkHttpClient client = new OkHttpClient();
    client.interceptors().add(..);
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .client(client)
    .build();

    View Slide

  183. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();

    View Slide

  184. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

    View Slide

  185. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(GsonConverterFactory.create())
    .addConverterFactory(ProtoConverterFactory.create())
    .build();

    View Slide

  186. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(ProtoConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .build();

    View Slide

  187. Setup
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(ProtoConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .build();

    View Slide

  188. Extensibility
    • Converter.Factory
    • CallAdapter.Factory

    View Slide

  189. Converter.Factory
    interface SomeService {

    @GET("/some/proto/endpoint")

    Call someProtoEndpoint();

    @GET("/some/json/endpoint")

    Call someJsonEndpoint();

    }X
    SomeJsonResponse Proto? No! JSON? Yes!

    View Slide

  190. Converter.Factory
    interface Converter {
    interface Factory {
    Converter> create(Type type);
    }
    }X
    SomeJsonResponse Proto? No! JSON? Yes!

    View Slide

  191. Converter.Factory
    SomeJsonResponse Proto? No! JSON? Yes!

    View Slide

  192. Converter.Factory
    class ProtoConverterFactory {
    Converter> create(Type type);
    }X
    SomeJsonResponse
    null No!
    JSON? Yes!

    View Slide

  193. JSON
    Converter.Factory
    class ProtoConverterFactory {
    Converter> create(Type type);
    }X
    SomeJsonResponse
    null No!
    Converter> Yes!
    class GsonConverterFactory {
    Converter> create(Type type);
    }X

    View Slide

  194. Converter.Factory
    interface Converter {
    interface Factory {
    Converter> create(Type type);
    }X
    }X

    View Slide

  195. Converter.Factory
    interface Converter {
    interface Factory {
    Converter> create(Type type);
    }X
    T fromBody(ResponseBody body);
    RequestBody toBody(T value);
    }X

    View Slide

  196. Extensibility
    • Converter.Factory
    • CallAdapter.Factory

    View Slide

  197. CallAdapter.Factory
    interface GitHubService {

    @GET("/repos/{owner}/{repo}/contributors")

    Call> repoContributors(..);

    @GET("/repos/{owner}/{repo}/contributors")

    Observable> repoContributors2(..);

    }X
    Call RxJava? No! Call? Yes!

    View Slide

  198. CallAdapter.Factory
    interface CallAdapter {
    interface Factory {
    CallAdapter> create(Type type);
    }

    }X
    Call RxJava? No! Call? Yes!

    View Slide

  199. CallAdapter.Factory
    Call RxJava? No! Call? Yes!

    View Slide

  200. CallAdapter.Factory
    Call Call? Yes!
    class RxJavaCallAdapterFactory {
    CallAdapter> create(Type type);
    }X
    null No!

    View Slide

  201. CallAdapter.Factory
    Call
    class RxJavaCallAdapterFactory {
    CallAdapter> create(Type type);
    }X
    null No!
    JSON
    CallAdapter> Yes!
    class CallAdapterFactory {
    CallAdapter> create(Type type);
    }X

    View Slide

  202. CallAdapter.Factory
    interface CallAdapter {
    interface Factory {
    CallAdapter> create(Type type);
    }X
    }X

    View Slide

  203. CallAdapter.Factory
    interface CallAdapter {
    interface Factory {
    CallAdapter> create(Type type);
    }X
    Type responseType();
    Object adapt(Call value);
    }X

    View Slide

  204. Extensibility
    • Converter.Factory
    • CallAdapter.Factory

    View Slide

  205. Under Construction

    View Slide

  206. Under Construction
    • Parameter handlers

    View Slide

  207. Under Construction
    • Parameter handlers
    • Logging?

    View Slide

  208. Under Construction
    • Parameter handlers
    • Logging?
    • Finalizing mock module

    View Slide

  209. Under Construction
    • Parameter handlers
    • Logging?
    • Finalizing mock module
    • Documentation

    View Slide

  210. Under Construction
    • Parameter handlers
    • Logging?
    • Finalizing mock module
    • Documentation
    • WebSockets! (in v2.1)

    View Slide

  211. Release?

    View Slide

  212. Release?
    dependencies {

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

    View Slide

  213. jakewharton
    jakewharton
    twitter.com/
    google.com/+
    .com
    Retrofit Two
    jakewharton

    View Slide