Gradle Tutorial : Part 10 : Consuming Endpoints in Android Code

Welcome to Part 10 of the Gradle Tutorial. In the previous episode i.e. Part 9, we looked at writing Cloud Endpoints using Objectify as the Persistence layer. We wrote a Quote Endpoint class that exposed various methods like list Quotes, add Quotes, remove Quotes and modify Quotes.

LAST UPDATE : January 22, 2015 :
i) Minor text changes for build.gradle file as of Android Studio version 1.0.2

This tutorial adds to both Part 8 and Part 9 of the tutorials, where it will show how to consume the Endpoints code inside of your Android application. This would sort of logically complete the step that we set to do earlier in the series, which includes:

  • Generate a Android Project in Android Studio
  • Add an App Engine module with Cloud Endpoints to the Android project, thereby making it a multi-module project. In Part 8 and Part 9, we looked writing Cloud Endpoints and testing them locally via the API Explorer.
  • In this episode, we shall look at consuming that Endpoint in the Android code. This would really make you as an Android developer appreciate the power of Endpoints since within the same dev environment, you can do your client development (Android app) and Server side Code (App Engine + Cloud Endpoints) and then integrate and test it out locally.

If you have reached this part of tutorial without understanding the earlier parts, I suggest to go through Parts 7 , 8 and 9.

ep8-logo

This part assumes that you have installed Android Studio on your development machine and that you are familiar with basic Gradle commands, project structure and files as we have seen in the series so far.

I have used Android Studio (Beta) 1.0.2 on Windows for this blog post.

Download Project

The Android Studio project for this episode has been hosted on Github. If you want, you can download that.

What is in this episode?

We shall cover the following in this blog post:

  • Begin with where we left of in the last episode i.e. we created a Quote Endpoint service that allowed us to expose various methods like listQuote, insertQuote, updateQuote, removeQuote and so on.
  • Visit our Android Application and integrated the Quote Endpoint Service into the code. This is not an Android tutorial and hence the integration will be kept simple. We shall simply retrieve a list of Quotes via the Service and use Toast to display the quotes. You should familiar though with using the Android AsyncTask to asynchronously invoke our API backend from the Android client.

Before we begin

Make sure that the following is valid for your setup:

  1. You have setup the Hello CloudEndpoints project either from Github or by following the earlier part of the series.
  2. You can launch a local dev server for api module and are able to visit the quoteEndpoint API via the API Explorer at http://localhost:8080/_ah/api/explorer
  3. You have added a few Quote records and the listQuote method is giving back atleast 1 or more Quote objects.

If not, I suggest to follow Part 9 and get your setup correct.

Endpoints Client Libraries generation

Since, we are going to be consuming i.e. invoking the Endpoints API from our Android application, we need to get hold of the client libraries that we can use to invoke the API.

Technically, since the Endpoints are REST API Services hosted over HTTP, you can always write your own HTTP networking code and handle the request/response data inside of your Android application. But this will be a time consuming affair.

Google Cloud Endpoints provides a way for us to generate the client side libraries. For those of you, who have used Eclipse plugin to develop Cloud Endpoints, you must be familiar with the step of Generating Client Libraries.

In Android Studio, when you build your api project, there is a lot of stuff going on behind the scenes. If you noticed carefully when doing the App Engine Gradle build, generate endpoint libraries was done for you. What this means is that all the networking, marshalling/unmarshalling code has been generated and wrapped by an easy to use Endpoint class that you can invoke from your Android code.

If you look closely into your directories on your machine, you will find that there is a build folder for your api project that got generated and a bunch of folders got generated there with libraries, .class files, source files and so on.

ep10-1

 

Since the whole stuff is integrated now, you don’t have to mess around with these files at all. But it is good to know what has got generated behind the scenes and so on.

Gradle Files

Just to recap your understanding of multi-module projects and their dependencies, take a look again at app/build.gradle . You will notice that the Android app project is dependent on the api project and mentions that dependency against the compile configuration as shown below:

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  compile 'com.android.support:appcompat-v7:21.0.3'
  compile project(path: ':api', configuration: 'android-endpoints')
}

 

Android Application

Let us first look at the Android application to see what we are going to do. The first screen is shown below:

device-2014-08-27-135012

We have just added an additional button Get Quotes. When we click that button, it will make a call to the Endpoints API , specifically the listQuotes method.  It uses the client libraries for that, which we shall see in a while.

Once it gets the quotes, it goes through the list and displays the quotes, one by one via Toast, as shown below:

device-2014-08-27-135050

The next few sections will take you through the code and we will be focusing only on the app module in the project.

activity_main.xml

Nothing much happening here. All I have done is add a Button to the layout as shown below. The onClick of the button invokes the getQuotes method in the MyActivity.java class.


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MyActivity">

<TextView
android:text="Hello Cloud Endpoints"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get Quotes"
android:id="@+id/btnGetQuotes"
android:layout_below="@+id/textView"
android:onClick="getQuotes"
/>
</RelativeLayout>

MyActivity.java

In this section, I will only highlight the method that is invoked when the Get Quotes button is clicked. As mentioned in the earlier section, we need to have a getQuotes method in the activity class as shown below:


public void getQuotes(View v) {
new EndpointsAsyncTask(this).execute();
}

Few things to note here:

  1. Observe how compact the code is.
  2. It invokes an Async Task class, since it is important to call this functionality in an Asynchronous fashion.
  3. The EndpointsAsyncTask class is a template class that I have borrowed from the brilliant post at https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/HelloEndpoints

The class is shown below, I have modified it only so slightly.

EndpointsAsyncTask.java


package com.mindstorm.hellocloudendpoints;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Pair;
import android.widget.Toast;

import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.extensions.android.json.AndroidJsonFactory;
import com.google.api.client.googleapis.services.AbstractGoogleClientRequest;
import com.google.api.client.googleapis.services.GoogleClientRequestInitializer;
import com.mindstorm.api.quoteEndpoint.QuoteEndpoint;
import com.mindstorm.api.quoteEndpoint.model.Quote;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

class EndpointsAsyncTask extends AsyncTask<Void, Void, List<Quote>> {
private static QuoteEndpoint myApiService = null;
private Context context;

EndpointsAsyncTask(Context context) {
this.context = context;
}

@Override
protected List<Quote> doInBackground(Void... params) {
if(myApiService == null) { // Only do this once
QuoteEndpoint.Builder builder = new QuoteEndpoint.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
@Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
// end options for devappserver

myApiService = builder.build();
}

try {
return myApiService.listQuote().execute().getItems();
} catch (IOException e) {
return Collections.EMPTY_LIST;
}
}

@Override
protected void onPostExecute(List<Quote> result) {
for (Quote q : result) {
Toast.makeText(context, q.getWho() + " : " + q.getWhat(), Toast.LENGTH_LONG).show();
}
}
}

Let us go through the code in brief:

  1. This is a standard Android Async Task.
  2. Notice that since everything is all integrated inside of Android Studio, we simply imported the classes via the com.mindstorm.api.quoteEndpoint packages.
  3. An instance of the QuoteEndpoint is built via the builder as shown. Here we specify the HTTP Transport, JSON Factory and since there is currently no authentication stuff, the 3rd parameter is null. Since we are testing it locally , we are providing 10.0.2.2 which is the localhost for an Application that is run on the Android Emulator.
  4. Once the QuoteEndpoint service instance is available, we can invoke one of the various methods that it exposes like listQuotes, insertQuote, removeQuote, updateQuote, etc.
  5. Notice that the Cloud Endpoints client libraries behind the scenes do the marshalling/unmarshalling for us between Java objects and JSON data format and we get the response via our Java objects. In this case, we get back a list collection of Quote objects.

That is all really to it. You should try out writing other Async Tasks for more methods, so that you can familiar with this pattern.

In terms of authentication, I do not think anything changes just because it is Android Studio. You should read up on the Authentication episodes that I have covered earlier as part of my Cloud Endpoints tutorials, should you need to add security to your Endpoints.

Few Gotchas

  1. Remember to start the api application i.e. run the Local Development Server before your run your Android application.
  2. Always test out your Endpoints via the Local API Explorer before starting off with Android Development.
  3. Remember that 10.0.2.2 is the localhost’s IP Address in the Android emulator.
  4. If you deploy your Endpoints to App Engine live environment, do remember to change those settings with the actual deployed application id.
  5. Important: Do read up on the App Engine Java Endpoints Module template. It will provide you with the Async Task template that you will need to use in your Android code to invoke the Endpoint services.

Moving forward

With this episode, we have come to some sort of a logical end in our journey, since we are doing less of Gradle and more of Android + Endpoints stuff in the last few episodes, but that is ok.

Hopefully, the series has helped you understand better the different files that are getting generated vis-a-vis the Gradle stuff.

All the best for your projects!

Advertisements

43 thoughts on “Gradle Tutorial : Part 10 : Consuming Endpoints in Android Code

  1. Hello!

    I have a question…

    Is it possible to change the generated endpoints client library destiny folder? How can i edit the endpointsSrcClasses task?

    That’s will be awesome!

    1. See the following two properties that you can set as part of the endpoints closure. Maybe they could help:

      clientLibsJarOut: Output directory of client library jars when exporting. (Must be of type: File, will be created if doesn’t exist)
      clientLibsSrcJarOut: Output directory of client library source jars when exporting, if not specified no source jars will be copied. (Must be of type: File, will be created if doesn’t exist)

      The above is taken from the documentation at : https://github.com/GoogleCloudPlatform/gradle-appengine-plugin

      1. Thanks for the reply and the link =)

        Unfortunately it doesn’t work. I received this message when build gradle:
        “Configuration on demand is an incubating feature.
        Creating properties on demand (a.k.a. dynamic properties) has been deprecated and is scheduled to be removed in Gradle 2.0. Please read http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html for information on the replacement for dynamic properties.
        Deprecated dynamic property: “clientLibsSrcJarOut” on “project ‘:endpoints'”, value: “client-android-endpoin…”.
        Relying on packaging to define the extension of the main artifact has been deprecated and is scheduled to be removed in Gradle 2.0″

        I really hope you can help me =)

  2. @rominirani what should i do if want to access the local development server from my Smartphone (at the same network: ex: My local development server has this IP: 192.168.1.110).

    When i tried to run the endpoints with specific IP Address: 192.168.1.110 i can’t even access the Api Explorer…

    Please need your help 🙂

  3. @rominirani :

    Nice tutorial, very well explained, thanks

    check this link for next episode about android wear
    https://ljpmblog.appspot.com/?p=99

    It’s about other quotes and similar approach

    @Renan:

    Dont use gradle 2.+ and ignore warnings.

    Check version compatibility, we are using android-studio 0.8.6 (beta) or 0.8.9 (canary)
    http://tools.android.com/tech-docs/new-build-system/version-compatibility

    https://github.com/rominirani/HelloCloudEndpoints/blob/master/gradle/wrapper/gradle-wrapper.properties
    distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip

    https://github.com/rominirani/HelloCloudEndpoints/blob/master/build.gradle
    classpath ‘com.android.tools.build:gradle:0.12.2’

    So:
    Android Studio Android Gradle Plugin Gradle
    0.9.+ (still in development) 0.13+ (still in development) 2.0+
    0.8.+ 0.12+ 1.10 – 1.12

  4. @ardock Thanks!

    Another question to @ardock and @rominirani. Is there a way to share a constant (Value) between App and Endpoints, using Gradle?

    For example, I want to share the Google Cloud Project ID and i want to share the Key values from a GCM message (when i set that on the server and retrieve it at the app)..

    Thank you Two!

  5. Hi
    This is a great series but I am having problems consuming the endpoints.
    When I look in the build folder for your api there is no generated source folder.
    When I try to import Quote in the EndpointsAsyncTask it can not find either quoteEndpoint.QuoteEndpoint or quoteEndpoint.model.Quote
    Have i missed some generation step, I thought the client steps were generated automatically ?
    Thanks for any help

    1. Sorry, I don’t have the answer but I have the same problem!
      I don’t have a generated source folder either.
      In the previous tutorial, QuoteEndpoint and Quote were both part of the com.mindstorm.api package but now in EndpointsAsyncTask.java they are being imported from com.mindstorm.api.quoteEndpoint and com.mindstorm.api.quoteEndpoint.model respectively – neither of which I can find. So what happened in the meantime??? 🙂

    2. Thank you for great blog. very helpful, but

      Have the same problem ! Endpoint Client’s Libraries was not generated. I synced project with Gradle Files, but it did not help.

      Please help !!! Thank you.

  6. im new in android with android studio and Gcloud and this is a great tutorial!!!! does someone use the InsertQuote method? , if someone can give a hand with that would be great…
    and… @rominirani should be excellent if u extend more these example with more things!

  7. Hi,
    Thank you for your tutorials, really great !

    I have been following every step the best I could, I managed to deploy the back end and launch the frontend (merged with your frontend from your eclipse tutorial).
    But when I try to read or write in the back end, I keep having this error : “Could not Add Quote﹕ 404 Not Found”.
    Do you have an Idea of what I could have done bad ?
    On google console, the api works fine.

    Thank you anyway,
    Nicolas.

    1. Have you tried the Android App locally first and seen if the request reaches the local development Server? 404 is weird – are you trying to change or modify the endpoint URL on your own ?

      1. Thank you for your reply. I have been trying both locally and online. Same error. Whcih is weird because I used your settings in local. Therefore, for the endpoint URL, I’d say default for local and my project ID online. Is this right ?
        Thus, I already usedebug and I don’t know how to find more info in Android Studio (log, errors and so). So I can’t manage to move along to debug this.

      2. Yes, through this URL I managed to access the API explorer and to run command such as add or display quotes. But from the front end, it doesn’t work.
        At first, i managed to get a toast of a quote within the emulated app. So it worked at one point ..
        I might have done a mistake when I merged this frontend and the one frome your eclipse tutorial. I just took the “UI” from it and linked it to your Android Studio code.
        I ‘ll try tomorrow to reinclude the “get quotes” button and toast fonction to see if it works. I will keep you updated.
        Thank you again

  8. Hello,

    I don’t know why but Android Studio displays an error on Builder in QuoteEndpoint.Builder in the AsyncTask. Anybody could help me? Thanks in advance!

  9. Hello, with regards to my question above about the Builder: I discovered that the Builder shall not be a QuoteEndpoint.Builder but a QuoteApi.Builder where the import for QuoteApi shall be (at least in my environment) com.mindstorm.api.quoteApi.QuoteApi.

    I am using AndroidStudio 1.1.0

    Thanks a lot for your nice tutorials!
    Eric

    1. Hi my friend, its works fine in my emulator with appspot.com, but in my device not works, does not happens.

      Does Anybody have some idea?

      In my logcat show the following message whe I click in “GET QUOTES” button…
      “Application name is not set. Call Builder#setApplicationName.”

  10. Really helpful tutorial. Thanks.

    I would like to collect network traffic on an Android device without rooting. Anyone can help me with coding? How should I do it?

    Regards,

  11. First I want to say Thank you so much for this series!

    My question is:
    If I have java class “Quote” in the back-end module which is @Entity of the Data store and I have some @ApiMethod which return specific Quote, for example

    @ApiMethod(name = “getQuoteById”, httpMethod = ApiMethod.HttpMethod.GET)
    public User getQuoteById(@Named(ID) long id) {
    return ofy().load().type(Quote.class).id(id).now();
    }

    Should I have separate java class “Quote” in the Android app module? (because I guess it is a good idea to separate the logic of back-end from the logic in front-end(Android app))

    Could you give me a example what is the proper way to do that? How to transfer data from
    “@Entity Quote” (in the back-end) into “Quote” class (in the android app)?

    Thank you in advance.

    1. In general, I do agree that separating out the front-end set of classes from the backend is usually a good approach. One of the ways you could possibly do this is also to return some sort of a JSON response from the server and then marshall it into your front-end classes. This marshalling will possibly ignore, combine, translate and do things as per what you want. However there will be some dependency or the other on the data format in that case.

      1. Thank you for you quick response.

        What would be your approach and how you would do in this situation?
        (I just wonder if my structure of modules is correct)

  12. I would do the following:
    1) Assume that we have to do a GET call and return an Employee object.
    2) In the Endpoint method implementation, I would query the datastore for the data and then create a new Employee Object which will be marshalled and returned back to the client over the wire.
    3) The Employee Object class would be known on the client side also and it can get the data.

    This way, I will not expose the backend datastore object as is.

  13. Hi again,

    I have one question which is not so connect with that tutorial but I have a problem which really don’t know how to solve. So my situation is that:

    I have one @Entity class (for example Animal) and one @Subclass (Dog) and one Endpoint which has a @ApiMethod which returns List. When I write a code in the Android app module and get the result from that method (List) I cannot recognize which of this objects are from type Dog, because the android part don’t use the original “Animal” and “Dog” classes but use some other classes with the same names but generated from that Endpoint (the package of this classes is something like “com.backend.endpoint.model.Animal”) .

    How I can use the Polymorphism of GAE Entities in Android part?

    Thanks you in advance.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s