プログラムの実装

設定後、モバイルアプリに初期化処理とプッシュメッセージの受信ハンドラーを追加します。

全体の構成

チュートリアルの冒頭でも示したように、このチュートリアルでは、Google 社から提供されている GCM のサンプルコードを元に、Kii Cloud 用に改変したものを使って実装方法を説明します。オリジナルのサンプルコードは以下のとおりです。

参考ソース:https://github.com/googlesamples/google-services

GCM のサンプルコード、および、本チュートリアルでは、以下のような機能を実装しています。

  • GCM のデバイストークンの取得:Android のサービスとしてデバイストークンの取得処理を実装するため、実装の独立性が高まるとともに、処理が安定して予期しない動作を防止できます。
  • メッセージ受信時の処理:受信処理がサービス化されているため、ネットワークアクセスなどの時間がかかる処理を記述しても安全に実行できます。
  • デバイストークン変更の検出:GCM 側でデバイストークンが変更された場合にも、プッシュ通知の対象デバイスを自動的に再登録できます。
  • Google Play services のインストール確認:動作対象外の環境を検知して、エラーを表示できます。

クラス構成

最終的な実装は、以下の図に示すクラス構成となります。

  • プログラムの開始時など、適当なタイミングで RegistrationIntentService のサービス開始 Intent を実行します(Kii Cloud ではログイン完了時が適切です)。RegistrationIntentService では、デバイストークンの取得や Kii Cloud への登録処理を行います。
  • RegistrationIntentService の初期化が完了すると、ブロードキャストによって初期化の完了(または失敗)を通知します。BroadcastReceiver を実装しておくと、完了通知を受信できます。
  • InstanceIDListenerService のサブクラスとしてサービスを実行しておくと、GCM 側でデバイストークンが変更されたとき、それを onTokenRefersh() メソッドによって受信できます。このハンドラーでは、RegistrationIntentService のサービス開始 Intent によって、デバイストークンの更新処理を実行します。
  • GCMReceiver クラスによって、プッシュメッセージの受信処理を行います。このクラスは Android SDK によって提供されるクラスです。AndroidManifest.xml の定義によって、GCMListenerService のサブクラスに受信時の処理が伝えられます。
  • GCMListenerService のサブクラスでは、onMessageReceived() メソッドの実装にプッシュメッセージを受け取ったときの処理を記述することができます。
  • Android では、プッシュメッセージを受け取ってもユーザーインターフェイスへの表示は行われません。表示したい場合は、受信処理として、ステータスバーへの表示処理を記述する必要があります。その際 PendingIntent によってメインとなる Activity を起動するようにしておけば、ステータスバーのクリックによって Activity を起動することができます。

GCM ではメッセージのパラメータの組み合わせによって onMessageReceived() メソッドを呼ばず、ステータスバーに直接通知を表示するケースを用意していますが、Kii Cloud ではこのパラメータをサポートしません。常に onMessageReceived() メソッドが呼び出されます。

初期化開始処理の実装

デバイストークンをインストールする前に、Kii Cloud のユーザーをログイン状態にしておく必要があります。プッシュ通知はログイン中のユーザーとデバイスを紐付けることで実現するためです。

以下はユーザーのログイン処理です。これを MainActivity.onCreate() メソッドに追加します。追加後、クラスライブラリの import の設定を行ってください(RegistrationIntentService は後で実装するため、エラーのままにしておいてください)。

public class MainActivity extends AppCompatActivity {
  private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ......
    if (!checkPlayServices()) {
      Toast.makeText(MainActivity.this, "This application needs Google Play services", Toast.LENGTH_LONG).show();
      return;
    }

    String username = "user1";
    String password = "123ABC";
    KiiUser.logIn(new KiiUserCallBack() {
      @Override
      public void onLoginCompleted(int token, KiiUser user, Exception exception) {
        if (exception != null) {
          Toast.makeText(MainActivity.this, "Error login user:" + exception.getMessage(), Toast.LENGTH_LONG).show();
          return;
        }
        Toast.makeText(MainActivity.this, "Succeeded to login", Toast.LENGTH_LONG).show();
        Intent intent = new Intent(MainActivity.this, RegistrationIntentService.class);
        startService(intent);
      }
    }, username, password);
  }

  private boolean checkPlayServices() {
    GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
    int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
    if (resultCode != ConnectionResult.SUCCESS) {
      if (apiAvailability.isUserResolvableError(resultCode)) {
        apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
                .show();
      } else {
        Log.i("PushTest", "This device is not supported.");
        finish();
      }
      return false;
    }
    return true;
  }
}

ここで実行している処理は以下の 3 つです。

  1. 初めに、Google Play services が利用できることを checkPlayServices() メソッドで確認します。利用できない場合はエラーメッセージを表示して初期化をスキップします。
  2. Kii Cloud のユーザーのログイン処理を行います。アプリ作成時 にテストで作成したユーザーは、ユーザー名が user1、パスワードが 123ABC です。ログインできない場合はエラーを表示して初期化をスキップします。
  3. ログインに成功したときは、サービス RegistrationIntentServicestartService() メソッドによって起動します。

checkPlayServices() メソッドは、GCM のサンプルをそのまま記載しています。finish() メソッドによってアプリを終了させる処理が実装されているため、ご注意ください。

実際のアプリでは、次のように実装するはずです。

  • アプリの起動時と復帰時に Google Play services のチェックを実行

  • ユーザーのログイン成功、仮ユーザー(Pseudo User)の登録成功、トークンでのログインの完了などのタイミング(またはそれ以降)で、RegistrationIntentService を起動

初期化処理本体の実装

次に、RegistrationIntentService を実装します。これは、初期化処理を行う Android サービスです。ログイン処理完了時の startService() から呼び出されます(後述のデバイストークン変更時にも呼び出されます)。

この処理は、以下のように IntentService のサブクラスとして実装します。サービス上の処理のため、Kii Cloud の API はブロッキング API を使用できます。

新規クラスを作成し、下記の実装とします。

public class RegistrationIntentService extends IntentService {
  private static final String TAG = "RegIntentService";
  public static final String INTENT_PUSH_REGISTRATION_COMPLETED = "com.example.pushtest.COMPLETED";
  public static final String PARAM_ERROR_MESSAGE = "ErrorMessage";

  public RegistrationIntentService() {
      super(TAG);
  }

  @Override
  protected void onHandleIntent(Intent intent) {
    String error = null;
    try {
      synchronized (TAG) {
        InstanceID instanceID = InstanceID.getInstance(this);
        String senderId = getString(R.string.gcm_defaultSenderId);
        String token = instanceID.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

        boolean development = true;
        KiiUser.pushInstallation(development).install(token);
      }
    } catch (Exception e) {
      Log.d(TAG, "Failed to complete token refresh", e);
      error = e.getLocalizedMessage();
    }
    Intent registrationComplete = new Intent(INTENT_PUSH_REGISTRATION_COMPLETED);
    registrationComplete.putExtra(PARAM_ERROR_MESSAGE, error);
    LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
  }
}

ここでは、次の処理を行っています。

  • getString(R.string.gcm_defaultSenderId) メソッドによって、先ほどコピーした JSON ファイルから Sender ID の値を読み取ります。デバッガで確認すると、senderId は、設定画面 の Sender ID や、JSON ファイル内の project_number と同じ値になるはずです。もし、R のシンボルが解決できない場合は、前のページにある build.gradle の設定を見直してください。

  • getToken() メソッドで GCM からデバイストークンを取得します。

  • 取得したデバイストークンは、pushInstallation() メソッドの後に install() メソッドを呼び出し、現在ログイン中のユーザーと紐付けて Kii Cloud に登録します(このタイミングでログイン中であることが要求されます)。このコードでは、開発用のプッシュ通知環境で初期化しています。配布用の場合は、development を false にします。

  • 実行が終わったら、com.example.pushtest.COMPLETED という名前でブロードキャストを行います。パラメータとして、エラーメッセージを保持します。成功はエラーメッセージの null で表現されます。この受信ハンドラーは、あとで実装します。

  • この処理は、初期化時と、デバイストークンの更新処理が同時に動く可能性があるため、TAG を使って synchronized で同期化しています。

このコードは Kii Cloud SDK を使う場合の実装です。Thing-IF SDK を使用する場合、このままのコードで 動作を確認 した後で、Thing-IF SDK を使用する Android アプリ向けのプッシュ通知の導入手順の Thing-IF 向けの変更 に進んでください。

なお、ユーザーがログインしていない状態で pushInstallation() メソッドを呼び出すと、例外が発生します。プロセスの再起動に備えるためには、SDK による保存情報からのログイン などの機能を使ってログイン状態を保持することもできます。

初期化完了ハンドラーの実装

MainActivity に BroadcastReceiver を実装して、プッシュ通知の初期化完了時の処理を行います。

RegistrationIntentService でのプッシュ通知の初期化処理が完了すると、com.example.pushtest.COMPLETED でのブロードキャストが行われます。ここでは、ブロードキャストの受信ハンドラーを記述します。

BroadcastReceiver はどこで実装しても問題ありませんが、今回は MainActivity でメッセージを受け取ります。先ほど onCreate() メソッドに追加した処理の前あたりに mRegistrationBroadcastReceiver の new を追加します。さらに、そのフィールドの宣言、onResume() メソッド、onPause() メソッドを追加します。

public class MainActivity extends AppCompatActivity {
  private BroadcastReceiver mRegistrationBroadcastReceiver;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ......
    mRegistrationBroadcastReceiver = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        String errorMessage = intent.getStringExtra(RegistrationIntentService.PARAM_ERROR_MESSAGE);
        Log.e("GCMTest", "Registration completed:" + errorMessage);
        if (errorMessage != null) {
          Toast.makeText(MainActivity.this, "Error push registration:" + errorMessage, Toast.LENGTH_LONG).show();
        } else {
          Toast.makeText(MainActivity.this, "Succeeded push registration", Toast.LENGTH_LONG).show();
        }
      }
    };
    ......
  }

  @Override
  protected void onResume() {
    super.onResume();
    LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
            new IntentFilter(RegistrationIntentService.INTENT_PUSH_REGISTRATION_COMPLETED));
  }

  @Override
  protected void onPause() {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
    super.onPause();
  }
}

ここでは、以下の処理を行っています。

  • クラスのフィールドとして、ブロードキャストレシーバ mRegistrationBroadcastReceiver を保持します。
  • onCreate() メソッドでは、BroadcastReceiver の実装を追加しています。ここでは、プッシュ通知の初期化完了時の処理を記述します。先ほど実装した RegistrationIntentService の最後では、Intent のパラメータとして、ErrorMessage フィールドにエラーの有無とエラーメッセージを登録しました。これを getStringExtra() メソッドで取得して、エラーメッセージまたは完了メッセージを出力しています。
  • onResume() メソッドでは、ブロードキャストレシーバを登録します。ここでは、com.example.pushtest.COMPLETEDmRegistrationBroadcastReceiver を関連付けているため、RegistrationIntentService の最後で実行している完了通知がこのクラスに届くようになります。
  • onPause() メソッドでは、登録したブロードキャストレシーバを破棄します。

デバイストークン変更処理の実装

InstanceIDListenerService を使うと、GCM でデバイストークンが更新されたときの処理を記述できます。

GCM では GCM サーバー側でデバイストークンが更新されることがあります。その場合には、新しいデバイストークンを使ってプッシュ通知を再登録する必要があります。

再登録の検出は、以下のように実装できます。新規クラスを作成し、InstanceIDListenerService のサブクラスとして処理を実装します。

public class MyInstanceIDListenerService extends InstanceIDListenerService {
    @Override
    public void onTokenRefresh() {
        Intent intent = new Intent(this, RegistrationIntentService.class);
        startService(intent);
    }
}

ここでは、onTokenRefresh() メソッドでデバイストークンの変更を検出した際、サービス RegistrationIntentServicestartService() メソッドによって起動する処理を記述しています。

これは、先ほど実装したログイン完了時の処理と同じです。これによって、プッシュ通知の再登録処理が実行されます。

受信ハンドラーの実装

GCM でプッシュメッセージを受信したときは、ブロードキャストレシーバ経由で、最終的に GcmListenerService のサブクラスが呼び出されます。

新規クラスを作成し、下記を追加します。

public class MyGcmListenerService extends GcmListenerService {
  private static final String TAG = "MyGcmListenerService";

  @Override
  public void onMessageReceived(String from, Bundle data) {
    ReceivedMessage message = PushMessageBundleHelper.parse(data);
    KiiUser sender = message.getSender();
    PushMessageBundleHelper.MessageType type = message.pushMessageType();
    switch (type) {
      case PUSH_TO_APP:
        PushToAppMessage appMsg = (PushToAppMessage)message;
        Log.d(TAG, "PUSH_TO_APP Received");
        break;
      case PUSH_TO_USER:
        PushToUserMessage userMsg = (PushToUserMessage)message;
        Log.d(TAG, "PUSH_TO_USER Received");
        break;
      case DIRECT_PUSH:
        DirectPushMessage directMsg = (DirectPushMessage)message;
        Log.d(TAG, "DIRECT_PUSH Received");
        break;
    }
  }
}

ここでは、プッシュメッセージの種類に応じてデバッグログにメッセージを出力しています。このチュートリアルでは、プッシュメッセージを受信できるところまでを、ログによって確認します。

Kii Cloud のプッシュ通知では、PUSH_TO_APP、PUSH_TO_USER、DIRECT_PUSH の 3 通りの機能を用意しています。このチュートリアルの最後では、開発者ポータルのユーザーインターフェイスからプッシュメッセージを送信するテストを行いますが、その場合には DIRECT_PUSH を受け取ることになります。

プッシュ通知を受け取った際にステータスバーにメッセージを表示したい場合は、ステータスバーへの表示 を参照してください。

画面表示のための拡張

プッシュメッセージを受信した際、その結果をモバイルアプリのユーザーインターフェイスに直接反映したい場合は、構成の拡張が必要な場合があります(ステータスバーへの反映は上記のリンクの通りです)。

本チュートリアルでは、MyGcmListenerService で直接プッシュメッセージの処理を記述していますが、サービス内で直接ユーザーインターフェイスを操作することは基本的にできません。

モバイルアプリが動作中のとき、受信結果を画面に反映させたい場合は、上記の初期化完了通知と同様に、ブロードキャストを利用できます。画面反映を行いたいユーザーインターフェイス処理クラスに BroadcastReceiver を実装し、サービスの受信処理からブロードキャストを送信して処理を委譲します。

なお、受信処理はサービスとして動作しているため、Activity が存在しない場合にもプッシュメッセージを受け取る可能性がある点にご注意ください。

アプリの実行

実装完了後、アプリを実行してみます。

起動後、トーストとして、ログイン完了のメッセージ「Succeeded to login」と、プッシュ通知初期化完了時のメッセージ「Succeeded push registration」の 2 つが表示されれば成功です。

エラーが表示される場合やメッセージが表示されない場合、デバッガなどを使ってプログラムと AndroidManifest.xml を確認して問題を解決してください。

次のステップ テストメッセージの送信 で実際にテストメッセージを送信してみましょう。


<< マニフェストの設定 テストメッセージの送信 >>