Your Android app and Amazons' DynamoDB database in the cloud: a tutorial

Android and DynamoDB

android dynamodb

DynamoDB

Amazon DynamoDb is a nonrelational database that is fast and flexible. It is a NoSQL database service with consistent, single-digit millisecond latency at any scale. DynamoDB is a managed cloud database supporting both document and key-value store models

You can use DynamoDB in your Android apps.

Android and DynamoDB: The two options

DynamoDB Object Mapper API

Using the DynamoDB Mapper API you would define a model in your app matching the model of your DynamoDB table. You then use the DynamoDBMapper class as the entry point to DynamoDB to access your data.

Document API

The Document API lets you access the individual attributes within data objects directly. You don’t have to map the DynamoDB table properties to your apps’ class members. The Document API acts as an interface between your app and the returned data.

The Document API has new data types to assist in the serialization and deserialization process.

So what’s the difference?

Both APIs have methods enabling you to perform create, update and delete operations as well as executing queries on your DynamoDB tables.

The difference between the two APIs is how the data is represented in your app. You can choose which API to use and you can even mix and match the two depending on your needs.

This tutorial uses the Document API to interact with our DynamoDB table.

Working with the Document Object

When using the Document API, your data is contained within a Document object which you pass to and from your DynamoDB table.

Documents and JSON

You can convert your Document objects to and from JSON objects however not all DynamoDB data types can be represented in JSON.

Use the Document.fromJson() and Document.toJson() methods to perform the conversion.

The Document object accepts the standard JSON types:

  • String
  • Number
  • Boolean
  • JSON arrays
  • JSON maps

The Document object also accepts the Amazon DynamoDB types:

  • binary data
  • null
  • ordered lists

The Document API does not support:

  • data types such as:
    • S3Link
    • Date
    • Calendar, etc.
  • Batch operations
  • Conditional expressions

Practical examples

For this tutorial, we assume that you have:

  • A DynamoDB table – if you don’t, see more on tables in the documentation. Or simply login to the Console and create a table. Choose your own Table name and enter Telephone as the Primary key (Partition key) and leave the data type as String. Leave the Use default settings box selected and press Create to create the table
  • A Cognito Identity Pool – users of your app will need credentials to access any AWS service. We won’t implement logging in but will allow guest users to get Cognito credentials so that we can access the DynamoDB data. So, if you don’t already have a Cognito Identity Pool, then create one. You can see our youtube tutorials on how to do that
  • An Android app

Setup

The manifest file

Add this permission to your manifest file:

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

build.gradle (Module:app) file

Also, add these dependencies to your build.gradle fle:

implementation 'com.amazonaws:aws-android-sdk-core:2.6.+'
implementation 'com.amazonaws:aws-android-sdk-ddb:2.6.+'
implementation 'com.amazonaws:aws-android-sdk-ddb-document:2.6.+'

Our custom classes

We have three custom classes: the Contact, DatabaseAccess and DummyData classes.

The Contact class

A simple class with two String variables: Telephone and name. We use it to construct a contact having a name and telephone number.

The DummyData class

We use this class to generate our dummy contacts. It has two String arrays: telephoneNumbers and names. These contain the telephone numbers and corresponding names of our dummy contacts.

The DummyData class has two methods:

  • getTelephoneNumbers() – which returns a String array of telephone numbers
  • getContacts() – which returns a List of contacts, each with a telephone number and name

The DatabaseAccess class

We create a DatabaseAccess Class which gives us an instance of a DatabaseAccess object. This enables us to access the class’ methods to interact with our DynamoDB table.

We’ve based this class on AWS – Android samples in GitHub:

https://github.com/awslabs/aws-sdk-android-samples

To build this class, you’ll need:

  • your Cognito Identity Pool Id
  • your identity pool region
  • your DynamoDB table name

Our DatabaseAccess class has the following methods:

  • getItem() – which returns an item from the table
  • getAllContacts() – which returns all the items from the table
  • deleteContact() – which deletes an item in the table
  • updateContact() – which updates an item in the table
  • addContact() – which adds an item to the table

Getting credentials to access DynamoDB

You cannot access any of AWS’ services, including DynamoDB without Cognito credentials. There are two ways of getting Cognito credentials:

  • users can either sign-in and then exchange their ID token for credentials
  • or unauthenticated, guest users can be issued with credentials. We use this option in our tutorial

Getting credentials: It all happens in our DatabaseAccess class

 We have this code in the DatabaseAccess constructor:

// Create a new credentials provider
credentialsProvider = new CognitoCachingCredentialsProvider(context, COGNITO_IDENTITY_POOL_ID, COGNITO_IDENTITY_POOL_REGION);
// Create a connection to the DynamoDB service
dbClient = new AmazonDynamoDBClient(credentialsProvider);
/*MUST SET db client REGION HERE ELSE DEFAULTS TO US_EAST_1*/
dbClient.setRegion(Region.getRegion(Regions.EU_WEST_1));
// Create a table reference
dbTable = Table.loadTable(dbClient, DYNAMODB_TABLE);

Note:
  • credentialsProvider – this provides the credentials for the given Identity Pool
  • dbClient – we use this client to access our DynamoDB table using the credentials provided by the credentialsProvider. As we are not handling signed-in users, these would be the credentials for a guest user
  • setRegion() – we must set the region for our DynamoDB table here else it will default to the US_EAST_1 region
  • dbTable – we get an instance of our table, configured for the given client and table name

Roles and Policies

You’ll also need a Policy for your Identity Pools’ unauthenticated role. Your Identity Pool gives your unauthenticated users credentials, which enables them to assume a Role. The role has a permissions policy attached to it which determines which AWS services and resources the guest users may access and what actions they may perform.

Here’s the important part of the policy that’s attached to our unauthenticated role:

"Effect": "Allow",
"Action": [
"dynamodb:PutItem",
"dynamodb:DescribeTable",
"dynamodb:Query",
"dynamodb:GetItem",
"dynamodb:Scan",
"dynamodb:DeleteItem",
"dynamodb:UpdateItem"
],
"Resource": "arn:aws:dynamodb:eu-west-1:xyz...xyz:table/your table"

An overview of the app

dynamoDb Android app screenshot

Our app has one Activity, the MainActivity. It displays a number of buttons and a text view:

  • Get all contacts – gets all the items out of the table
  • Delete contact – deletes an item in the table
  • Add contact – adds an item in the table
  • Get contact – gets an item out the table
  • Update contact – updates an item in the table
  • The text view displays a message while busy and the result when finished

Clicking a button performs the relevant operation in an AsyncTask. Busy, result and error messages are displayed in the logCat.

Interacting with the DynamoDB table

Let’s look at the methods we use to create, update, delete and query items in the DynamoDB table. All our DynamoDB methods are contained in the DatabaseAccess class.

We use an AsyncTask for all the DynamoDB methods as they need to be run off the main thread.

Adding a single item

Pressing the Add contact button gets a dummy contact and starts an AsyncTask to get the matching item out the table.

We need to pass a Document object containing the contact when we execute the AsyncTask so we convert the contact to a document object using Gson and the Documents’ fromJson() method. You could of course simply have used the Contact class’ getters to get the name and telephone numbers and then used the Document class’ put() method to add them to a new Document object.

We use the dummy contacts’ telephone number as our primary key.

To add an item, we use the DatabasAccess’ addContact() method. We pass it the dummy contact which contains the primary key, the telephone number and a name.

We use the Document APIs put() method to add attributes to the document object, which in this case is the dummyContact.

We then call:

Document result = dbTable.putItem(dummyContact, putItemOperationConfig);

to put the item in the table. Note the following:

  • dbTable - our table object
  • putItem() - a method of the com.amazonaws.mobileconnectors.dynamodbv2.document.Table class
  • dummyContact - our Document object containing all our attributes
  • putItemOperationConfig – allows us to configure the putItem operation. We use the withReturnValues() option to specify that we want the completed operation to return ALL_OLD values. This will return any existing attributes content if they are overwritten (i.e. the old data). Supported values are:
    • NONE - This is the default. Nothing is returned
    • ALL_OLD – returns the old content of any overwritten attributes
  • result – this is a Document object containing the result of the operation. In this case it would be any old data but only if these attributes content were overwritten

The result document is then passed back to the AsyncTasks’ onPostExecute() method where it is processed.

Getting a single item

We need the primary key of the item that we want to get from the table. When pressing the Get item button, we get a random telephone number (our primary key) and pass it to an AsyncTask which uses our DatabaseAccess class’ getItem() method to get the matching item for the given primary key (the telephone number).

The DatabaseAccess method, getItem() then gets the item using this line of code:

Document result = dbTable.getItem(new Primitive(telephone));

Note:

  • result - we get the requested item result in the form of a Document object
  • dbTable – a reference to our DynamoDb table
  • getItem – gets the item in the table for the given primary key and returns it as a Document object
  • Primitive – we convert our String telephone variable to a DynamoDb Primitive object. A Primitive object is a DynamoDBEntry representing a scalar DynamoDB type. “A scalar type can represent exactly one value. The scalar types are number, string, binary, Boolean, and null.”

Getting all the items

Pressing the Get all contacts button starts an AsyncTask which calls the DatabaseAccess class’ getAllContacts() method.

There are two methods that you can use to search your table:

  • query()- which uses indexed fields within your table to quickly find the data you’re looking for. However, you need to know the index key for the item that you are looking for
  • scan() – searches every item in the table. This can be slow for large tables

Heres’ the code that we used:

public List<Document> getAllContacts() {
/*using scan to get all contacts*/
ScanOperationConfig scanConfig = new ScanOperationConfig();
List<String> attributeList = new ArrayList<>();
attributeList.add("Telephone");
attributeList.add("name");
scanConfig.withAttributesToGet(attributeList);
Search searchResult = dbTable.scan(scanConfig);
return searchResult.getAllResults();
}

Note:

  • ScanOperationConfig – enables us to configure the scan operation
    • withAttributesToGet – the attributes that we want returned for each returned item
  • searchResult – contains all the results
    • getAllResults() – returns a List of Document objects, each containing an items’ attributes as defined in ScanOperationConfig

Deleting a single item

To delete an item, we need the items’ primary key. Pressing the Delete item button gets a random telephone number, which we pass on to an AsyncTask to delete the associated item from the table.

In the AsyncTask we call the DatabaseAccess class’ deleteContact() method.

In the deleteContact() method we call the Table class’ deleteItem() method, like this:

Document result = dbTable.deleteItem(new Primitive(string), new DeleteItemOperationConfig().withReturnValues(ReturnValue.ALL_OLD));

Note:

  • DeleteItemOperationConfig – enables us to configure the deletItem() operation
  • withReturnValues – specifies which values we want returned. The deletItem() method only supports these return values:
    • ALL_OLD – returns the deleted items’ content
    • NONE – this is the default. Nothing is returned

Updating an item

We need the primary key of the item that we want to update. Pressing the Update item button gets a random telephone number which we pass to an AsyncTask.

We call the DatabaseAccess class’ updateContact() method, passing the telephone number as a parameter.

In the updateContact() method, we get the matching item for the telephone primary key as a Document object. We then update a number of attributes in the retrieved document. Note that you can only update existing attributes.

We then update the item in the table with this code:

Document updateResult = dbTable.updateItem(retrievedDoc, new Primitive(telephone)
, new UpdateItemOperationConfig().withReturnValues(ReturnValue.UPDATED_NEW));

Note:

  • updateItem() – updates the given document (our retrievedDoc) in the table
  • UpdateItemOperationConfig – enables us to configure the updateItem operation
    • withReturnValues – the values that we want returned. Use ReturnValues if you want to get the item attributes as they appear before or after they are updated. Supported values are:
      • NONE – nothing is returned. This is the default
      • ALL_OLD – returns all attributes as they were before the update
      • UPDATED_OLD – returns only updated attributes as they were before the update
      • ALL_NEW – returns all the attributes as they appear after the update
      • UPDATED_NEW – returns only the updated attributes as they are after the update

Check out our DynamoDB and Android youtube tutorial

You can download the code here download35