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

Build an efficient REST Client on Android - Droidcon IT 2016 - Matteo Gazzurelli

Build an efficient REST Client on Android - Droidcon IT 2016 - Matteo Gazzurelli

It is important to limit and optimise network traffic in mobile communications.
A well designed rest client is the best you can do in order to save network bandwidth and increase your online app responsiveness.
This talk is focused on how to create an optimal rest client with persistent cache in Android using HTTP/2, Retrofit, OkHttp and Realm while relying on an AppEngine hosted backend.

Link: http://it.droidcon.com/2016/sessions/build-an-efficient-rest-client-on-android/

Matteo Gazzurelli

April 08, 2016
Tweet

Other Decks in Technology

Transcript

  1. © DuckMa 2016 - www.duckma.com 2 Have you ever parsed

    a JSON string by hand? Do you still use plain HttpUrlConnection to connect to a server? Do you have an app that consumes a REST endpoint?
  2. © DuckMa 2016 - www.duckma.com A Good
 REST Client THE

    MAIN INGREDIENTS OF A FAST WEB API 3 Caching Http 2
  3. © DuckMa 2016 - www.duckma.com HTTP 2 4 • Based

    on Google’s SPDY • Supported by most browsers • goals: reducing web page load latency and improving web security https://http2.github.io/faq
  4. © DuckMa 2016 - www.duckma.com CACHING 5 • Caching System

    (Good for Dynamic APIs) • APC • Varnish • Memcache
  5. © DuckMa 2016 - www.duckma.com CACHING 6 • HTTP Headers

    (Good for Static APIs) • Expires • Cache-Control • Entity Tags • Last-Modified
  6. © DuckMa 2016 - www.duckma.com WHAT IS REST? 7 •

    Representational State Transfer (REST) • REST is an architectural style consisting of a coordinated set of architectural constraints applied to components, connectors, and data elements • REST is platform and language independent.
  7. © DuckMa 2016 - www.duckma.com REST KEY PRINCIPLE 8 •

    Give every “thing” an ID • Link things together • Use standard methods • Resources with multiple representations • Communicate statelessly http://www.infoq.com/articles/rest-introduction
  8. © DuckMa 2016 - www.duckma.com REST (HTTP) CLIENTS IN ANDROID

    9 • DefaultHttpClient / HttpUrlConnection • Volley • Spring • Retrofit • …
  9. © DuckMa 2016 - www.duckma.com WHAT IS RETROFIT 10 Type-safe

    HTTP client for Android and Java by Square, Inc. Uses annotations to describe HTTP requests and URL parameter replacement Retrofit turns your REST API into a Java interface.
  10. © DuckMa 2016 - www.duckma.com RETROFIT CONVERTERS 11 Converters can

    be added to support other types. • Gson • Jackson • Moshi • Protobuffer • Wire • Simple XML • Scalars (primitives, boxed, and String)
  11. © DuckMa 2016 - www.duckma.com RETROFIT 101 12 •Create a

    POJO/JavaBean Model •Create Retrofit Interface •Create the Call object •Execute the request •Use the result
  12. © DuckMa 2016 - www.duckma.com RETROFIT MODEL EXAMPLE 13 public

    class Content { String id; String title; String image; public String getId() { return id; } public void setId(String id) { this.id = id; }
  13. © DuckMa 2016 - www.duckma.com RETROFIT INTERFACE EXAMPLE 14 public

    interface AppAPI { @GET("/") @Headers({"Cache-Control: public, only-if-cached, max- stale=2147483647"}) Call<ContentResponse> loadCachedContents(); }
  14. © DuckMa 2016 - www.duckma.com RETROFIT CALL EXAMPLE 15 mAppApi

    = retrofit.create(AppAPI.class); Call<ContentResponse> call = mAppApi.loadContents(); call.enqueue(this); @Override public void onResponse(Call<ContentResponse> call, Response<ContentResponse> response) {} @Override public void onFailure(Call<ContentResponse> call, Throwable t) {}
  15. © DuckMa 2016 - www.duckma.com RETROFIT (A)SYNC CALLS 16 If

    you want to execute a synchronous call just replace call.enqueue() with call.execute() remember that you have to perform this outside the main thread To cancel a running request call.cancel(); calls can only be used once but you can easily clone them Call<ContentResponse> c = call.clone(); c.enqueue(this);
  16. © DuckMa 2016 - www.duckma.com WHAT’S NEW IN RETROFIT 2

    17 • Package with New Version (avoid conflicts) • New Service Declaration • A way to cancel the ongoing transaction • New Url resolving concept • OkHttp is now required
  17. © DuckMa 2016 - www.duckma.com WHAT IS OKHTTP 18 OkHttp

    is an HTTP client that’s efficient by default: • HTTP/2 support allows all requests to the same host to share a socket. • Connection pooling reduces request latency (if HTTP/2 isn’t available). • Transparent GZIP shrinks download sizes. • Response caching avoids the network completely for repeat requests
  18. © DuckMa 2016 - www.duckma.com OKHTTP - NOT ONLY FOR

    RETROFIT 19 Can be used as http client for other libraries: • Picasso • Glide • Fresco Okio Dependent: Okio is a new library that complements java.io and java.nio to make it much easier to access, store, and process your data.
  19. © DuckMa 2016 - www.duckma.com INTERCEPTORS 20 Powerful mechanism that

    can monitor, rewrite, and retry calls. Interceptors can be chained and are called in order. https://github.com/square/okhttp/wiki/Interceptors
  20. © DuckMa 2016 - www.duckma.com RETROFIT AND OKHTTP INTERCEPTOR 21

    OkHttpClient client = new OkHttpClient(); client.interceptors().add(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Response response = chain.proceed(chain.request()); return response; } }); Retrofit retrofit = new Retrofit.Builder() .baseUrl(“http://test-project-json.appspot.com”) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build();
  21. © DuckMa 2016 - www.duckma.com LOGGING 22 Latest version of

    OkHttp ships with a logging interceptor compile 'com.squareup.okhttp3:logging-interceptor:3.2.0' HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(interceptor).build();
  22. © DuckMa 2016 - www.duckma.com TWO LEVEL OF CACHING 23

    • Soft Caching (Network level) • Persistent Caching (Database)
  23. © DuckMa 2016 - www.duckma.com SOFT CACHING WITH OKHTTP 24

    •OkHttp as caching bucket •Response caching uses HTTP headers for all configuration. •There are cache headers to force a cached response, force a network response, or force the network response to be validated with a conditional GET.
  24. © DuckMa 2016 - www.duckma.com SOFT CACHING WITH OKHTTP 25

    OkHttpClient.Builder okBuilder = new OkHttpClient.Builder(); Cache cache = new Cache(new File(getCacheDir(), CACHE_DIR), CACHE_SIZE); okBuilder.cache(cache); Private cache directory
  25. © DuckMa 2016 - www.duckma.com PERSISTENT CACHING 26 Various Android

    Storage Options: •Shared Preferences •Internal/External Storage •Sqlite •Realm.io
  26. © DuckMa 2016 - www.duckma.com REALM.IO 27 Realm is a

    replacement for SQLite • Uses little resources • Easy to use • Fast • Cross-Platform (C++) • Advanced https://realm.io/docs/java/latest/#getting-started
  27. © DuckMa 2016 - www.duckma.com REALM IS FAST 28 Tests

    run on an Galaxy S3, using the latest available version of each library as of Sept 28, 2014.
  28. © DuckMa 2016 - www.duckma.com REALM LIST / PROXY CLASSES

    29 The RealmProxy classes are our way of making sure that the Realm object doesn’t contain any data itself, but instead access the data directly in the database.
  29. © DuckMa 2016 - www.duckma.com REALM WITH RETROFIT 30 https://realm.io/docs/java/latest/#retrofit

    Retrofit does not automatically add objects to Realm, instead you must manually add them using the realm.copyToRealm() or realm.copyToRealmOrUpdate() methods.
  30. © DuckMa 2016 - www.duckma.com REALM WITH RETROFIT 31 @Override

    public void onResponse(Call<ContentResponse> call, Response<ContentResponse> response) { if (response.isSuccessful()) { mRealm.beginTransaction(); mRealm.copyToRealm(mContentsArray); mRealm.commitTransaction(); } }
  31. © DuckMa 2016 - www.duckma.com BACKEND? APPENGINE 32 Easy and

    fast way to create http2 backend https://console.developers.google.com.
  32. © DuckMa 2016 - www.duckma.com APPENGINE - FLASK (PYTHON) 33

    @app.route('/') def returnJson(): folder = 'json/app.json' f = open(folder,'r') resp = Response(response=f.read(), status=200, mimetype="application/json") return resp Create a static json file to be served
  33. © DuckMa 2016 - www.duckma.com BACKEND? 34 http://blog.duckma.com/2016/01/how-to-create- simple-project-in-google.html If

    you want to start using Google App Engine, Start Here: https://console.cloud.google.com/start/appengine
  34. © DuckMa 2016 - www.duckma.com DEMO SOFT CACHE 37 •

    D/OkHttp: --> GET http://test-project-json.appspot.com/ • D/OkHttp: --> END GET • D/OkHttp: <-- 200 OK http://test-project-json.appspot.com/ (322ms) • D/OkHttp: <-- END HTTP • D/OkHttp: --> GET http://test-project-json.appspot.com/ • D/OkHttp: Cache-Control: public, only-if-cached, max-stale=2147483647 • D/OkHttp: --> END GET • D/OkHttp: <-- 200 OK http://test-project-json.appspot.com/ (6ms) • D/OkHttp: Warning: 110 HttpURLConnection "Response is stale" • D/OkHttp: <-- END HTTP
  35. © DuckMa 2016 - www.duckma.com RETROFIT INTERFACE EXAMPLE 38 public

    interface AppAPI { String FORCE_CACHE_HEADERS = "Cache-Control: public, only-if-cached, max-stale=" + Integer.MAX_VALUE; @GET("/") Call<ContentResponse> loadContents(); @GET("/") @Headers({FORCE_CACHE_HEADERS}) Call<ContentResponse> loadCachedContents(); }
  36. © DuckMa 2016 - www.duckma.com DEMO PERSISTENT CACHE 39 @Override

    public void onFailure(Call<ContentResponse> call, Throwable t) { RealmResults<Content> results = mRealm.where(Content.class).findAll(); mContentsArray.addAll(results); mAdapter.notifyDataSetChanged();
  37. © DuckMa 2016 - www.duckma.com SAVE DATA IN REALM 40

    @Override public void onResponse(Call<ContentResponse> call, Response<ContentResponse> response) { mContentsArray.clear(); if (response.isSuccessful()) { mContentsArray.addAll(response.body().getContents()); mRealm.beginTransaction(); mRealm.copyToRealm(mContentsArray); mRealm.commitTransaction();
  38. © DuckMa 2016 - www.duckma.com CONTENT MODEL 41 public class

    Content extends RealmObject { String id; String title; String image; public String getId() { return id; } public void setId(String id) { this.id = id; }
  39. © DuckMa 2016 - www.duckma.com GSON CONVERTER 42 Gson gson

    = new GsonBuilder() .setExclusionStrategies(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { return f.getDeclaringClass().equals(RealmObject.class); } @Override public boolean shouldSkipClass(Class<?> clazz) { return false; } }) .create(); https://code.google.com/p/google-gson/issues/detail?id=440
  40. © DuckMa 2016 - www.duckma.com RETROFIT CLIENT WITH OKHTTP 43

    OkHttpClient.Builder okBuilder = new OkHttpClient.Builder(); Cache cache = new Cache(new File(getCacheDir(), CACHE_DIR), CACHE_SIZE); okBuilder.cache(cache); HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(); logInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS); okBuilder.addInterceptor(logInterceptor); Retrofit retrofit = new Retrofit.Builder() .baseUrl(Config.ENDPOINT) .addConverterFactory(GsonConverterFactory.create(gson)) .client(okBuilder.build()) .build();
  41. © DuckMa 2016 - www.duckma.com WHO AM I ? 44

    Matteo Gazzurelli CEO DuckMa DuckMa is a mobile focused design and software boutique primarily working with startups and leading brands. We are a group of passionate professionals who make handcrafted mobile native apps. San Francisco Brescia