1 / 56

Best Practices for Accessing Google APIs on Android

Best Practices for Accessing Google APIs on Android. Yaniv Inbar May 10, 2011 Speaker Feedback: goo .gl/ EUnAo Twitter Hashtags : #io2011 # GoogleAPIs #Android. Google APIs Client Library for Java ( goo.gl/ZrqPu ). Now in Beta. New Google APIs. Android. Google Data APIs.

nova
Download Presentation

Best Practices for Accessing Google APIs on Android

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Best Practices for Accessing Google APIs on Android • YanivInbar • May 10, 2011 • Speaker Feedback: goo.gl/EUnAo • Twitter Hashtags: #io2011 #GoogleAPIs #Android

  2. Google APIs Client Library for Java (goo.gl/ZrqPu) • Now in Beta

  3. New Google APIs Android Google Data APIs Java 5 (SE, EE) Any REST APIs JSON XML Photos Videos Google App Engine OAuth 2.0 Client Login OAuth 1.0 Platforms APIs Data Formats Auth OUTLINE

  4. New Google APIs Android Google Data APIs Java 5 (SE, EE) Any REST APIs JSON XML Photos Videos Google App Engine OAuth 2.0 Client Login OAuth 1.0 Platforms APIs Data Formats Auth OUTLINE

  5. Platforms Java Platforms – Any Java Environment • Platform neutral • Works (mostly) the same in any Java environment • Pluggable • Pluggable HTTP library • Pluggable data format • Pluggable streaming JSON library • Pluggable streaming XML library • Pluggable Authentication

  6. Java 5 (SE, EE) HelloBuzz Example public class HelloBuzz { public static void main(String[] args) throws IOException { Buzz buzz = new Buzz(newNetHttpTransport(), new JacksonFactory()); ActivityFeed feed = buzz.activities.list("112550920411899420633", "@public").execute(); if (feed.items != null) for (Activity activity : feed.items) System.out.println(activity.buzzObject.content); } }

  7. Google App Engine HelloBuzzServlet Example public class HelloBuzzServletextends HttpServlet { @Override protected void doGet(HttpServletRequestreq, HttpServletResponseresp) throws IOException { resp.setContentType("text/plain"); PrintWriter writer = resp.getWriter(); Buzz buzz = new Buzz(newUrlFetchTransport(), new JacksonFactory()); ActivityFeed feed = buzz.activities.list("112550920411899420633", "@public").execute(); if (feed.items!= null) for (Activity activity : feed.items) writer.println(activity.buzzObject.content); } }

  8. Android HelloBuzzActivity Example public class HelloBuzzActivityextends ListActivity { @Override public void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); Buzz buzz = new Buzz(newNetHttpTransport(), new AndroidJsonFactory()); List<Spanned> activities = new ArrayList<Spanned>(); try { ActivityFeed feed = buzz.activities.list("112550920411899420633", "@public").execute(); if (feed.items!= null) for (Activity activity : feed.items) activities.add(Html.fromHtml(activity.buzzObject.content)); setListAdapter(newArrayAdapter<Spanned>(this, android.R.layout.simple_expandable_list_item_1, activities)); } catch (IOExceptione) {} } }

  9. Platforms Java Platforms – Pluggable HTTP library • UrlFetchTransport • Google App Engine only • NetHttpTransport • Based on java.net.HttpURLConnection • Included in Java & Android SDK • Preferred choice on Android since Gingerbread • ApacheTransport • Based on Apache HTTP Client • Included in Android SDK • Preferred choice for older Android SDK’s up to FroYo • AndroidHttp.newCompatibleTransport() • Picks NetHttpTransport or ApacheTransport based on SDK Level

  10. Platforms Java Platforms – HTTP Request Factory • Library provides request factory abstraction to provide hooks to: • Initialize all requests • Run code before executing a request • Retry unsuccessful requests • Example for Google Calendar: • void initializeRequestFactory() { • requestFactory = transport.createRequestFactory(newHttpRequestInitializer() { • public void initialize(HttpRequest request) { • GoogleHeaders headers = new GoogleHeaders(); • headers.setApplicationName("Google-CalendarSample/1.0"); • headers.gdataVersion= "2”; • request.headers= headers; • request.enableGZipContent= true; • } • }); • }

  11. Platforms Java Platforms – HTTP Trouble-Shooting Tip • Enable logging of HTTP requests and responses: • If you also need to see Authorization header, use Level.ALLinstead • On Android, you also need to: • Logger.getLogger("com.google.api.client.http").setLevel(Level.CONFIG); adb shell setproplog.tag.HttpTransport DEBUG

  12. Android Android: using AsyncTask • Activities run in the main UI thread • No HTTP in main thread • Otherwise Android may shut down the application (“… is not responding”) • Simplest solution is to use AsyncTask • Sessions from Google I/O • [2010] Beginner's Guide to Android (goo.gl/qhaZg)

  13. Android HelloBuzzAsyncActivity Example (part 1 of 2) public class HelloBuzzActivityextends ListActivity { final Buzz buzz = new Buzz(newNetHttpTransport(), new AndroidJsonFactory()); @Override public void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); new LoadActivities().execute(); } class LoadActivitiesextends AsyncTask<Void, Void, ActivityFeed> { final ProgressDialogdialog = new ProgressDialog(HelloBuzzActivity.this); @Override protected void onPreExecute() { dialog.setMessage("Loading Activities..."); dialog.show(); } @Override protected ActivityFeeddoInBackground(Void... params) { try { return buzz.activities.list("112550920411899420633", "@public").execute(); } catch (IOExceptione) { return null; } }

  14. Android HelloBuzzAsyncActivity Example (part 2 of 2) @Override protected void onPostExecute(ActivityFeed feed) { dialog.dismiss(); if (feed == null) return; List<Spanned> activities = new ArrayList<Spanned>(); if (feed.items!= null) for (Activity a : feed.items) activities.add(Html.fromHtml(a.buzzObject.content)); setListAdapter(newArrayAdapter<Spanned>(HelloBuzzActivity.this, android.R.layout.simple_expandable_list_item_1, activities)); } } }

  15. Android Android – Using a background Service • Background service makes HTTP requests to API • Store result in a SQLite database • Activity lifecycle • Send async request to background service • If alive when response arrives, process immediately • Else, onCreate check for updated data • Sessions from Google I/O • [2010] Developing Android REST client applications (goo.gl/R15we)

  16. Android Android – Shrink Application with ProGuard Based on HelloBuzzActivity sample (95% savings)

  17. Android Android – Setting Up ProGuard (goo.gl/x1hit) • Eclipse New Project Wizard generates default.properties and proguard.cfg • Add to default.properties: • Add to proguard.cfg: proguard.config=proguard.cfg # Needed by google-api-client to keep generic types and @Key annotations accessed via reflection -keepclassmembers class * { @com.google.api.client.util.Key <fields>; } -keepattributesSignature,RuntimeVisibleAnnotations,AnnotationDefault # Needed by Guava -dontwarnsun.misc.Unsafe

  18. New Google APIs Android Google Data APIs Java 5 (SE, EE) Any REST APIs JSON XML Photos Videos Google App Engine OAuth 2.0 Client Login OAuth 1.0 Platforms APIs Data Formats Auth OUTLINE

  19. Google Data APIs Google Data APIs (“GData”) • Old Decentralized Architecture • XML • Some have read-only JSON-C • ClientLogin, OAuth 1.0, and OAuth 2.0 • Examples: YouTube Data API Google Calendar Data API Blogger Data API Google Contacts Data API

  20. Google Data APIs Google Data APIs (“GData”) – Java Library • Google Data Java Client Library • Nice XML data model • But only works with Google Data APIs • Nothing else is supported • Doesn’t support Android • May be fixed in Ice Cream Sandwich SDK? • Still maintained and not deprecated • But we’ve stopped developing it 2 years ago

  21. New Google APIs Google APIs • New Centralized Architecture • JSON • OAuth 1.0 and OAuth 2.0 • Google I/O sessions • Google I/O ’10 “How Google Builds APIs” (goo.gl/i1WfR) • Google I/O ‘11 “Life of a Google API Developer” (goo.gl/VKJ0q) • Examples: Buzz API Latitude API Search API for Shopping

  22. New Google APIs Google APIs - Discovery Service (goo.gl/iHUN6) • Announcing V1 of the Discovery Service today • Directory of supported APIs • Discovery document for each API • A list of API resource schemas based on JSON Schema. • E.g. “ActivitiesFeed” • A list of API methods and available parameters for each method. • E.g. “buzz.activities.list” • A list of available OAuth 2.0 scopes. • E.g. “https://www.googleapis.com/auth/buzz” • Google I/O ’11 session “Building Custom Client Libraries for Google APIs” (goo.gl/b5P6b)

  23. New Google APIs Google APIs – Explorer (goo.gl/Ccni0) • Web-based Google APIs exploration tool

  24. New Google APIs OAuth 2.0 – Google apis console (goo.gl/UyAZB)

  25. New Google APIs Google APIs – Generated Libraries (goo.gl/avR14) • Announcing generated Java libraries based on the Discovery API • Get an activity feed: • Insert an activity: • Delete an activity: • ActivityFeed feed = buzz.activities.list("112550920411899420633", "@public").execute(); • Activity activity = new Activity(); • activity.buzzObject = new ActivityObject(); • activity.buzzObject.content = "Posted using Google API Client Library for Java " • + "(http://code.google.com/p/google-api-java-client/)"; • Activity result = buzz.activities.insert("@me", activity).execute(); • buzz.activities.delete("@me", "@self", activity.id).execute();

  26. New Google APIs Android Google Data APIs Java 5 (SE, EE) Any REST APIs JSON XML Photos Videos Google App Engine OAuth 2.0 Client Login OAuth 1.0 Platforms APIs Data Formats Auth OUTLINE

  27. JSON JSON – Streaming parser/serializer library • Pluggable choice of streaming library • Avoid memory overhead of a full parser like JSONObject • Stream parsing faster than “full” data models • JacksonFactory • Fastest. Based on popular Jackson library (goo.gl/Z0yZF) • GsonFactory • Fast, and smaller than Jackson. • Based on Google GSON library (goo.gl/xBO5C) • AndroidJsonFactory • Same as GSON, but built in to Honeycomb (SDK 3.0) in package android.util (goo.gl/uJHiR)

  28. JSON JSON – Data Model • Rich Java Object to JSON mapping • Similar to functionality provided by Jackson and Google GSON • Examples • Java String/Number/Boolean maps to JSON string/number/boolean • Java enums can be used for JSON string • Java Arrays and Collection can be used for JSON arrays • Map or Java objects can be used for JSON objects • Full richness of Java supported, including generic types (e.g. List<Activity>) • Extend GenericJsonwhen you need to preserve arbitrary fields • Important when using PUT to update an entry to avoid dropping data

  29. JSON JSON – Data Model Example { "updated":"2010-05-20T23:08:07.471Z", "id":"tag:google.com,2010:buzz:z12nu3oa1r25gr3wp04cd3zp2zvafrjrlso0k", "object": { "content":"Presenting live at Google I/O!” } } public class Activity { @Key public DateTimeupdated; @Key public String id; @Keypublic ActivityObjectobject; } public class ActivityObject { @Keypublic String content; } JSON Java

  30. JSON JSON – Gzip and Partial Response Based on HelloBuzz sample (95% savings)

  31. JSON JSON Example – Partial with the Generated Libraries • Before: • After: • Or for more data: • ActivityFeed feed = buzz.activities.list("112550920411899420633", "@public").execute(); • Buzz.Activities.List request = buzz.activities.list("112550920411899420633", "@public"); • request.fields= "items/object/content"; • ActivityFeed feed = request.execute(); • Buzz.Activities.List request = buzz.activities.list("112550920411899420633", "@public"); • request.fields= "items(object/content,updated,id)"; • ActivityFeed feed = request.execute();

  32. JSON JSON: streaming entries HttpResponse response = buzz.activities.list("112550920411899420633", "@public").executeUnparsed(); InputStream content = response.getContent(); JsonParser parser = jsonFactory.createJsonParser(content); parser.nextToken(); parser.skipToKey("data"); parser.skipToKey("items"); while (parser.nextToken() == JsonToken.START_OBJECT) { Activity activity = parser.parse(Activity.class, null); // process activity }

  33. XML XML – Data Model <entry xmlns='http://www.w3.org/2005/Atom’xmlns:gCal='http://schemas.google.com/gCal/2005'> <id>http://www.google.com/calendar/feeds/default/calendars/abc123%40group.calendar.google.com</id> <updated>2011-05-09T23:59:51.000Z</updated> <title type='text'>abc 2</title> <gCal:timezone value='UTC'/> </entry> public class CalendarEntry { @Key public String id; @Key public DateTimeupdated; @Key public Title title; @Key(“gCal:timezone”)public TimeZonetimeZone; } public class Title { @Key(“@type”)public String type; @Key(“text()”)public String value; } public class TimeZone { @Key(“@value”)public String value; } XML Java

  34. XML XML – Tips • Must declare XML namespaces: • Partial response fields mask is automatically computed URL query parameter: • public static final XmlNamespaceDictionary DICTIONARY = new XmlNamespaceDictionary() • .set("", "http://www.w3.org/2005/Atom") // default namespace • .set("gCal", "http://schemas.google.com/gCal/2005"); • public class CalendarUrl extends GoogleUrl { • ... • } • <Fextends Feed> FexecuteGetFeed(CalendarUrlurl, Class<F> feedClass) throws IOException { • url.fields= GoogleAtom.getFieldsFor(feedClass); • HttpRequest request = requestFactory.buildGetRequest(url); • return request.execute().parseAs(feedClass); • }

  35. Photos Videos Media • Download Media (GET): • HttpResponse.getContent() returns InputStream • HttpResponse.contentTypeis the media type • Upload Media (POST or PUT): • Use InputStreamContentfor uploading an input stream • type is the content type • length is the length of the media content (if known) • inputStreamis the media content input stream

  36. Photos Videos Media • public class PostPhotoActivityextends Activity { • @Override • public void onCreate(BundlesavedInstanceState) { • super.onCreate(savedInstanceState); • HttpRequestFactoryrequestFactory = new NetHttpTransport().createRequestFactory(); • Intent intent = getIntent(); • Bundle extras = intent.getExtras(); • InputStreamContent content = new InputStreamContent(); • ContentResolvercontentResolver = getContentResolver(); • Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM); • content.inputStream= contentResolver.openInputStream(uri); • Cursor cursor = contentResolver.query(uri, null, null, null, null); • cursor.moveToFirst(); • content.type= intent.getType(); • content.length= cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.SIZE)); • HttpRequest request = requestFactory.buildPostRequest(newGenericUrl( • "https://picasaweb.google.com/data/feed/api/user/default/albumid/default"), content); • GoogleHeaders headers = new GoogleHeaders(); • request.headers= headers; • String fileName = cursor.getString(cursor.getColumnIndexOrThrow(Images.Media.DISPLAY_NAME)); • headers.setSlugFromFileName(fileName); • request.execute().ignore(); • } • }

  37. New Google APIs Android Google Data APIs Java 5 (SE, EE) Any REST APIs JSON XML Photos Videos Google App Engine OAuth 2.0 Client Login OAuth 1.0 Platforms APIs Data Formats Auth OUTLINE

  38. Auth AccountManager • User accounts centrally controlled on Android • Including synchronization • AccountManager: abstraction to store auth token per account • Need to know the “account type” and “auth token type” • Google Accounts controlled by AccountManager • Account type always “com.google” • Auth token type depends on authentication method and Google API • Tip: library provides a handy GoogleAccountManager • Don’t try to bypass AccountManager and handle authentication yourself

  39. Client Login ClientLogin (goo.gl/WkGFX) • Username/password authentication for Google Data APIs • Not supported by new Google APIs, like Buzz and Latitude! • Request permission from user to have complete read/write access to a Google service • Auth token type is “service name” • For example “cl” for Google Calendar

  40. Client Login ClientLogin for Calendar Example

  41. Client Login ClientLogin for Calendar Example (1 of 3) public class HelloCalendarActivityextends ListActivity { GoogleAccountManageraccountManager; HttpRequestFactoryrequestFactory; SharedPreferencessettings; String accountName; String authToken; @Override public void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); requestFactory= transport.createRequestFactory(newHttpRequestInitializer() { public void initialize(HttpRequest request) { GoogleHeaders headers = new GoogleHeaders(); headers.gdataVersion= "2"; headers.setGoogleLogin(authToken); request.headers= headers; } });

  42. Client Login ClientLogin for Calendar Example (2 of 3) accountManager = new GoogleAccountManager(this); settings = this.getSharedPreferences("prefs", 0); authToken= settings.getString("authToken", null); accountName= settings.getString("accountName", null); Account account = accountManager.getAccountByName(accountName); if (account == null) { chooseAccount(); } else { new LoadCalendars().execute(); } } }

  43. Client Login ClientLogin for Calendar Example (3 of 3) private static final String AUTH_TOKEN_TYPE = "cl"; private void chooseAccount() { AccountManager.get(this).getAuthTokenByFeatures("com.google", AUTH_TOKEN_TYPE, null, this, null, null, new AccountManagerCallback<Bundle>() { public void run(AccountManagerFuture<Bundle> future) { try { Bundle bundle = future.getResult(); setAccountName(bundle.getString(AccountManager.KEY_ACCOUNT_NAME)); setAuthToken(bundle.getString(AccountManager.KEY_AUTHTOKEN)); new LoadCalendars().execute(); } catch (Exception e) { } } }, null); }

  44. OAuth 2.0 OAuth 2.0 (goo.gl/CdEGm) • Latest OAuth standard, supported by (almost) all Google APIs • Including the Google Data APIs • Request a more fine-grained “scope” of access • Example for Google Buzz: • https://www.googleapis.com/auth/buzz for read/write access to Buzz data • https://www.googleapis.com/auth/buzz.readonly for read access to Buzz data • https://www.googleapis.com/auth/photos for read access to Buzz photos • Auth tokens are temporary and expire in 1 hour • Check for 401 error response code and go through auth flow again

  45. OAuth 2.0 OAuth 2.0 – Getting an Auth Token • Sorry, don’t have an ideal story here yet. • Use auth token type of “oauth2:” plus space-separated scopes • For example: oauth2:https://www.googleapis.com/auth/buzz • Code example is exactly the same, just change the AUTH_TOKEN_TYPE

  46. OAuth 2.0 OAuth 2.0 for Buzz Example

  47. OAuth 2.0 OAuth 2.0 – Issues • Issue #1: User Interface shows the auth token type, not a comprehensible message about what permission is being granted • Quick-and-dirty: you can use “Google Buzz”as an alias for “oauth2:https://www.googleapis.com/auth/buzz”. We will try to set up more of these aliases. • Issue #2: Anonymous unregistered quota only gives you zero or near-zero for new Google APIs like Buzz • Solution: register on the Google apis console and get an “API key” or “access key” from the API Access tab under “Simple API Access” • Make sure you flip the switch to ON for the API you need in the “Services” tab • Free registered quota for Buzz for example 1,000,000 queries/day, so you only pay for usage above that level

  48. OAuth 2.0 OAuth 2.0 for Buzz Example (1 of 4) public class HelloBuzzActivityextends ListActivity { private static final String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/buzz"; GoogleAccessProtectedResourceaccessProtectedResource= new GoogleAccessProtectedResource(null); Buzz buzz; Account account; @Override public void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); buzz = new Buzz(newNetHttpTransport(), accessProtectedResource, new AndroidJsonFactory()); buzz.accessKey= "ABCdef123_9q"; ... if (account != null && accessProtectedResource.getAccessToken() == null) { getAuthToken(account); } } }

  49. OAuth 2.0 OAuth 2.0 for Buzz Example (2 of 4) @Override protected ActivityFeeddoInBackground(Void... params) { try { // execute HTTP requests } catch (HttpResponseExceptione) { if (e.response.statusCode== 401) { accountManager.invalidateAuthToken(accessProtectedResource.getAccessToken()); accessProtectedResource.setAccessToken(null); SharedPreferences.Editor editor2 = settings.edit(); editor2.remove(PREF_AUTH_TOKEN); editor2.commit(); getAuthToken(account); } }

More Related