Build an efficient REST Client on Android - Droidcon IT 2016 - Matteo Gazzurelli2. © 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?
3. © DuckMa 2016 - www.duckma.com
A Good
REST Client
THE MAIN INGREDIENTS OF A FAST WEB API
3
CachingHttp 2
4. © 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
5. © DuckMa 2016 - www.duckma.com
CACHING
5
• Caching System (Good for Dynamic APIs)
• APC
• Varnish
• Memcache
6. © DuckMa 2016 - www.duckma.com
CACHING
6
• HTTP Headers (Good for Static APIs)
• Expires
• Cache-Control
• Entity Tags
• Last-Modified
7. © 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.
8. © 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
9. © DuckMa 2016 - www.duckma.com
REST (HTTP) CLIENTS IN ANDROID
9
• DefaultHttpClient / HttpUrlConnection
• Volley
• Spring
• Retrofit
• …
10. © 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.
11. © 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)
12. © 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
13. © 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;
}
14. © 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();
}
15. © 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) {}
16. © 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);
17. © 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
18. © 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
19. © 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.
20. © 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
21. © 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();
22. © 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();
23. © DuckMa 2016 - www.duckma.com
TWO LEVEL OF CACHING
23
• Soft Caching (Network level)
• Persistent Caching (Database)
24. © 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.
25. © 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
26. © DuckMa 2016 - www.duckma.com
PERSISTENT CACHING
26
Various Android Storage Options:
•Shared Preferences
•Internal/External Storage
•Sqlite
•Realm.io
27. © 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
28. © 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.
29. © 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.
30. © 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.
31. © 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();
}
}
32. © DuckMa 2016 - www.duckma.com
BACKEND? APPENGINE
32
Easy and fast way to create http2 backend
https://console.developers.google.com.
33. © 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
34. © 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
35. © DuckMa 2016 - www.duckma.com
DEMO
35
https://github.com/duckma/Rest-Client-Demo
• Retrofit
• Gson
• Interceptor
• OkHTTP
• Caching
• realm.io
36. © DuckMa 2016 - www.duckma.com
DEMO
36
Download app: duk.ma/droidconit2016
37. © 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
38. © 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();
}
39. © 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();
40. © 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();
41. © 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;
}
42. © 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
43. © 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();
44. © 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 FranciscoBrescia
45. © DuckMa 2016 - www.duckma.com
THANK YOU
45
We are hiring!
duk.ma/jobpositions