MBaaS徹底入門――Kii Cloudでスマホアプリ開発

第5回Kii Cloudを用いたチャットアプリケーションの開発その1]―ユーザサインアップ/サインインの実装

前回はKii Cloudを用いたアプリケーション開発環境のセットアップを行いました。そしていよいよ今回から、Kii Cloudを用いた「チャットアプリケーション」の開発を行っていきます。

この連載で開発したアプリケーションについては、GitHub上にて全体のコードをApache License, Version 2.0のライセンスで公開いたします。アプリケーションのコードや細かい動作など、お手持ちの開発環境や端末にてぜひ確認してみてください。

チャットアプリケーションの機能

今回開発するチャットアプリケーションの機能は下記の4つです。チャットアプリケーションとして機能するよう最低限の機能のみに絞っています。

これらの機能は今回の連載を通じて1つずつ実装していきます。

  • ユーザサインアップ(サインイン)
  • 友達追加
  • チャットルーム作成
  • メッセージ送受信

チャットアプリケーションの設計・全体像

チャットアプリケーションとして必要な機能を実現するために、まずはアプリケーションの全体像を決定し、バケットならびにトピック間のデータフロー設計を行います。

「バケット」は第3回でも登場しましたが、オブジェクトと呼ばれるKey-Valueで構成されるJSONデータを格納する入れ物の役割を果たします。データを柔軟に扱うのに最適です。バケットにはKey-ValueのJSONデータだけではなく、動画や音楽などのファイルを紐づけて保存することもでき、最新のAndroid Cloud SDKでは分割ダウンロード・アップロードにも対応しています。また条件を指定したクエリを送信して、オブジェクトを検索・取得することもできます。

一方「トピック」は、プッシュメッセージを送信するためのチャンネルの役割を果たします。データを保存する必要のない、通知を目的としたメッセージの購読・送信に最適です。ユーザはこのトピックを購読することにより、トピックに送信されたメッセージを受信することができるようになります。トピックもバケットと同様に、スコープ(デフォルトのアクセス制御)を持っているため、アクセス制御を柔軟に設定することができます。

これらの「バケット」⁠トピック」の性質を考慮しつつ、チャットアプリケーションに最適な構成とします。今回はKii Cloud上にバケット・トピックを下記のように配置します。

チャットアプリケーションのバケット・トピック
チャットアプリケーションのバケット・トピック

chat_users(アプリケーションスコープ:バケット)

チャットアプリケーションに登録しているユーザを格納します。

アプリケーションスコープのバケットは全てのユーザから閲覧・検索が可能なため、アプリケーション上では友達を検索するために利用します。

chat_room(グループスコープ:バケット)

チャットルームを表すバケットです。1つのバケットで1チャットルームとします。

このバケットにメッセージをオブジェクトとして保存し、チャットのログとして残すようにします。

chat_friends(ユーザスコープ:バケット)

友達を登録するバケットです。友達を追加した場合にこのバケットへ情報を格納し、友人リストとして動作するようにします。ユーザスコープのバケットのため、ログインしているユーザのみこの情報を参照することができます。

invite_notify(ユーザスコープ:トピック)

チャットルームへの招待通知を行うためのトピックです。

初回のサインアップ時にこのトピックを作成・購読します。他のユーザがチャットルームへの招待を行った際に、このトピックを通じて通知が行われるようにします。

このようにバケット・トピックをスコープごとに適切に利用することで、それぞれのアクセス権を初めから1つずつ設定する必要なく、簡単にデータの公開設計ができます。

次に各機能ごとに、データフローの設計を行います。

ユーザサインアップ(サインイン)

サインアップ時のデータフロー
サインアップ時のデータフロー

ユーザサインアップ時には、下記の動作を行うようにします。

  1. ユーザを新規に作成し、Kii Cloudへ登録を行う。
  2. chat_usersバケットに自身の情報を登録し、他のユーザから検索できるようにする。
  3. 他のユーザから通知を受け取るために自分専用の invite_notify トピックをユーザスコープに作成する。ただしユーザスコープのトピックは、デフォルトのACLで他のユーザからメッセージが送信できない設定となっているため、追加でACLを設定し、他のユーザからメッセージが送信できるように設定する。

こうすることでユーザの検索が可能になり、他のユーザからチャットルームへ招待された際の通知を受信することができるようになります。

友達追加

友達追加時のデータフロー
友達追加時のデータフロー

友達追加時には、下記の動作を行うようにします。

  1. アプリケーションに登録しているユーザ一覧がアプリケーションスコープの ⁠chat_users⁠⁠ バケットに格納されているため、このバケットに対してユーザの検索を行う。
  2. 検索結果から追加したいユーザを選択し、ユーザスコープの ⁠chat_friends⁠⁠ バケットに友達リストに表示する人として追加する。

こうすることで友人を検索し、友達リストに追加することができます。

チャットルーム作成

チャットルーム作成時のデータフロー
チャットルーム作成時のデータフロー

チャットルーム作成時には、下記の動作を行うようにします。

  1. UserA(チャットを開始する人)は、まずグループを作成し、UserB(チャットに招待された人)をグループのメンバーとして追加する。その後、チャットメッセージを保存するためのバケットをKii Cloud上のグループスコープに作成する。
  2. UserAはこのバケットを購読し、他のユーザからチャットメッセージが送信された場合にプッシュ通知を受信できるようにする。
  3. UserB(チャットに招待された人)に対して、invite_notifyトピックを通じてチャットへ招待されたことを通知する。この通知には作成されたグループの情報を付加し、どのチャットに招待されたか判別できるようにする。
  4. invite_notifyに送信されたメッセージはプッシュ通知(GCM)によりKii CloudからUserB(チャットに招待された人)に対して通知される。
  5. 招待通知メッセージに含まれているグループの情報より、チャットを行うバケットを特定し購読を行う。これによって他のユーザからメッセージが送信された場合にプッシュ通知を受信できるようにする。

こうすることでチャットルームを作成することができ、メッセージが送信されたことをプッシュ通知によってリアルタイムで通知することができるようになります。

メッセージ送受信

メッセージ送受信時のデータフロー
メッセージ送受信時のデータフロー

メッセージ送受信時には、下記の動作を行うようにします。

  1. UserA(メッセージ送信者)は、チャットのメッセージをチャットルーム作成時に作成したグループスコープのバケットにオブジェクトとして保存する。
  2. UserB(メッセージ受信者)は、チャットルームへ招待された際にバケットを購読している。そのため、バケットに新しいオブジェクトが追加されたタイミングでサーバーから通知が送られる。
  3. UserB(メッセージ受信者)はプッシュ通知をトリガーとして、新しいメッセージをバケットから取得する。

こうすることでメッセージを受信者・送信者間でやりとりすることができます。

このように今回実装するチャットアプリケーションの4つの機能についてデータフローの設計を行いましたが、サーバ側の実装が無視できないくらい大きなウェイトを占めていることが見て取れます。

しかしながら、Kii Cloudに実装されているバケット・トピックの機能を利用することでこれらのサーバーの設置や実装が必要なくなるため、短期間でのアプリケーションの開発や、品質の高いクライアントの開発に専念することができます。

アプリケーション初期化コードの実装

Android Cloud SDKの各機能を使用する前に、Android Cloud SDKの初期化メソッドを呼ぶ必要があります。その初期化コードの実装を下記の手順で行います。

チャットアプリケーションでは、KiiChatApplicationクラスのonCreate()メソッド内に初期化メソッド(Kii.initializeメソッド)を設置しています。これによりアプリケーション実行時にAndroid Cloud SDKが初期化され、Android Cloud SDKの各機能が使用できます。

リスト Android Cloud SDKの初期化メソッド
// Android Cloud SDKを初期化!
Kii.initialize(ApplicationConst.APP_ID, ApplicationConst.APP_KEY, Site.JP);

初期化メソッドには下記の引数を与えます。

  • AppID(アプリケーションID)
  • AppKey(アプリケーションKEY)
  • Site(サーバーロケーションを表すenum)

これらの情報は開発者ポータルから確認できます。作成したアプリケーション名をクリック後、⁠Access Keys」ボタンからAppIDとAppKeyを、⁠Edit」ボタンからロケーションをそれぞれ確認できます。

AppID、AppKeyの確認
AppID、AppKeyの確認
サーバーロケーションの確認
サーバーロケーションの確認

ここで注意しなくてはならないのは、ClientID、ClientSecretの扱いです。AppID、AppKeyの下に確認できるClientID、ClientSecretは、アプリケーションの管理者権限を取得するために必要な情報です。そのため絶対に外部に漏らしてはいけません。Androidアプリケーションに組み込むと大変危険です。ご注意下さい。

サインアップの実装

ユーザを利用するアプリケーションの要である「サインアップ」の実装を行っていきましょう。サインアップ/サインインのフローは下記のようにします。

サインアップ/サインインのフロー
サインアップ/サインインのフロー

ユーザに入力してもらった値を元に、サインアップできるように実装します。サインアップ時には下記の情報を元にユーザを作成します。

  • Email(ユーザ識別子として入力。サインイン時、ならびにユーザ検索時に利用する)
  • パスワード(サインインで利用するパスワード)
  • ユーザネーム(チャットで利用する名前)

またサインアップでは「ユーザサインアップ(サインイン⁠⁠」で示したフローとなるよう、下記の動作もあわせて組み込みます。

  • chat_users バケットに自分自身を登録
  • GCMのregistration IDをKii Cloudに登録
  • invite_notify トピックの作成
  • invite_notify トピックのACLの設定
  • invite_notify トピックの購読

これらを元にして、SignupDialogFragment内のAsyncTaskでサインアップできるよう下記のように実装します。

なお、ここに掲載しているコードは内容をわかりやすくするため、GitHubに掲載しているものと少しコメントの内容・位置が異なっております。あらかじめご了承ください。

private class SignupTask extends AsyncTask<Void, Void, Boolean> {
        
    private final String username;
    private final String email;
    private final String password;
        
    private SignupTask(String username, String email, String password) {
        this.username = username;
        this.email = email;
        this.password = password;
    }

    @Override
    protected void onPreExecute() {
        ProgressDialogFragment.show(getFragmentManager(), "Signup", "Processing...");
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        try {
            // KiiUserのサインアップ処置
            KiiUser.Builder builder = KiiUser.builderWithEmail(email);
            KiiUser kiiUser = builder.build();
            kiiUser.setDisplayname(username);
            kiiUser.register(password);
            Logger.i("registered user uri=" + kiiUser.toUri().toString());

            // 登録したKiiUserをChatUserとしてAppスコープのバケツに保存しておく(検索用)
            ChatUser user = new ChatUser(kiiUser.toUri().toString(), username, email);
            user.getKiiObject().save();

            // プッシュ通知で利用するGCM registration IDの送信
            String registrationId = GCMUtils.register();
            KiiUser.pushInstallation().install(registrationId);

            // ユーザトピックの作成
            KiiTopic topic = KiiUser.topic(ApplicationConst.TOPIC_INVITE_NOTIFICATION);
            topic.save();

            // ACL設定
            KiiACL acl = topic.acl();
            acl.putACLEntry(new KiiACLEntry(KiiAnyAuthenticatedUser.create(), TopicAction.SEND_MESSAGE_TO_TOPIC, true));
            acl.save();

            // トピックの購読
            KiiPushSubscription subscription = kiiUser.pushSubscription();
            subscription.subscribe(topic);
            return true;
        } catch (Exception e) {
            Logger.e("failed to sign up", e);
            return false;
        }
}

    @Override
    protected void onPostExecute(Boolean result) {
        ProgressDialogFragment.hide(getFragmentManager());
        if (result) {
            // サインアップ処理が正常に行われた場合は、コールバックメソッドで呼び出し元に通知
            OnSignupListener listener = onSignupListener.get();
            if (listener != null) {
                listener.onSignupCompleted();
            }
        } else {
            ToastUtils.showShort(getActivity(), "Unable to sign up");
        }
        dismiss();
    }
}

実際のサインアップ処理はdoInBackgroundで実行しています。少し長いので1つずつ見ていきましょう。

1.ユーザサインアップ

// KiiUserのサインアップ処理
KiiUser.Builder builder = KiiUser.builderWithEmail(email);
KiiUser kiiUser = builder.build();
kiiUser.setDisplayname(username);
kiiUser.register(password);

ユーザに入力してもらった情報を元にユーザを作成し、登録を行います。

  • KiiUser.builderWithEmail(java.lang.String email):Emailを元にKiiUserを作成します。この時点ではインスタンスが作成されたのみで、Kii Cloudへの保存はされていません。
  • KiiUser#setDisplayname(java.lang.String displayname):KiiUserのディスプレイネームの項目にUsernameを設定します。
  • KiiUser#register(java.lang.String password) :KiiUserをKii Cloudへ登録し、サインアップします。

2.chat_users バケットに自分自身を登録

// 登録したKiiUserをChatUserとしてAppスコープのバケツに保存しておく(検索用)
ChatUser user = new ChatUser(kiiUser.toUri().toString(), username, email);
user.getKiiObject().save();

次は他のユーザから検索可能となるように自分自身の情報をアプリケーションスコープのバケットに登録します。

登録したKiiUserの情報、ユーザ名、emailを元に、ChatUserのインスタンスを作成します。その後、アプリケーションスコープのバケットにユーザのURI、ユーザ名、emailをオブジェクトとして登録します。これで他のユーザから検索ができるようになります。

この1・2によって「ユーザサインアップ/サインイン」データフローの1つ目が完成しました。

3.GCMのregistration IDをKii Cloudに登録

// プッシュ通知で利用するGCM registration IDの送信
String registrationId = GCMUtils.register();
KiiUser.pushInstallation().install(registrationId);

次はプッシュ通知をKii Cloudから送信できるように、GCMから取得したregistration IDをKii Cloudへ登録します。

4.invite_notify トピックの作成

// ユーザトピックの作成
KiiTopic topic = KiiUser.topic(ApplicationConst.TOPIC_INVITE_NOTIFICATION);
topic.save();

次は "invite_notify" というユーザスコープトピックを作成し、そのトピックへサインアップ済みのユーザからチャット招待通知が送信できるようにACLの設定を行います。

まずは、KiiUser.topic(java.lang.String topicName) でユーザスコープのトピックを作成し、KiiTopic#save() でKii Cloudにトピックを作成します。

5.invite_notify トピックのACL設定

// ACL設定
KiiACL acl = topic.acl();
acl.putACLEntry(new KiiACLEntry(KiiAnyAuthenticatedUser.create(), TopicAction.SEND_MESSAGE_TO_TOPIC, true));
acl.save();

次にACL設定です。ACLはホワイトリスト形式のため、アクセスを許可するエントリを入力します。

今回はサインアップ済みのユーザ(KiiAnyAuthenticatedUser)からのみトピックへのメッセージ送信(TopicAction.SEND_MESSAGE_TO_TOPIC)を許可するエントリを作成し、トピックのACLに作成したエントリを設定します。最後に KiiACL#save() し、Kii Cloudへの反映を忘れないようにしてください。

6.invite_notify トピックの購読

// Topicの購読
KiiPushSubscription subscription = kiiUser.pushSubscription();
subscription.subscribe(topic);

最後に、先程作成した "invite_notify" トピックを購読します。これによって他のユーザからチャットルームへ招待メッセージが送信された際、端末に通知が送信されるようになります。

KiiUser#pushSubscription() でsubscriptionのインスタンスを作成し、先程作成したtopicを引数にして KiiPushSubscription#subscribe(KiiTopic topic) をコールすることで、対象のトピックを購読することができます。

この3~6によって「ユーザサインアップ/サインイン」データフローの2つ目が完了し、ユーザサインアップ/サインインのメインフローが実装できました。これでサインアップの機能は完成です。

ここでのポイントは、Android Cloud SDKのAPIは全てブロッキングAPIを利用していますが、メインスレッドをブロックしないようにAsyncTaskを利用している点です。

Android Cloud SDKのAPIはブロッキングAPIとノンブロッキングAPIの双方を提供しています。実装する局面にあわせてこれらのAPIを上手にご活用ください。

サインインの実装

ここからは一度サインアップしたユーザが同じユーザで利用できるよう、サインインの実装を行います。

チャットアプリケーションでは2種類のログイン方法を提供します。

  • サインアップ時に利用したユーザ識別子・パスワードによる認証
  • サインアップ・サインイン時に取得したトークンによる認証

それぞれについて実装していきましょう。

サインアップ時に利用したEmail・パスワードによる認証

サインアップ時に利用したEmail・パスワードでログインできるように実装します。

ユーザ識別子・パスワードを用いてログインするメソッド:KiiUser.logIn(KiiUserCallBack callBack, java.lang.String userIdentifier, java.lang.String password) を利用し、下記のように実装します。

KiiUser.logIn(new KiiUserCallBack() {
    @Override
    public void onLoginCompleted(int token, KiiUser user, Exception e) {
        if (e != null) {
            // Something to do.
            return;
        }
        if (checkRemember.isChecked()) {
            // ログイン状態を保持する場合は、SharedPreferencesにAccessTokenを保存する
            Logger.i(user.getAccessToken());
            PreferencesManager.setStoredAccessToken(user.getAccessToken());
        }
    }
}, email, password);

AsyncTaskを利用していないため、Callbackを使ったメソッドを利用しています。メインスレッドをブロックしないように注意してください。

サインインが完了した場合、KiiUserCallback()中のonLoginCompletedにより結果が返されます。まずはExceptionを確認し、何か問題が発生していないか必ず確認してください。

もしサインインに成功している場合は、サインイン状態を保持するオプションを確認のうえ、アクセストークンをSharedPreferencesに保存します。このアクセストークンは次の項目で説明する「アクセストークンによる認証」で必要になります。

サインアップ・サインイン時に取得したアクセストークンによる認証

サインアップ・サインイン時に取得したアクセストークンで認証できるように実装しましょう。

アプリケーションをユーザが利用する際、サインインの手間を省きたいユーザにとって、毎回Email・パスワードでログインするのはユーザビリティの面でよくありません。そのため、サインイン状態を保持するオプションを選択したユーザに対してアクセストークンによる認証を用意しましょう。

方法としては、サインアップ・サインイン時に取得したアクセストークンをSharedPreferencesで保持しておき、次回以降のサインインでそのアクセストークンを利用するようにします。

アクセストークンを用いてログインするメソッド:KiiUser.loginWithToken(KiiUserCallBack callBack, java.lang.String token) を利用し、下記のように実装します。

KiiUser.loginWithToken(new KiiUserCallBack() {
    @Override
    public void onLoginCompleted(int token, KiiUser user, Exception e) {
        if (e == null) {
            // サインイン成功時はチャット画面に遷移
            Intent intent = new Intent(MainActivity.this, ChatMainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
        } else {
            // サインイン失敗時はサインイン画面に遷移
            PreferencesManager.setStoredAccessToken("");
            Intent intent = new Intent(MainActivity.this, SigninActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
        }
        ProgressDialogFragment.hide(getSupportFragmentManager());
    }
}, token);

AsyncTaskを利用していないため、Callbackを使ったメソッドを利用しています。

サインインが完了した場合、Email・パスワードによる認証と同様に、KiiUserCallback() 中の onLoginCompleted により結果が返されます。ここでもまずExceptionを確認し、何か問題が発生していないか必ず確認してください。

もしサインインに成功した場合は、チャットのメイン画面へ遷移させます。しかしサインインに失敗した場合(アクセストークンで認証できなかった場合⁠⁠、改めてEmail、パスワードによる認証が必要なため、サインインの画面に遷移させるようにします。

これでサインインの機能は完成しました。アプリケーションを起動させて動作を確認してみてください。

以上で、サインアップ/サインインの機能が実装できました。

まとめ

ユーザサインアップ/サインインの実装を終えて、いかがでしたでしょうか。

ユーザ管理が必要なアプリケーションでは、サーバーもクライアントもそれぞれの知識が必要なうえ、両方開発するとなると大きく時間を削られてしまいます。しかし、Kii Cloudを利用するとクライアント開発のみに専念でき、かつ数行でユーザ管理が可能なアプリケーションが作成できることが実コードを通じて感じていただけたのではないでしょうか。

本連載ではAndroidを対象としていますが、iOS、Javascript(REST)向けのSDKでも同様の実装をすることができます。また8月21日からUnity向けのSDKを提供開始しました。是非こちらもお試しください。

次回は主要機能の2つ目「友達追加」部分について実装を行っていきます。どうぞお楽しみに。

おすすめ記事

記事・ニュース一覧