Past Posts in this Series
 
Blog Post Link
Part 1 - Why Scale Matters http://blogs.msdn.com/b/brunoterkaly/archive/2011/09/27/supporting-billions-of-entities-rows-for-mobile-android-series-part-1-why-scale-matters.aspx
Part 2 - What are some high level cloud offerings? http://blogs.msdn.com/b/brunoterkaly/archive/2011/09/27/supporting-billions-of-entities-rows-for-mobile-android-series-part-2-what-are-some-high-level-cloud-offerings.aspx
Part 3–Architecture and Data Options http://blogs.msdn.com/b/brunoterkaly/archive/2011/09/28/supporting-billions-of-entities-rows-for-mobile-android-series-part-3-architecture-and-data-options.aspx
Part 4–Building a Cloud-based RESTful service for our Android, iOS, and Windows Phone 7 Clients http://blogs.msdn.com/b/brunoterkaly/archive/2011/09/28/supporting-billions-of-entities-rows-for-mobile-android-series-part-4-building-a-cloud-based-restful-service-for-our-android-ios-and-windows-phone-7-clients.aspx
Part 5–Using the Portal and Setting up your Azure Account (Microsoft Cloud) http://blogs.msdn.com/b/brunoterkaly/archive/2011/10/05/supporting-billions-of-entities-rows-for-mobile-android-series-part-5-using-the-portal-and-setting-up-your-azure-account-microsoft-cloud.aspx
Part 6–Reading and Writing to Windows Azure (Cloud-based) Tables using standard HTTP and Fiddler http://blogs.msdn.com/b/brunoterkaly/archive/2011/10/05/supporting-billions-of-entities-rows-for-mobile-android-series-part-6-reading-and-writing-to-windows-azure-cloud-based-tables-using-standard-http-and-fiddler.aspx
Part 7–Migrating your Azure (Cloud RESTful Service) to be Hosted in a Microsoft Data Center http://blogs.msdn.com/b/brunoterkaly/archive/2011/10/07/supporting-billions-of-entities-rows-for-mobile-android-series-part-7-migrating-your-azure-cloud-restful-service-to-be-hosted-in-a-microsoft-data-center.aspx
Part 8 (This Post) –Writing an Android Client to consume RESTful data from Azure (Microsoft Cloud) http://blogs.msdn.com/b/brunoterkaly/archive/2011/10/10/supporting-billions-of-entities-rows-for-mobile-android-series-part-8-writing-an-android-client-to-consume-restful-data-from-azure-microsoft-cloud.aspx
You will need to download the Azure 90-day free trial. Click the image to the right.  
 
   

  This Post is About Android Client
  Finally, we are ready to talk about Android development. Yes, that means we are going to take out the Eclipse IDE and code in Java. Our Android client will make JSON calls against cloud data.

q31r4cui

  Setting up Eclipse and Android for Development
  You obviously need to setup your Android development environment. These links will get your started. You will need to learn about downloading and installing Eclipse, the Android SDK, the Java runtime, etc.   Everything that follows here assumes you have a properly configured Eclipse environment and associated Android tooling.
Download Link
Download the Android SDK http://developer.android.com/sdk/index.html
Setting up Eclipse and Android http://insanitydesign.com/wp/projects/nehe-android-ports/setting-up-eclipse-and-android/
Installing the SDK http://developer.android.com/sdk/installing.html
Setting up your Android Environment http://fyi.oreilly.com/2009/02/setting-up-your-android-develo.html
   


  Creating the Android Project in Eclipse
  Select File / New / Android Project.
3zwf155z

Fill in the following information:
1. Project Name
2. Build Target
3. Application Name
4. Package name
5. The rest will auto-fill

rj3sugli

  CloudClientActivity.java
  We are now ready to start adding our java code. Notice we have already been provided CloudClientActivty.java.

vwj21gkn

Replace the existing code in CloudClientActivity.java with the following:             

package com.bruno.cloudclient;

import java.io.IOException;

import com.bruno.cloudclient.R;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.LightingColorFilter;
import android.os.Bundle;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class CloudClientActivity extends Activity {
    
    /** Called when the activity is first created. */
    ArrayAdapter<String> adapter = null;  // 
    String listName;
    SharedPreferences settings;
    /***************************************************************
    *  Purpose      - Startup routine. Configures view and accesses
    *                 xml file that is your view. Set the file system
    *                 sharing mode.
    ****************************************************************/
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        try 
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

            // Set file sharing mode to private
            settings = getPreferences(Context.MODE_PRIVATE);
            listName = settings.getString("listName", "Bruno");


            // Get some data. Create an adapter and attach to our listview 
            // control.
            final Data data = new Data();
            // This is a key call. It retrieves the dat from the web service.
            // The data is in JSON originally, then converted to simple strings.
            adapter = new ArrayAdapter<String>(this, R.layout.list_item,
                    R.id.listItem, data.getItems(listName));

            // Attach the data to the ListView
            final ListView myList = (ListView) findViewById(R.id.list);
            myList.setAdapter(adapter);

            // Create an "Add" button. This button will allow users to
            // add data stored in web service.
            Button btnAdd = (Button) findViewById(R.id.btnAdd);
            // Manage button color for a black screen
            btnAdd.getBackground().setColorFilter(
                    new LightingColorFilter(0xFFFFFFFF, 0xFFAA0000));

            // Add a textbox the user can use to enter new data to be added
            // to the web service.
            final EditText txtItem = (EditText) findViewById(R.id.txtItem);

            // Setup the button listener
            btnAdd.setOnClickListener(new Button.OnClickListener() {
                public void onClick(View v) {
                    settings = getPreferences(Context.MODE_PRIVATE);
                    listName = settings.getString("listName", "Bruno");
                    // Passing true to modifyData, which will add a new record
                    // in the cloud
                    data.modifyItem(listName, txtItem.getText().toString(), true);
                    // Clear the textbox
                    txtItem.setText("");
                    try {
                        refresh();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });

            // If you click on a ListItem in the ListBox you will
            // pass "false" to modifyItem() which will delete
            // a record in the cloud
            myList.setOnItemClickListener(new OnItemClickListener() 
            {
                public void onItemClick(AdapterView<?> a, View v, int position,
                        long id) 
                {
                    String s = myList.getItemAtPosition(position).toString();
                    settings = getPreferences(Context.MODE_PRIVATE);
                    listName = settings.getString("listName", "Bruno");
                    data.modifyItem(listName, s, false);
                    try 
                    {
                        refresh();  // Refresh the listbox to reflect the deleted entry
                    } 
                    catch (IOException e) 
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    // Notify user that record was just deleted
                    makeToast(s);
                }
            });
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
    }
 
    /***************************************************************
    *  Purpose      - Notify user that we just deleted a record
    *                 Pops a little message
    ****************************************************************/
    public void makeToast(String item) 
    {
        LayoutInflater inflater = getLayoutInflater();
        View layout = inflater.inflate(R.layout.toast_layout,
                (ViewGroup) findViewById(R.id.toast_layout_root));
        ImageView image = (ImageView) layout.findViewById(R.id.image);
        image.setImageResource(R.drawable.icon);
        TextView text = (TextView) layout.findViewById(R.id.text);
        text.setText(item + " deleted.");
        Toast toast = new Toast(getApplicationContext());
        toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
        toast.setDuration(Toast.LENGTH_SHORT);
        toast.setView(layout);
        toast.show();
    }

    /***************************************************************
    *  Purpose      - Get latest data. Fill the view, attach view and
    *                 adapter, tell adapter to refresh view. 
    ****************************************************************/
    public void refresh() throws IOException 
    {
        Data d = new Data();
        settings = getPreferences(Context.MODE_PRIVATE);
        listName = settings.getString("listName", "Bruno");
        adapter = new ArrayAdapter<String>(this, R.layout.list_item,
                R.id.listItem, d.getItems(listName));
        // Attach adapter and view, then refresh with the
        // notifyDataSetChanged event
        ListView myList = (ListView) findViewById(R.id.list);
        myList.setAdapter(adapter);
        adapter.notifyDataSetChanged();
    }

}


                
             

  Data.java
  Add a java module and insert code.

le3ul5l4

Call it Data.java.

image

You should see this:

image

You will need to take note of the URL for your Azure service. See “YOUR_AZURE_SERVICE” below.

It should correspond to:

bl5n2vwz

Open Data.java and insert this code:        
package com.bruno.cloudclient;


import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import android.os.AsyncTask;

public class Data {
    private ArrayList<String> items;

    /***************************************************************
    *  Purpose      - Populate or list view control. Makes a call
    *                 the web service using a hard-coded address.
    *                 
    * 
    **************************************************************** 
    *  Assumptions  - RESTful service available with data
    * 
    **************************************************************** 
    *  Effects      - none, simply reads data
    * 
    **************************************************************** 
    *  Inputs       - A list (represents an android user) to 
    *                 that is looked up from the RESTful service
    **************************************************************** 
    *  Returns      - A populated array (will fill the adapter
    *                 which is mapped to our listbox
    * 
    ****************************************************************/
    public ArrayList<String> getItems(String listName) throws IOException 
    {

        // ArrayList will be populated from the JSON data coming from
        // the web server
        items = new ArrayList<String>();
        
        JSONArray ja = new GetShoppingListData()
           .doInBackground("http://fastmotorcycleservice.cloudapp.net/FastMotorcycleListService.svc/list/"+listName);
        
        // Loop through json array and return results to be used in 
        // the listbox via the adapter object
        for (int i = 0; i < ja.length(); i++) 
        {
            try 
            {
               items.add(ja.getString(i));
            } 
            catch (Exception e) 
            {
                e.printStackTrace();
            }
        }
        return items;
    }
    /***************************************************************
    *  Purpose      - Does a delete or add asynchronously. If pass
    *                 true, then an add is done. Else do delete.
    ****************************************************************/
    public void modifyItem(String listName, String item, boolean operation) 
    {
        if (operation == true) 
        {
            HttpResponse response = new AddShoppingListData()
                .doInBackground(
                        "http://YOUR_AZURE_SERVICE.cloudapp.net/FastMotorcycleListService.svc/list/"+listName,
                    item);
        } 
        else 
        {
            HttpResponse response = new DeleteShoppingListData()
                    .doInBackground(
                            "http://YOUR_AZURE_SERVICE.cloudapp.net/FastMotorcycleListService.svc/list/"+listName,
                    item);
        }

    }
    
    /***************************************************************
    *  Purpose      - A private class to allow:
    *                 Asynchronously retrieve data from the RESTful
    *                 service hosted in the cloud
    **************************************************************** 
    *  Assumptions  - RESTful service up and and running
    * 
    **************************************************************** 
    *  Inputs       - A url to look up data
    * 
    **************************************************************** 
    *  Returns      - A JSON array full of data
    * 
    ****************************************************************/
    private class GetShoppingListData extends
            AsyncTask<String, Void, JSONArray> {
        @Override
        protected JSONArray doInBackground(String... urls) 
        {
            // HttpClient used to talk to web service.
            HttpClient httpclient = new DefaultHttpClient();
            
            // This will be the array we need to convert. We
            // get the data from the web service.
            JSONArray listItems = null;
            String jason = null;
            
            // Setup the RESTful call to 'GET' the data
            HttpGet request_http_get = new HttpGet(urls[0]);

            
            // Read the JSON data and return
            try 
            {
                // Fill a response object using a request
                HttpResponse response_http_get = httpclient.execute(request_http_get);
            
                // Length represents the number of data items returned
                // by RESTful service
                long length = response_http_get.getEntity().getContentLength();

                // "entity" ends up being the data coming back from web server.
                HttpEntity entity = response_http_get.getEntity();

                // Read the bytes, one byte at a time.
                InputStream stream = entity.getContent();
                
                // Allocate a series of bytes
                byte[] buffer = new byte[(int) length];
                
                // Read bytes from RESTful service.
                // After this loop, we end up with -> ["busa","gxr1000","ninja250"]
                for (int i = 0; i < length; i++) 
                {
                    buffer[i] = (byte) stream.read();
                }
                // The string "buffer" looks something 
                // like this -> ["busa","gxr1000","ninja250"]
                jason = new String(buffer);
                // Convert to Json array for Android ListBox.
                // This ends up being a 3 element json array (see "busa" above)
                listItems = new JSONArray(jason);
            } 
            catch (Exception e) 
            {
                System.out.println(e);
            }
            return listItems;
        }
    }
 
    /***************************************************************
    *  Purpose      - A class used to Post data to the RESTful service (Microsoft
    *                 cloud, Azure) using Http 'POST'
    **************************************************************** 
    *  Inputs       - (1) The service address
    *                 (2) The data to be posted (what the user types in)
    **************************************************************** 
    *  Comments     - (1) Using HttpPost for "Add" operations
    *                 (2) Using UTF8 because default Android is UTF-16
    ****************************************************************/
    private class AddShoppingListData extends
            AsyncTask<String, Void, HttpResponse> {
        HttpResponse response_http;

        @Override
        protected HttpResponse doInBackground(String... params) 
        {
            HttpClient httpclient = new DefaultHttpClient();
            HttpPost request_add = new HttpPost(params[0]);

            try {
                request_add.addHeader("Content-Type", "application/json");
                String postData = "\"" + params[1] + "\"";
                request_add.setEntity(new ByteArrayEntity(postData.getBytes("UTF8")));

                response_http = httpclient.execute(request_add);

            } catch (ClientProtocolException e) {
                // TODO
            } catch (IOException e) {
                // TODO
            }
            return response_http;
        }
    }

    /***************************************************************
    *  Purpose      - A class used to Post data to the RESTful service (Microsoft
    *                 cloud, Azure) using Http 'POST'
    **************************************************************** 
    *  Inputs       - (1) The service address
    *                 (2) The data to be posted (what the user types in)
    **************************************************************** 
    *  Comments     - (1) Using HttpPost for "Delete" operations
    *                 (2) Fixing up spaces with s.replace()
    ****************************************************************/
    private class DeleteShoppingListData extends
            AsyncTask<String, Void, HttpResponse> {
        @Override
        protected HttpResponse doInBackground(String... params) 
        {
            HttpClient httpclient = new DefaultHttpClient();
            String s = params[1];
            // Fix up spaces
            String charEscaped = s.replace(" ", "%20");
            HttpDelete request_delete = new HttpDelete(params[0] + "/" + charEscaped);
            HttpResponse response_delete = null;
            try 
            {
                // Do the RESTful delete command
                response_delete = httpclient.execute(request_delete);
            } 
            catch (ClientProtocolException e) 
            {
                e.printStackTrace();
            } catch (IOException e) 
            {
                e.printStackTrace();
            }
            return response_delete;
        }
    }
}


                
          

  Main.xml
  The next 4 files represent the user interface. Lets start with Main.xml. It is already provided.

dd5pzpb2

Open the text editor. Right-mouse click and choose Open With / Text Editor.

04qfffvi

Insert the following xml code:        
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <ListView
    android:layout_alignParentTop="true" android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="fill_parent">  </ListView>
  <LinearLayout
    android:layout_below="@+id/ListView01" android:id="@+id/LinearLayout01" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_alignParentBottom="true">    <EditText
      android:id="@+id/txtItem"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:layout_alignParentBottom="true">
    </EditText>
    <Button
      android:id="@+id/btnAdd"
      android:layout_alignRight="@id/txtItem"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:text="Add"
      android:layout_alignParentBottom="true">
    </Button>
  </LinearLayout>
</RelativeLayout>
                


  Dialog.xml
  Add a new xml file by right-mouse clicking on layout. Choose New / Android XML File.

ntroj41s

Insert the following xml code:   
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/layout_root"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:padding="10dp">
  <EditText
    android:id="@+id/listName"
    android:layout_width="100dp"
    android:layout_height="fill_parent"
    android:layout_marginRight="10dp"/>
  <Button
    android:id="@+id/btnCreateList"
    android:layout_width="100dp"
    android:layout_height="fill_parent"
    android:textColor="#FFF"/>
</LinearLayout>

                


  list_item.xml
  Add a new xml file by right-mouse clicking on layout. Choose New / Android XML File.

Insert the following xml code:        
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/layout_root"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:padding="10dp">
  <EditText
    android:id="@+id/listName"
    android:layout_width="100dp"
    android:layout_height="fill_parent"
    android:layout_marginRight="10dp"/>
  <Button
    android:id="@+id/btnCreateList"
    android:layout_width="100dp"
    android:layout_height="fill_parent"
    android:textColor="#FFF"/>
</LinearLayout>

                



  toast_layout.xml
  Add a new xml file by right-mouse clicking on layout. Choose New / Android XML File.

Insert the following xml code:        

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content">
  <TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dp"
    android:textSize="16sp"
    android:id="@+id/listItem">
  </TextView>
</LinearLayout>

                



  Modify AndroidManifest.xml
  We need to tell the application that an connecting to the internet is possible from within the application. This is done by modifying the XML inside of AndroidManifest.xml.

We need to add the following line:

<uses-permission android:name="android.permission.INTERNET" />


This is what AndroidManifest.xml should look like:
       
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.bruno.cloudclient"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="10" />

	<uses-permission android:name="android.permission.INTERNET" />



    <application android:icon="@drawable/icon" android:label="@string/app_name">        <activity android:name=".CloudClientActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>
                


  Ready to compile and run !
  Close your Android emulators, if they are open. Save and close all the java and xml files.

Select the following command: Project / Clean.

image

There should be no errors.

We are ready to run our Android application. Right-mouse click on the CloudClient project and choose Run As / Android Application.


ku04rj1c

Unlock the client.

pmo2xuuf

If everything was coded correctly (and assuming you used Azure Storage Explorer to add data), you should see the following:

astrm2oi

This corresponds to our Azure Storage Explorer version of the data:

image

    Conclusion
  This concludes this 8-part series on constructing a RESTful Azure (Microsoft Cloud) and performing CRUD operations from an Android client application. This has demonstrated the whole process from start to end, complete with source code. Hope you found value in it!
-Bruno