This article is the companion to one I just posted about connecting Mobile Services to Windows Azure Table and Blob Storage. Prior to taking a look through this article and the mentioned code, I would go through the original article on Mobile Services and Windows Azure Storage. This article will cover how to connect the Mobile Service we set up there with an Android client using the Mobile Services SDK for Android. All of the source code for this Android app is available here in GitHub. Since this app deals with talking to Table and Blob Storage through Mobile Services, I’m only going to highlight here when we’re talking to Mobile Services from Android and maybe a few other important pieces. If you want to see how the entire app works (such as how we display table names and blob names in a table view) take a closer look at the source code.
Before you run the app
The first thing you need to do before you run this app is adjust the constructor method for the StorageService class to use your Mobile Service’s URL and Application Key. You can grab these by logging into the Windows Azure portal, going to your Mobile Service, and clicking the MANAGE KEYS button when viewing your service’s dashboard. Those values go into the new MobileServiceClient(… method call. The second thing you need to do is actually download a forked version of the Android Mobile Services SDK. I’ll address this in more detail below in the Parameters Support section. Once you’ve downloaded that version of the SDK, import the Android version (sdk/android/src/sdk/) into Eclipse and then reference it from the StorageDemo Android project.
The Storage Service
All of the “action” for talking to our Mobile Service is done inside of the StorageService class. For that reason, the majority of the code we’ll look at today is from that class. The first thing we’ll look at is the constructor method for the class:
As mentioned above, you’ll need to set the mobileserviceurl and applicationkey to your Mobile Service’s before running. After that, we’re initializing our local tables so we can use them to access the tables in our Mobile Service. Unlike the quick start application or most tutorials with Mobile Services, the variable type for all of the table variables is not a MobileServiceTable<ClassType> that is strongly typed to a class in my project.
Typed vs untyped data
As I said, most of the examples you’ll see of working with Mobile Services for Android demonstrate using strongly typed objects that have serialization attributes set so Mobile Services will know how to serialize and deserialize them. Also, MobileServices uses the object’s class name for the table name. However, it’s also possible to use untyped data when talking with Mobile Services. One of the program managers working on Mobile Services, Yavor Georgiev, actually has a great article all about using typed and untyped data with the Android Mobile Services SDK. The point is that in these samples we’re not using any typed data. This is optional as we could generate a typed class that represents the data we get back for tables, each specific table’s data, containers, and blobs. However, since we’re making a generic solution that will work with any table data, we’re just going to use the MobileServiceJsonTable. Just to make sure I’m perfectly clear, you do NOT have to use MobileServiceJsonTable here and can used typed classes, I’m not typing anything so this example is capable of loading data from any storage account. With all that said, let’s start looking at interacting with our Mobile Service.
Since we only need the table names when we load our tables, we’re just looping through them and building an arraylist of the TableName properties for reach result:
We’re actually getting back more information so we could track all of it if we wanted to. After we load the tables, we’re broadcasting an action named tables.loaded. Because I wanted all of my data stored in one location (Storage Service), I needed a way to let a different part of the application know that the data was loaded. In this case, the TablesActivity class has registered to be notified whenever this action is broadcasted so when that is fired, the list view that’s part of that activity will rebind to the loaded data.
Adding tables is actually quite easy:
We just need to generate a new JsonObject and put the table name in as a property before calling the insert method. When the table is created we’re recalling the getTables method above. This is just a quick way to trigger the list view to be reloaded. We’re also hitting the server for all of the tables again though we could avoid this by just inserting the new table’s name into the local array of table names and broadcasting the tables.loaded action.
Deleting tables is a little interesting. We HAVE to send over a JsonObject with an id property. If this isn’t included, the SDK will say it can’t try to delete the data. Additionally, when the delete script is called on the server, not all of the data we put into the JsonObject is included. In fact, only the id property is included. For that reason, we have to pass the name of the table we want to delete in as parameters:
Again when that’s done, we call getTables to trigger fetching the tables and reloading the list view. There is actually something very interesting about us passing the parameters in to this method, but I’ll address that at the end of the article (if you missed the note above about getting the application to compile or feel like skipping ahead and want to know why things aren’t working, read the note below about Parameter Support).
Reading table rows
To read the rows that are part of the selected table, we need to specify the table name:
Reading data from a Mobile Service table requires us to call the execute method. One of the overrides for this allows us to pass in a MobileServiceQuery. We can generate this by calling the parameter method on our MobileServiceJsonTable. Doing so with the table name will set the table name to be a property on the request.parameters object in our server side script. At the end, we broadcast the tablerows.loaded action which will be received by the TableRowsActivity and trigger reloading the list view.
Inserting table rows
Adding a table row requires us to do two things: fill a JsonObject with all of the data for the row, and pass the table name in as a parameter:
The data for the table row is passed in as a list of key-value pairs and is then looped through and added to the JsonObject. We next create a new list of key-value pairs to act as the parameters and add the table name to it. We then call the insert method on the mTableTableRows property. When that gets the data back we recall getTableRows to repull the data (again an unnecessary step if we want) which will trigger the tablerows.loaded action.
Deleting table rows
Deleting a table row, much like deleting a table, requires passing over an object with the id property and then passing the other information in as parameters:
The parameters required here are the table name, Partition Key, and Row Key. When the delete call comes back, we call getTableRows which will reload the data.
Updating table rows
Updating a table row is very close to inserting a table row:
One thing we have to do differently is add an id field. This is interesting because each table row actually has an id field that is set automatically when you insert to Table Storage. However, we’re not showing this in the UI or passing it over (it’s unnecessary for Table Storage). HOWEVER, you must pass over a non-zero id field in the data item to the update method or it won’t work. When that call comes back, we call getTableRows again to reload the table rows. That’s a wrap on tables, now we can look at blobs.
Getting the containers is pretty much the same as getting tables:
After getting the results, we loop through them and just pull out each item’s container name. Here we broadcast containers.loaded which triggers ContainersActivity to reload it’s listview.
Adding a container is very similar to adding a table except we’re also sending in a parameter:
The parameter specifies if the container we’re creating is public or not. If the container is created as public, we’ll be able to load up the blobs inside of it without needing a Shared Access Signature (SAS, but more on that later).
Deleting containers is just like deleting tables:
We pass over a zero id as the JsonObject and then pass over the container name as a parameter. When the call to delete comes back, we then call getContainers again to refetch the containers and trigger the reload the list view.
Getting the blobs is very simple as we just pass over the container name we want to get the blobs for:
However, once we get the data back, things are a little more complicated. We are storing an array of the names for each of the blobs (to more easily bind to the list view) as well as an array of the actual Json data for each blob to make it a little easier to load the blob (more on that later too). Once we’ve looped through the blobs, we broadcast the blobs.loaded method which triggers BlobsActivity to reload it’s listview.
Creating new blobs is handled a little bit interestingly. In order to create a blob, our Mobile Service tells Blob Storage it wants to create a new blob with a specified container name and blob name. It does this by generating a Shared Access Signature (SAS) which we can use as part of a URL to then post the blob data from the client. So, from the client we call getSasForNewBlob:
This method sends over a zero for the id and then specifies the container name and blob name as parameters. When this is done, we save the results (which includes the SAS URL) from the server, and then broadcast the blob.created action. This triggers BlobActivity to then upload the data to the SAS URL endpoint that was returned from the server. You can see how this is done by looking at the BroadcastReceiver and the ImageUploaderTask in the BlobActivity class.
Loading individual blob data
If the blob we’re looking at isn’t public, then we can’t just use it’s URL property to load the blob data (in the case of the sample app, we’re dealing with images when it comes to adding new blobs). Instead we have to get a “readable” SAS URL which will then briefly allow us to read the blob’s data (and load the image data). For that reason there is a getBlobSas method:
This method is nearly the exact same as the getSasForNewBlob method above (because on the sever side we get a read+write SAS URL) except for the action broadcasted. Here we’re broadcasting blob.loaded. This is received by the BlobDetailsActivity class which will then use the SAS URL retrieved to load the image data (look at BlobDetailsActivity’s ImageFetcherTask).
Deleting blobs is similar to deleting tables or containers:
We pass over a zero id and then put the container name and blobs name in the parameters that get sent over. We then call getBlobsForContainer to reload the blobs for the container.
Finished with storage
That’s all we need to do from the client side. Some of these method calls you’ve seen don’t really do anything differently than calls to Mobile Services when we’re using SQL Database storage normally. If we were using typed classes for Tables, Containers, and Blobs (which we could do since their data fields will be the same) things would look even more similar to normal usage. While for “generic data” you wouldn’t be able to use a typed class (necessarily) for the table rows data, in theory you’ll know exactly what data fields your tables have within them and could use a typed class for each of your different tables if you wanted to.
Unfortunately at the time of writing, the Android Mobile Services SDK doesn’t support sending optional parameters over when making insert, update, or delete requests. Thankfully I forked the repo and have added the ability to send parameters. I’m in the process of finishing up some unit tests that will test the new functionality before I can do a pull request against the official Android SDK and hopefully get that merged in. For now, you can access the forked SDK with parameter support here. In order to use it, you’ll need to import the Android SDK into Eclipse and then reference it from your Android project (or the StorageDemo app in the case of this sample).
Today we looked at how to implement a client that connects to a Mobile Service that uses Table and Blob Storage. Some aspects of it are no different from using normal SQL Database storage. Some of it requires a few additional steps. With the knowledge from this article and the server side one, you should now know how to comfortably combine SQL Database, Table, and Blob Storage. A good knowledge of Blob Storage and getting SAS URLs (and how to upload blobs with them) is important even if you stick to SQL Storage since it, and the JSON that is used between the client and Mobile Services, isn’t really meant for file storage. Good luck and remember that the source code for this and the server side scripts is all available here on GitHub.