(O)Authenticated rest interaction in Android

(O)Authenticated rest interaction in Android

A talk about rest interaction and oauth in android I gave in Droidcon Italy 2014. More details can be found here http://mytechaddiction.blogspot.it/2014/02/rest-interaction-in-android.html

9e13b6114dfd38952b3699dd779c6958?s=128

Federico Paolinelli

February 07, 2014
Tweet

Transcript

  1. Thanks for coming!

  2. About me Federico Paolinelli fedepaol@gmail.com mytechaddiction.blogspot.com @fedepaol plus.google.com/+FedericoPaolinelli

  3. Let’s talk about REST interaction

  4. Resources….

  5. … you want to access

  6. … you want to access http(s)

  7. 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
  8. What are our goals?

  9. Making our apps work like magic

  10. … or at least • providing a good user experience

    • being efficient in terms of power / bandwidth
  11. DON’TS (it would be better not to…)

  12. Antipatterns • Performing the call on the ui thread

  13. Antipatterns • Performing the call on the ui thread •

    Hosting the thread that perform the call in the activity (or fragment)
  14. 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
  15. RULE #1 Decouple the UI from the http call

  16. Decoupling the call from the UI • The activity might

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

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

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

    be killed • Config changes handling • Can’t schedule requests • Harder to test
  20. So where am I supposed to perform the call?

  21. 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)
  22. How to tell the service to initiate a request Activity

    Service startService() Intent onStartCommand()
  23. How to tell the service to initiate a request (2)

    Activity Service bindService() bind() ServiceInterface onBind()
  24. 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
  25. IntentService or how to kill two birds with one stone

  26. 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! }
  27. Activity Service Request

  28. Where should I store the result data?

  29. 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
  30. Storage Use Transactions (and yieldIfContededSafely()) ! bulkInsert() with content providers

    ! Load only New data if the api offers a “since” parameter
  31. Activity Service Request Storage

  32. Notify the UI of the result of the request one

    last step..
  33. 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);
  34. 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);! // ..! }!
  35. Activity Service Request Storage Notify Update

  36. When?

  37. When to fetch the data React to user input !

    Scheduled prefetch (using AlarmManager) ! Push from the server ( GCM)
  38. Big cookie approach CommonsCreative: http://www.flickr.com/photos/shellewill79/5333263261/

  39. 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
  40. Off the shelf solutions

  41. 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/
  42. 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
  43. Others.. • Volley • DataDroid (https://github.com/foxykeep/datadroid) • Ion (https://github.com/koush/ion) •

    Retrofit (http://square.github.io/retrofit)
  44. PostmanLib CommonsCreative: http://www.flickr.com/photos/workfromtheheart/5881426342/

  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. PostmanLib UI Helper IntentService(s) sendRestAction() Intent Execute() onServerResult() LocalBroadcast https://github.com/fedepaol/PostmanLib--Rings-Twice--Android

  54. Yes, but I came here to hear of OAuth

  55. 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
  56. OAuth All we need is an Access Token

  57. Get a client id and a client secret

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

  59. OAuth 1.0(a)

  60. OAuth 1.0(a) - SignPost • Lightweight • Does not wrap

    http calls • Easy to use ! http://code.google.com/p/oauth-signpost/
  61. OAuth 2.0

  62. 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
  63. At some point, the user needs to interact with the

    service …
  64. How to get the Auth Code use the System Browser

    ! open a WebView ! use a Custom Activity
  65. PostmanLib uses Scribe under the hood

  66. 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
  67. 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
  68. 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
  69. Thanks Federico Paolinelli @fedepaol fedepaol@gmail.com