Thursday, July 26, 2012

Building Awesome Android Apps with Google Cloud Endpoints


Here's a follow up on my Google IO 2012 talk on building awesome Android Apps using Google Cloud Endpoints. You can sign up for the Google Cloud Endpoints Trusted Tester program and try this out!

Google Cloud Endpoints allows you to define business logic on App Engine and access them via RESTful or RPC APIs on multiple platforms including Android, IOS and JavaScript. Cloud Endpoints are built using Google’s API infrastructure, which means that you get all the benefits of Google APIs (Google+ API, GMail API etc):

  • Built-in authentication support
  • APIs Console to manage your APIs
  • APIs Explorer to discover and try out your APIs on a browser
  • Automatically generated client libraries for Android, IOS and JavaScript



The Google Plugin for Eclipse (GPE) allows you to create and easily consume Cloud Endpoints.  GPE provides the following features to help you develop Cloud Endpoints:

  • Automatic generation of an Endpoint class that contains code to create, get, list, update and delete your entity class - an entity class is just a POJO with JPA/JDO persistence annotations.
  • Automatic generation of API configuration.

GPE provides the following additional features for Cloud Endpoints for Android:

  • Generation of an App Engine Backend project.
  • Generation of a strongly typed client library for Android. GPE copies over the generated client library and their dependencies to the Android project.
  • Support for Google Cloud Messaging (GCM). GCM allows your Endpoints to notify Android devices of changes to any API resources on the App Engine.

Get started with Cloud Endpoints by picking one or more of the following target platforms. Do note that you can use the same App Engine backend for one or more target platforms.

  • Android
  • IOS
  • Javascript

Cloud Endpoints for Android

Prerequisites:

Workflows:
  • Creating an Android Application that uses Cloud Endpoints
  • Adding Authentication to the Cloud Endpoints
  • Using Google Cloud Messaging with Cloud Endpoints

Creating an Android Application that uses Cloud Endpoints


  1. Create an Android project using the ADT’s Android Application Wizard.
  2. Create an App Engine Backend project for the Android project.
  3. Create your Entity class (POJO) - this is a class that represents the Endpoint resource with a bunch of class attributes, setter and getter functions. You can add JPA annotations (@Entity, @Id etc) or JDO annotations to specify that this entity class needs to be persisted. As an example, here is a class representing a Note.

package com.cloudnotes;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Note {

 @Id
 private String id;
 private String description;
 private String emailAddress;

 public Note() {
 }

 public String getId() {
return id;
 }
 
 public String getDescription() {
   return description;
 }

 public String getEmailAddress() {
   return emailAddress;
 }
 

 public void setId(String idIn) {
   this.id = idIn;
 }
 

 public void setDescription(String description) {
   this.description = description;
 }

 public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
 }
}

  1. Generate the Endpoint class for each Entity class. This generates an Endpoint class that has the implementation for insert, update, remove, get, list methods on the Endpoint class using JPA or JDO as specified in the Entity class.

The @Api annotation indicates that this is an Endpoint class. You can modify this class as per your needs. To add additional methods, you will need to add the @ApiMethod annotations for these methods. Example: a searchNote(String note) method would have the annotation: @ApiMethod (httpMethod=”GET”, name=”note.search”)


Note:
    • In the Endpoint methods, the return value type cannot be simple type such as String or int. The return value needs to be a POJO, an array or a Collection.
    • The listXXX(), getXXX(id) are exposed as HTTP GETs, insertXXX(XXX) is exposed as a HTTP POST, updateXXX(XXX) is exposed as a HTTP PUT, and removeNote(Note) is exposed as a HTTP DELETE.

  1. Generate Cloud Endpoint Client Library for the App Engine project by right-clicking the App Engine project, and selecting Google > Generate Cloud Endpoint Client Library
  2. Modify your Android Application to call into your defined Endpoint. You can write a class that extends the Android AsyncTask to make sure that the network call to the Endpoint does not block any UI thread.

The following code snippet shows how you can call into Noteendpoint.insertNote()

Builder endpointBuilder = new Noteendpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) { }
});
 

Noteendpoint endpoint = CloudEndpointUtils.updateBuilder(
endpointBuilder).build();
        try {
Note result = endpoint.insertNote(note).execute();
} catch (IOException e) {
//Handle exception
}

  1. Test Cloud Endpoints in the local environment.
    • Run App Engine local development server for testing. Hit the Run button on the App Engine application to start the local development server.
    • In your Android project, set CloudEndpointUtils.LOCAL_ANDROID_RUN to true. This indicates that the Android application connects to the local development server.
    • Run your Android application in your emulator.

  1. Test Cloud Endpoints with App Engine.
    • Set the Application ID for the App Engine project.
    • Deploy the App Engine project to App Engine.
    • In your Android project within the CloudEndpoints class, set LOCAL_ANDROID_RUN to false. This indicates that the Android application connects to the remote App Engine app.
    • Run your Android application in your emulator.

Adding Authentication to the Cloud Endpoints

Cloud Endpoints can be authenticated with Google Accounts using OAuth 2.0. This allows your Endpoint to verify and know the identity of the authenticated user. The following steps will help you add Authentication to your Cloud Endpoints.

Note: Android authentication code shown here requires the google-play-services.jar file  to be included in your libs/ folder. This JAR file has not yet been released. I will update this blog once the JAR file is released.

  1. Register a client ID in the Google APIs Console for your Android application
  2. Register an App Engine App ID.
  3. In the Endpoint class, under the @Api annotation (or the @ApiMethod annotation), set the registered Client ID the “clientIds” attribute, and the App Engine App ID as the “audience” attribute. The following code snippet shows this:

@Api(name = "noteendpoint",
clientIds = {"7232929396-4d7boi9meg5qmir9q80eg048qfval6rc.apps.googleusercontent.com
"},
audiences = {"cloudnotes2012.appspot.com"})
public class NoteEndpoint {...}

  1. Modify your methods to take in a User object (com.google.appengine.api.users.User) as an additional parameter. For an authenticated call, App Engine automatically fills in this attribute with the logged on user. The following code shows adding a User parameter to the listNote() method.
   

public List<Note> listNote(User user) {...}

  1. Modify your methods to modify the Datastore queries to query by the logged in User’s email address. The following code snippet shows querying for Notes for a given User.
Query query = mgr.createQuery("select n from Note n where n.emailAddress = :emailAddress");
query.setParameter("emailAddress", user.getEmail());

  1. GoogleAccountCredential.java allows you to manage Google Account selection and authorization of Google Accounts on the Android Application. You can modify your Android Application to use GoogleAccountCredential to get a handle to the Endpoint.

To fetch a GoogleAccountCredential instance:
 

GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(

context, audience);

where:
context is the Android Application Context
audience is the App Engine App ID (eg:cloudnotes2012.appspot.com)

To specify the credential while creating a handle to the Endpoint:



Builder endpointBuilder = new Noteendpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new JacksonFactory(),
credential);  
Noteendpoint endpoint = CloudEndpointUtils.updateBuilder(
endpointBuilder).build();

Using Google Cloud Messaging with Cloud Endpoints

Google Cloud Messaging allows your Cloud Endpoints to send notifications to registered Android devices whenever the state of a resource changes.

For example, let’s say a user uses a Note application from 2 devices: Device A and Device B. If the user is adding a note from Device A. Google Cloud Messaging can be used in the Cloud Endpoint for the insert operation to ping Device B to indicate that a note has been added.


You can perform the following steps to use Google Cloud Messaging with Cloud Endpoints.

  1. In the Google APIs Console, enable Google Cloud Messaging


  1. In the Google APIs Console, create a server API key. This will help App Engine securely communicate with the Google Cloud Messaging Server.

  1. In the App Engine project, DevicePing.java is a helper class that allows you to ping all registered devices. You should update DevicePing.API_KEY to specify the API key obtained in the previous step.
  2. In the App Engine project, you can modify the generated Endpoint class to send pings to registered devices. The following code snippet can be inserted in the insertNote(Note) method to send a ping to all registered devices.

DevicePing.pingAllDevices(user.getEmail() + ":" + note.getId() + ":insert");

  1. In the Android project, GCMIntentService.java is a helper class that helps register the device and perform actions when a message is received from the Google Cloud Messaging Server.
    1. Specify the Google APIs Project ID in GCMIntentService.java in the PROJECT_ID field. The Project ID can be obtained from the URL to the API console project. Example: https://code.google.com/apis/console/?pli=1#project:<PROJECT_ID>:services
    2. Modify GCMIntentService.onMessage() to handle the incoming GCM notification.
    3. In your Android project, call GCMIntentService.register(..) from your application (eg: onCreate() of your activity) to register the device with the Google Cloud Messaging Server and App Engine.



I/O Overview Talks





Java Codelab

Appendix


Source Code for GoogleAccountCredential.java


import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.TransientAuthException;
import com.google.android.gms.common.AccountPicker;
import com.google.api.client.googleapis.extensions.android2.auth.GoogleAccountManager;
import com.google.api.client.http.BackOffPolicy;
import com.google.api.client.http.ExponentialBackOffPolicy;
import com.google.api.client.http.HttpExecuteInterceptor;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpUnsuccessfulResponseHandler;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import android.accounts.Account;
import android.content.Context;
import android.content.Intent;
import java.io.IOException;
/**
* Manages account selection and authorization for Google accounts.
*/
public final class GoogleAccountCredential implements HttpRequestInitializer {
 final Context context;
 final String scope;
 private String accountName;
 private final GoogleAccountManager accountManager;
 private Account account;
 /**
  * @param context context
  * @param scope scope to use on {@link GoogleAuthUtil#authenticate}
  */
 private GoogleAccountCredential(Context context, String scope) {
   accountManager = new GoogleAccountManager(context);
   this.context = context;
   this.scope = scope;
 }
 /**
  * Constructor a new instance using OAuth 2.0 scopes.
  *
  * @param context context
  * @param scopes OAuth 2.0 scopes
  * @return new instance
  */
 public static GoogleAccountCredential usingOAuth2(Context context, String... scopes) {
   Preconditions.checkArgument(scopes.length != 0);
   String scope = "oauth2:" + Joiner.on(' ').join(scopes);
   return new GoogleAccountCredential(context, scope);
 }
 /** Sets the audience scope to use with Google Cloud Endpoints. */
 public static GoogleAccountCredential usingAudience(Context context, String audience) {
   Preconditions.checkArgument(audience.length() != 0);
   String scope = "audience:" + audience;
   return new GoogleAccountCredential(context, scope);
 }
 /**
  * Sets the selected Google account name (e-mail address), for example {@code "johndoe@gmail.com"}
  * , or {@code null} for none.
  */
 public GoogleAccountCredential setAccountName(String accountName) {
   this.accountName = accountName;
   account = accountManager.getAccountByName(accountName);
   return this;
 }
 public void initialize(HttpRequest request) {
   RequestHandler handler = new RequestHandler();
   request.setInterceptor(handler);
   request.setUnsuccessfulResponseHandler(handler);
   request.setBackOffPolicy(new ExponentialBackOffPolicy());
 }
 /**
  * Returns the selected Google account name (e-mail address), for example
  * {@code "johndoe@gmail.com"}, or {@code null} for none.
  */
 public String getAccountName() {
   return accountName;
 }
 /** Returns the selected Google account or {@code null} for none. */
 public Account getAccount() {
   return account;
 }
 /** Returns all Google accounts or {@code null} for none. */
 public Account[] getAllAccounts() {
   return accountManager.getAccounts();
 }
 /**
  * Returns an intent to show the user to select a Google account, or create a new one if there are
  * none on the device yet.
  *
  * <p>
  * Must be run from the main UI thread.
  * </p>
  */
 public Intent newChooseAccountIntent() {
   return AccountPicker.newChooseAccountIntent(account,
       null,
       new String[] {GoogleAccountManager.ACCOUNT_TYPE},
       true,
       null,
       null,
       null,
       null);
 }
 /**
  * Returns an OAuth 2.0 access token.
  *
  * <p>
  * Must be run from a background thread, not the main UI thread.
  * </p>
  */
 public String getToken() throws IOException {
   BackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
   while (true) {
     try {
       try {
         return GoogleAuthUtil.getToken(context, accountName, scope);
       } catch (TransientAuthException e) {
         // network or server error, so retry use
         long backOffMillis = backOffPolicy.getNextBackOffMillis();
         if (backOffMillis == BackOffPolicy.STOP) {
           throw e;
         }
         // sleep
         try {
           Thread.sleep(backOffMillis);
         } catch (InterruptedException e2) {
           // ignore
         }
       }
     } catch (GoogleAuthException exception) {
       IOException io = new IOException();
       io.initCause(exception);
       throw io;
     }
   }
 }
 class RequestHandler implements HttpExecuteInterceptor, HttpUnsuccessfulResponseHandler {
   /** Whether we've received a 401 error code indicating the token is invalid. */
   boolean received401;
   String token;
   public void intercept(HttpRequest request) throws IOException {
     token = getToken();
     request.getHeaders().setAuthorization("Bearer " + token);
   }
   public boolean handleResponse(
       HttpRequest request, HttpResponse response, boolean supportsRetry) {
     if (response.getStatusCode() == 401 && !received401) {
       received401 = true;
       GoogleAuthUtil.invalidateToken(context, token);
       return true;
     }
     return false;
   }
 }
}



24 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. also...

    "Modify your Android Application to call into your defined Endpoint. "

    do you have a simple example of this? A class file would be a huge help.

    ReplyDelete
    Replies
    1. Here's a simple example of such a class. It extends AsyncTask to do the networking off the UI thread.

      NoteendpointAsyncTask.class

      Delete
    2. This comment has been removed by the author.

      Delete
  4. I haven't been accepted to the endpoints trusted testers. Acutally I try to run this example on my local machine.

    Can I provide an OAuth2 Request for my local app using GoogleAccountCredential.usingAudience(...) ?

    Can I use Google Cloud Messaging for Android on my local machine? How do I register a device to the Google Cloud Messaging for Android? Is it possible at all on localhost?
    I've already registered my GoogleAccunt and my Appengine Projekt for the Cloud Messaging.

    I'm so excited of the possibilites of cloudendpoints. Help would be really awesome!

    ReplyDelete
  5. Thanks for this tutorial! Do you have any other resource about this topic? additional tutorials and code examples will be great!

    ReplyDelete
  6. Thank you!
    It will be very nice if you can share the complete project to download and get it exactly right.

    Thank you!!

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. NET is a Programme Several important part of what Windows is running in Run On A Present For General klike program. Cloud Development This Cool People What do I need It. NET application that runs the computer That arrogant. For those who developed, .NET development, ASP development, SharePoint development, Microsoft development , software development, Singapore – Total eBiz Solutions Home

    ReplyDelete

  9. Thanks for this post it's really interesting all type of art work are done by iPhone.
    Very nice, keep it up.

    --------------------------------------------------------------------------------------------------------------
    iPhone App Development & iOS App Development & Android Application Development

    -------------------------------------------------------------------------------------------------
    iPhone 5 App Development & iPad Application Developer

    ReplyDelete
  10. Google was not intrigued by helping engineers profit from their applications. They essentially needed a stage to control. So they made improvement on it as cool and synergistic as conceivable. The neighborhood of engineers helped a ton. Then again, Apple was genuine about offering applications and profiting from them. So they put resources into the application dispersion chain.
    Andriod Application Development // iPhone Application Development // Mobile Application Development

    ReplyDelete
  11. Thanks for sharing this post. There are many Android Game applications in the market and OnGraph Technologies is the best Android Game Development Company in India always tried to launch new apps.

    ReplyDelete
  12. Thanks for this publish it's really exciting all kind of art perform are done by iPhone.Very awesome, keep it up. Cloud inventory management

    ReplyDelete
  13. Great Information Thanks For Sharing

    For More Information You Can Visit This Website Web Application Development Solutions

    ReplyDelete
  14. These are some of the common web application development solutions we provide Web 2.0, Community and Networking Applications Effective and easy-to-use user.

    Web Application Development

    ReplyDelete
  15. IPhone Application development and programming always require innovative thinking and in-depth technological knowledge.

    iPhone Application Development

    ReplyDelete
  16. Thanks so very much for taking your time to create this very useful and informative site. I have learned a lot from your site. Thanks!!

    JAVA Training Center in Chennai

    ReplyDelete
  17. iphone app development and androids applications when created, developers always require innovative thoughts in mind and in-depth technological knowledge.

    ReplyDelete
  18. I am getting problem while creating new entity. It is not getting shown in app engine.Please help me

    ReplyDelete