READ THIS FIRST
Update: 3-4-2016: If you're finding this blog and looking for information related to Azure Mobile Services, I'd strongly recommend checking out Azure Mobile Apps. Azure Mobile Apps is a new version (consider it a v2) of Azure's mobile backend support. All of the same features of Azure Mobile Services are there, with a lot of other very cool features to go along. You can read more about Azure Mobile Apps, and how to transition from Azure Mobile Services, here.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.
Reading tables
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.
Creating tables
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
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.
Reading containers
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.
Creating containers
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
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.
Reading blobs
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.
Inserting blobs
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
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.
Parameters Support
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).
Conclusion
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.
36 Comments
angel
when i import the source code for the android app into eclipse, they prompt errors such as cannot be resolved to a type for all your mobile service declaration. subsequently, the declaration that is being used to continue, all have the cannot be resolved to a type. May i know how to solve them?
Chris
You need to follow the instructions in the "Before you run the app" section. This sample is using a forked version of the Mobile Services SDK for Android to allow optional parameters to be sent over when performing insert, update, and delete requests. For that reason, you need to download the forked SDK, import that into Eclipse, and reference it from the source code in this project.
βrγΑΠ♠ΤΕø
Hi. I downloaded your source code and attempt to run it on my android phone, however, i wasn't able to insert picture at the insert blob page. I wonder why.
Chris
Can you give me more information? Did you receive an error on the client or on the server? Did the app run? etc
βrγΑΠ♠ΤΕø
currently, i'm testing it out on my android phone and i have no problem creating containers at the containersactivity. I checked my storage and i can see the containers being created. Soon, I selected the container name on the app and at the blobsactivity i was unable to create blob after select the image. I was also unable to test it out on my emulator as they prompt me a message stating no media was found.
βrγΑΠ♠ΤΕø
sorry for the trouble. After viewing the codes the entire day, i got confused with the file directory at the source code. The dialog view also made it even more confusing especially for newbie like me. Is there a simple tutorial on how to select image then upload to the existing container? I done my service side and client side codes. However the main uploading blob data part (Blob Activity) is the one which is rather confusing.
Chris
Sorry I don't have anything with that narrow of a focus. If you have an existing container, you should be able to strip everything out except the listing of the containers and then the uploading the blob. Let me know if you're getting a specific error and I can try to help.
βrγΑΠ♠ΤΕø
When i attempt to click the create button during the blob activity, this is the error i receive from the logcat.
05-09 02:17:59.056: E/StorageService(1348): {"code":500,"error":"Error: Internal Server Error"}
05-09 02:18:09.195: E/StorageService(1348): {"code":500,"error":"Error: Internal Server Error"}
Chris
Log into your Mobile Service and go to the Logging tab and tell me what error(s) are showing up there.
βrγΑΠ♠ΤΕø
This is the error from the lob tab
Error in script '/table/BlobBlobs.insert.js'. Error: The provided URI "http://ipolice/" is invalid. [external code] at insert (</table>:9:29) at </table>:1:8
Is it because i insert the wrong application key and account name?
Is the application key the key from the mobile service tab or the primary access key from the storage tab? And i reckon the account name should be the name of the storage/mobile service..
Chris
It sounds like the way you're initializing your blobService is wrong. Here's what it should be:
var accountName = 'StorageAccountName';
var accountKey = 'PrimaryStorageAccountKey';
var host = accountName + '.blob.core.windows.net';
var blobService = azure.createBlobService(accountName, accountKey, host);
Try fixing your stuff and if it's still throwing an error, email me your script.
Sergio J. Peralta
it happens because the name of the blob is incorrect see this instructions:
The container name must be a valid DNS name, conforming to the following naming rules:
- Container names must start with a letter or number, and can contain only letters, numbers, and the dash (-) character.
- Every dash (-) character must be immediately preceded and followed by a letter or number; consecutive dashes are not permitted in container names.
- All letters in a container name must be lowercase.
- Container names must be from 3 through 63 characters long.
- Avoid blob names that end with a dot (.), a forward slash (/), or a sequence or combination of the two.
S_G
Thank you for a clear and concise guide, it was very informative (especially for a new user such as myself). I would like to run this on Android 2.3.3 (API level 10). What changes must I focus on to enable this functionality?
Chris
I don't "believe" you'd need to make any changes. The Mobile Services SDK works with Android 2.2 and up so that shouldn't cause any issues. There may be some different method calls in the Table View but i believe it's all the same back to level 10.
Go_Fight_Nguyen
I would love to see this example in .net
Chris
You and me both. I've passed your request on to the guy on my team that does more of the .NET stuff. He's got it on his todo list, just a matter of time. In the mean time, there is this example which covers "some" of the same stuff: http://code.msdn.microsoft....
Go_Fight_Nguyen
Thanks
Mark Arnold
Chris - does your code in these articles still represent the preferred way to access tables/blobs through AZMS? Things change so fast. Also, do you know if the ability to send parameters is part of the Android AZMS SDK now?
Chris
As of right now, using the Azure module is still the best way to access table and blob storage from Mobile Services. When you say parameters, do you mean query string parameters or something else?
Mark Arnold
I'm speaking of the section titled Parameter Support. I'm wondering if your modifications have been merged into the official SDK yet. Thanks
Chris
Hi Mark, yes parameter support is now in the Android SDK so you don't need to work with my fork for it. As to accessing storage, the Azure module is still the way to go.
Kai
Thanks for a wonderful guide. Do keep up on the excellent documentation and focus on making things as easy as possible for developers -- this has convinced me to use Azure instead of AWS
Chris
Great to hear Kai. I'd love to hear more about what you're building with Azure. Feel free to reach out at anytime :)
Francisco Tineo
Thank you so much! This is a really wonderfull guide!
It works like a charm, even though I'm receiving a little error, this is the log from my mobile service:
Error
Error in script '/table/BlobContainers.insert.js'. Error: Container name format is incorrect.
[external code]
at insert (</table>:11:21)
at </table>:1:8
[external code]
As you can see, this happens every time I'm trying to start a Blob Storage Container.
At Eclipse I receive the following log: 03-19 16:52:44.518: E/StorageService(18253): {"code":500,"error":"Error: Internal Server Error"}
Doing a little experiment I found out that if I modify "BlobContainer" table's permission everything work, but when I set them back to default:
Insert, delete & update = Anyone with application key
Read = everyone
The problem came back and Eclipse shows the same error.
Any suggestions?
Thank you!
Chris
So the container name format error exists only when you secure the table script? It doesn't make much sense that the container name format would change given the table permissions. Can you try logging the container name when you have the table secured and when you don't and see if there is any difference?
ckw
I can't get this to work in Android. I keep getting a NoClassDefFoundError. Has anyone got any idea what I am doing wrong?
I have - downloaded the modified Azure SDK from GitHib, downloaded the example app from GitHub, imported both into my workspace in Eclipse and linked the two projects but when I run (either in the android emulator or on my phone) when it tries to instantiate StorageService I get the aforementioned exception. Any help would be much appreciated as I have been looking at this for ages.
ckw
Sorry, that should have read MobileServiceClient rather than StorageService.
Chris
You should be fine with the official SDK now. I would download the quickstart app from the portal and go from there.
dexternero
Hi - thanks for all the tutorials. I'm new to the game and learning a ton. Struggling to make this app work - probably making a stupid error.
Everything is fine through to the dialog where you select an image to upload. I am able to select an image but when I press create - to create the blob. Nothing happens when the create button is pressed. I checked the dashboard and the container seems to have been created but not the blob. I double checked I got all the scripts right. I can't see anything off in the client code - but then I'm not deep enough to error check that other than for basic mistakes. There are no build issues or error messages.
Any thoughts?
Thanks!
Chris
What happens when you step through the Android app int eh debugger? Are you getting the SAS URL back from your Mobile Service? Is the async task to actually do the upload starting?
seni
I had a same problem before. Is your Image file saved in external storage(SDCard)? then you need a permission in androidmenifest file.
sorry for my english.
Manish Ranjan
HI Chris,
Thanks a lot for the great example.
I have followed the detail and was successful able to run the application on Android device.
My query:"how i can update the existing blob image".
I couldn't find the script as well as code for updating the blob, though there are detail given for table update.
Could you please provide detail on blob image update please.
Deepak Rao
Hi Manish, Did u use android studio to create new app? Can you upload the code?
Lazaros Papadopoulos
Hi Chris! May I follow this tutorial nowadays along with the "Azure Mobile Apps" service or it is not applicable? Can I still combine Mobile Apps with Azure Storage ? thank you !
Chris
It should still work. You can also take a look at this: https://adrianhall.github.i...
Lazaros Papadopoulos
Thank you Chris! Very useful.