Slide 1

Slide 1 text

Thanks for coming!

Slide 2

Slide 2 text

About me Federico Paolinelli [email protected] mytechaddiction.blogspot.com @fedepaol plus.google.com/+FedericoPaolinelli

Slide 3

Slide 3 text

Let’s talk about REST interaction

Slide 4

Slide 4 text

Resources….

Slide 5

Slide 5 text

… you want to access

Slide 6

Slide 6 text

… you want to access http(s)

Slide 7

Slide 7 text

Http call (*) URL url = new URL(“https://api.github.com/users/fedepaol/events");! ! HttpURLConnection conn = (HttpURLConnection) url.openConnection();! ! InputStream in = new BufferedInputStream(conn.getInputStream());! // do whatever you want with the stream! conn.disconnect();! (*)Try/Catches not included

Slide 8

Slide 8 text

What are our goals?

Slide 9

Slide 9 text

Making our apps work like magic

Slide 10

Slide 10 text

… or at least • providing a good user experience • being efficient in terms of power / bandwidth

Slide 11

Slide 11 text

DON’TS (it would be better not to…)

Slide 12

Slide 12 text

Antipatterns • Performing the call on the ui thread

Slide 13

Slide 13 text

Antipatterns • Performing the call on the ui thread • Hosting the thread that perform the call in the activity (or fragment)

Slide 14

Slide 14 text

Antipatterns • Performing the call on the ui thread • Hosting the thread that perform the call in the activity (or fragment) • Storing the results (only) in memory

Slide 15

Slide 15 text

RULE #1 Decouple the UI from the http call

Slide 16

Slide 16 text

Decoupling the call from the UI • The activity might be killed

Slide 17

Slide 17 text

Decoupling the call from the UI • The activity might be killed • Config changes handling

Slide 18

Slide 18 text

Decoupling the call from the UI • The activity might be killed • Config changes handling • Can’t schedule requests

Slide 19

Slide 19 text

Decoupling the call from the UI • The activity might be killed • Config changes handling • Can’t schedule requests • Harder to test

Slide 20

Slide 20 text

So where am I supposed to perform the call?

Slide 21

Slide 21 text

Service A facility for the application to tell the system about something it wants to be doing in the background (even when the user is not directly interacting with the application)

Slide 22

Slide 22 text

How to tell the service to initiate a request Activity Service startService() Intent onStartCommand()

Slide 23

Slide 23 text

How to tell the service to initiate a request (2) Activity Service bindService() bind() ServiceInterface onBind()

Slide 24

Slide 24 text

CONS PROS Services pros / cons • Can run in background • Can be scheduled • When bound, offers a rich interface • Needs to be shut down • Needs to perform requests in a different thread

Slide 25

Slide 25 text

IntentService or how to kill two birds with one stone

Slide 26

Slide 26 text

IntentService • Performs the request inside a different thread • Dies when onHandleIntent returns • Queued • Command pattern protected void onHandleIntent(Intent intent) {! String myParameter = intent.getExtras().getString(…);! ! // Handle your request here! }

Slide 27

Slide 27 text

Activity Service Request

Slide 28

Slide 28 text

Where should I store the result data?

Slide 29

Slide 29 text

Where to store the data • Reloading the same data has a cost • Decoupled from the ui • Data can be accessed when offline/prefetched Persistent Storage SQLite Content Provider

Slide 30

Slide 30 text

Storage Use Transactions (and yieldIfContededSafely()) ! bulkInsert() with content providers ! Load only New data if the api offers a “since” parameter

Slide 31

Slide 31 text

Activity Service Request Storage

Slide 32

Slide 32 text

Notify the UI of the result of the request one last step..

Slide 33

Slide 33 text

Notify the UI • Local Broadcasts ! ! • Bound Interface (not available with intent services) ! • EventBus (Otto, GreenRobot’s EventBus) ! Intent intent = new Intent(Constants.SERVER_REQUEST_DONE);! intent.putExtra(Constants.REQUEST_ID, mRequestId);! intent.putExtra(Constants.IGNORE_PENDING_ID, mIgnorePending);! LocalBroadcastManager.getInstance(c).sendBroadcast(intent);

Slide 34

Slide 34 text

Notify the UI when using ContentProviders • Register observers • CursorLoader gets notified and reloads the cursor @Override! public Cursor query(Uri uri, String[] projection, String selection,! String[] selectionArgs, String sortOrder) {! // perform the query! cursor.setNotificationUri(getContext().getContentResolver(), uri);! return cursor;! }! ! @Override! public Uri insert(Uri uri, ContentValues values) {! // ..! getContext().getContentResolver().notifyChange(insertedId, null);! // ..! }!

Slide 35

Slide 35 text

Activity Service Request Storage Notify Update

Slide 36

Slide 36 text

When?

Slide 37

Slide 37 text

When to fetch the data React to user input ! Scheduled prefetch (using AlarmManager) ! Push from the server ( GCM)

Slide 38

Slide 38 text

Big cookie approach CommonsCreative: http://www.flickr.com/photos/shellewill79/5333263261/

Slide 39

Slide 39 text

Big cookie approach • The cell radio is one of the biggest battery drains on the phone • It takes up to 2 seconds to turn the radio on • Every time you transfer data, the radio is powered on for nearly 30 seconds

Slide 40

Slide 40 text

Off the shelf solutions

Slide 41

Slide 41 text

SyncAdapters • Designed to be used together with Content Providers and Account Authenticator • Native retry mechanism and exponential backoff • Check network availability • Sync with other apps • (Were) a bit tricky to setup http://developer.android.com/training/sync-adapters/

Slide 42

Slide 42 text

Robospice • Service decouples the interaction from the ui • Very high level, strongly typed • A lot of features such as caching, json parsing, prioritizing • Retry policy • Very active development https://github.com/octo-online/robospice

Slide 43

Slide 43 text

Others.. • Volley • DataDroid (https://github.com/foxykeep/datadroid) • Ion (https://github.com/koush/ion) • Retrofit (http://square.github.io/retrofit)

Slide 44

Slide 44 text

PostmanLib CommonsCreative: http://www.flickr.com/photos/workfromtheheart/5881426342/

Slide 45

Slide 45 text

PostmanLib • Allows asynchronous requests • Built on top of java scribe library • Tracks the state of pending requests https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 46

Slide 46 text

Implement a RestRequest interface public class PostmanRequest implements RestServerRequest {! @Override! public String getUrl() {! return "https://api.github.com/users/fedepaol/events";! }! ! @Override! public Verb getVerb() {! return Verb.GET;! }! ! @Override! public void onHttpResult(Response result, ! int statusCode, ! RequestExecutor executor,! Context context) throws ResultParseException {! String resultToParse = result.getBody();! // ...! }! } https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 47

Slide 47 text

Implement a RestRequest interface public class PostmanRequest implements RestServerRequest {! @Override! public String getUrl() {! return "https://api.github.com/users/fedepaol/events";! }! ! @Override! public Verb getVerb() {! return Verb.GET;! }! ! @Override! public void onHttpResult(Response result, ! int statusCode, ! RequestExecutor executor,! Context context) throws ResultParseException {! String resultToParse = result.getBody();! // ...! }! } https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 48

Slide 48 text

Implement a RestRequest interface public class PostmanRequest implements RestServerRequest {! @Override! public String getUrl() {! return "https://api.github.com/users/fedepaol/events";! }! ! @Override! public Verb getVerb() {! return Verb.GET;! }! ! @Override! public void onHttpResult(Response result, ! int statusCode, ! RequestExecutor executor,! Context context) throws ResultParseException {! String resultToParse = result.getBody();! // ...! }! } https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 49

Slide 49 text

Send a request and track the result PostmanRequest r = new PostmanRequest();! PostmanRequest r1 = new PostmanRequest();! ServerInteractionHelper.getInstance(this).sendRestAction(this, "ReqID", r, r1);! ! ! ! ! @Override! protected void onResume() {! mHelper.registerEventListener(this, this);! if (mHelper.isRequestAlreadyPending("ReqID")) {! // notify that a request is still pending, ie action bar spinner! }! }! ! @Override! public void onServerResult(String result, String requestId) {! if ("MyRequestID".equals(requestId)) {! // update the ui! }! https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 50

Slide 50 text

Send a request and track the result PostmanRequest r = new PostmanRequest();! PostmanRequest r1 = new PostmanRequest();! ServerInteractionHelper.getInstance(this).sendRestAction(this, "ReqID", r, r1);! ! ! ! ! @Override! protected void onResume() {! mHelper.registerEventListener(this, this);! if (mHelper.isRequestAlreadyPending("ReqID")) {! // notify that a request is still pending, ie action bar spinner! }! }! ! @Override! public void onServerResult(String result, String requestId) {! if ("MyRequestID".equals(requestId)) {! // update the ui! }! https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 51

Slide 51 text

Send a request and track the result PostmanRequest r = new PostmanRequest();! PostmanRequest r1 = new PostmanRequest();! ServerInteractionHelper.getInstance(this).sendRestAction(this, "ReqID", r, r1);! ! ! ! ! @Override! protected void onResume() {! mHelper.registerEventListener(this, this);! if (mHelper.isRequestAlreadyPending("ReqID")) {! // notify that a request is still pending, ie action bar spinner! }! }! ! @Override! public void onServerResult(String result, String requestId) {! if ("MyRequestID".equals(requestId)) {! // update the ui! }! https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 52

Slide 52 text

PostmanLib • Can batch / chain requests • Does not provide storage / caching • Does not provide scheduling out of the box • Can get a PendingIntent out of a request to schedule • Requests need to implement Parcelable https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 53

Slide 53 text

PostmanLib UI Helper IntentService(s) sendRestAction() Intent Execute() onServerResult() LocalBroadcast https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 54

Slide 54 text

Yes, but I came here to hear of OAuth

Slide 55

Slide 55 text

OAuth • Allows to access resources owned by the user without sharing credentials with the app • Used by nearly all the rest apis • User can revoke the permissions

Slide 56

Slide 56 text

OAuth All we need is an Access Token

Slide 57

Slide 57 text

Get a client id and a client secret

Slide 58

Slide 58 text

OAuth 1.0(a) CommonsCreative: http://www.flickr.com/photos/venndiagram/5331296718 Doesn’t look easy

Slide 59

Slide 59 text

OAuth 1.0(a)

Slide 60

Slide 60 text

OAuth 1.0(a) - SignPost • Lightweight • Does not wrap http calls • Easy to use ! http://code.google.com/p/oauth-signpost/

Slide 61

Slide 61 text

OAuth 2.0

Slide 62

Slide 62 text

Java Scribe • Uses HttpUrlConnection under the hood • Comes with a lot of api support included • Supports OAuth 1.0 and OAuth 2.0 • Does not expose the HttpUrlConnection outside https://github.com/fernandezpablo85/scribe-java

Slide 63

Slide 63 text

At some point, the user needs to interact with the service …

Slide 64

Slide 64 text

How to get the Auth Code use the System Browser ! open a WebView ! use a Custom Activity

Slide 65

Slide 65 text

PostmanLib uses Scribe under the hood

Slide 66

Slide 66 text

PostmanLib Register an oauth service private void setAuthService() {! OAuthHelper o = OAuthHelper.getInstance();! if (!o.isAlreadyAuthenticated("Facebook", this)) {! StorableServiceBuilder builder = new ! StorableServiceBuilder("Facebook")! .provider(FacebookApi.class)! .apiKey("xxxxxxxx")! .apiSecret("yyyyyyyyyy")! .callback("http://www.yoururl.com/callback", "code")! .scope("publish_actions");! ! o.registerOAuthService(builder, this);! }! } https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 67

Slide 67 text

PostmanLib Obtain the access token OAuthHelper o = OAuthHelper.getInstance();! if (!o.isAlreadyAuthenticated("Facebook", this)) {! o.authenticate(this, "Facebook");! } https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 68

Slide 68 text

PostmanLib Perform the request public class ShareOnFacebookAction implements RestServerRequest {! private static final String url = "https://graph.facebook.com/me/feed";! ! @Override! public String getOAuthSigner() {! return "Facebook";! }! // . . .! } https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

Slide 69

Slide 69 text

Thanks Federico Paolinelli @fedepaol [email protected]