読者です 読者をやめる 読者になる 読者になる

AWS Mobile Application Development

AWSのサービスをモバイルから使うにあたって調べた情報などまとめて行きます。

AndroidとDynamoDB実装方法 (4/4) - コーディング -

AWSのNoSQLデータベースDynamoDBをDBとしてデータ操作を行うための基本的な設定のご紹介です。

AndroidとDynamoDB実装方法 (3/4) - DynamoDB初期設定 -に続き、プログラミングになります。

MainActivity.javaと、DynamoDBのテーブルクラスを定義したBooks.javaの2ファイルからの構成となるプログラミングを行います。

[目次]

コード

Books.java

package com.example.sample.dynamodb_sample;

import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.*;
import com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.*;

/* Dynammoテーブル名*/
@DynamoDBTable(tableName = "Books")
public class Books {

    private String author;
    private String title;
    private int price;
    private String releasedate;

  /* パーティションキーで指定した属性名 */
    @DynamoDBHashKey(attributeName = "Author")
    public String getAuthor() {return author; }
    public void setAuthor(String author) { this.author = author; }

  /* ソートキーで指定した属性名 */
    @DynamoDBRangeKey(attributeName = "Title")
    public String getTitle() {return title; }
    public void setTitle(String title) { this.title = title; }

  /* 任意の属性名 AWSコンソールで事前定義不要*/
    @DynamoDBAttribute(attributeName = "Price")
    public int getPrice() {return price; }
    public void setPrice(Integer price) { this.price = price; }

  /* 任意の属性名 AWSコンソールで事前定義不要*/
    @DynamoDBAttribute(attributeName = "Releasedate")
    public String getReleasedate() {return releasedate; }
    public void setReleasedate(String releasedate) { this.releasedate = releasedate; }

}

プログラム内で記載している以下はAWSコンソールで事前定義をしたDynamoDBのマッピング定義で必ず定義が必要なアノテーションです。

@DynamoDBTable(tableName = "Books")
@DynamoDBHashKey(attributeName = "Author")
@DynamoDBRangeKey(attributeName = "Title")

以下は、AWSコンソールで事前定義をしたものではなく、本アプリで使用し得る属性名として定義しているアノテーションです。

@DynamoDBAttribute(attributeName = "Price")
@DynamoDBAttribute(attributeName = "Releasedate")

他にも色々とアノテーションを定義するルールが以下に記載されています。
docs.aws.amazon.com

MainActivity.java

package com.example.sample.dynamodb_sample;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.regions.Region;
import com.amazonaws.services.dynamodbv2.*;
import com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.*;

import java.util.Date;

public class MainActivity extends AppCompatActivity {

    private Books book;
    private Runnable runnable;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        // (1) Cognitoで出力した認証定義です
        CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
                getApplicationContext(),
                "ap-northeast-1:xxxxxxxxxxxxxxxxxxxxxxxxxxx
", // Identity Pool ID
                Regions.AP_NORTHEAST_1 // Region
        );

        // (2) DynamoDBのクライアントセッションを作成します
        AmazonDynamoDBClient ddbClient = new AmazonDynamoDBClient(credentialsProvider);

        // (3) デフォルトではUS-EASTがリージョンで指定されてしまうため意図的にAP_NORTHEASTにしています
        Region apNortheast1 = Region.getRegion(Regions.AP_NORTHEAST_1);
        ddbClient.setRegion(apNortheast1);

        // (4) マッパーのインスタンス作成
        final DynamoDBMapper mapper = new DynamoDBMapper(ddbClient);

        // (5) インターネットのアクセスを行うためにスレッドを作成し、その中でインサート処理を定義します
        runnable = new Runnable() {
            public void run() {
                Books book = new Books();
                book.setAuthor("Charles Dickens");
                book.setTitle("Great Expectations");
                book.setPrice(20000);
                Date date = new Date();
                book.setReleasedate(date.toString());
                mapper.save(book);
            };
        };

        Thread mythread = new Thread(runnable);
        mythread.start();

    }

}

コード解説

(1) AWSの認証

AndroidとDynamoDB実装方法 (2) - Cognito初期設定 - - AWS Mobile Application Developmentで取得した「Get AWS Credentials」を定義して認証処理を行います。

(2) DynamoDBのクライアントセッション作成

(1)でDynamoDB用のクライアントセッションを作成します。

(3) リージョンの指定

デフォルトではUS-EASTがリージョンで指定されてしまうため、これを設定しないと以下のエラーが発生します。その為、意図的にAP_NORTHEASTにしています。

E/AndroidRuntime: FATAL EXCEPTION: Thread-9
                  Process: com.example.sample.dynamodb_sample, PID: 2269
                  com.amazonaws.AmazonServiceException: User: arn:aws:sts::xxxxxxxxxxxxx:assumed-role/Cognito_DynamoTestUnauth_Role/CognitoIdentityCredentials is not authorized to perform: dynamodb:UpdateItem on resource: arn:aws:dynamodb:us-east-1:xxxxxxxxxxxxxx:table/Books (Service: AmazonDynamoDB; Status Code: 400; Error Code: AccessDeniedException; Request ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)

(4) マッパーのインスタンス作成

DynamoDBクライアントセッション情報を元に、DynamoDBのマッパーインスタンスを作成します。

(5) 別スレッドでDB処理を行う

Androidではインターネット処理をメインスレッド(UIスレッド)で行ってはいけません。ワーカスレッドを作成しそれ上で処理を行う必要があります。これを行わなかった場合以下のエラーが発生します。

E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: com.example.sample.dynamodb_sample, PID: 3056
                  java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sample.dynamodb_sample/com.example.sample.dynamodb_sample.MainActivity}: android.os.NetworkOnMainThreadException

Mainスレッドとは別でインターネットの処理し結果をメインスレッドへコールバックをすることがAndroidの基本です。実現方法法として、「Runnable」と「AsyncTask」がありますので「Runnable」は上記に書いていますので「AsyncTask」実装例を以下に記載します。

package com.example.sample.dynamodb_sample;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.regions.Region;
import com.amazonaws.services.dynamodbv2.*;
import com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.*;

import java.util.Date;

public class MainActivity extends AppCompatActivity {

    private CognitoCachingCredentialsProvider credentialsProvider;
    private static final String TAG = MainActivity.class.getSimpleName();

    private Books book;
    private DynamoDBMapper mapper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // (1) Cognitoで出力した認証定義です
        CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
                getApplicationContext(),
                "ap-northeast-1:xxxxxxxxxxxxxxxxxxxxxxx", // Identity Pool ID
                Regions.AP_NORTHEAST_1 // Region
        );

        // (2) DynamoDBのクライアントセッションを作成します
        AmazonDynamoDBClient ddbClient = new AmazonDynamoDBClient(credentialsProvider);

        // (3) デフォルトではUS-EASTがリージョンで指定されてしまうため意図的にAP_NORTHEASTにしています
        Region apNortheast1 = Region.getRegion(Regions.AP_NORTHEAST_1);
        ddbClient.setRegion(apNortheast1);

        // (4) マッパーのインスタンス作成
        mapper = new DynamoDBMapper(ddbClient);

        new db().execute("");

    }

    //AsyncTask<Params, Progress, Result>
    private class db extends AsyncTask<String, Integer, String> {

        /** doInBackgroundメソッドの実行前にメインスレッドで実行されます。
         非同期処理前に何か処理を行いたい場合に使用。
         */
        @Override
        protected void onPreExecute() {
            Log.i(TAG,"onPreExecuted ....");
        }

        /** メインスレッドとは別のスレッドで実行されます。
        非同期で処理したい内容を記述します。記載必須。
         */
        @Override
        protected String doInBackground(String... params) {
            Log.i(TAG,"doInBackground ....");

            Books book = new Books();

            int i = 0;

            for(i = 0; i < 20; i++){
                try {
                    Thread.sleep(1500);
                    book.setAuthor("村上春樹");
                    book.setTitle("1Q24" + "第" + i + "版");
                    book.setPrice(20000);
                    Date date = new Date();
                    book.setReleasedate(date.toString());
                    mapper.save(book);

                    publishProgress(i);

                }catch(InterruptedException e){
                    Toast.makeText(MainActivity.this, "Error", Toast.LENGTH_SHORT).show();
                }
            }
            /* onPostExecute(String s) の s に "Executed" が入ります */
            return "Executed";
        }

        /**メインスレッドで実行されます。
         非同期処理の進行状況をプログレスバーで 表示したい時などに使うことができます。
         */

        @Override
        protected void onProgressUpdate(Integer... values) {
            Log.i(TAG,"onProgressUpdate .... " + values[0]);
        }

        /**doInBackgroundメソッドの実行後にメインスレッドで実行されます。
         doInBackgroundメソッドの戻り値をこのメソッドの引数として受け取り、
         その結果を画面に反映させることができます。
         */
        @Override
        protected void onPostExecute(String s) {
            Log.i(TAG,"onPostExecute ....return value is " + s);
        }

    }
}

developer.android.com

[参考]