例外処理と調査方法

Kii Cloud SDK の API には、呼び出しによって例外が発生するものがあります。ここでは、例外への対処の方法や障害の調査方法の概略を示します。

サンプルコードでの記載

本ガイドのサンプルコードでは、エラーハンドリングの必要箇所のみを示し、詳細(メッセージの出力やエラーからの回復処理など)は省略しています。

たとえば、新規ユーザー作成の解説では、次のようなサンプルコードを扱っています。

ブロッキング API

ブロッキング API で問題が発生すると、例外が throw されます。処理方法は通常の Java プログラムと同様に、try~catch を使用します。本ガイドで提示されているサンプルコードは、特別な場合を除き、次のように抽象度の高い例外のみをハンドルしています。

try {
  user.register(password);
} catch (IOException e) {
  // Handle the error.
  return;
} catch (AppException e) {
  // Handle the error.
  return;
}

ノンブロッキング API

ノンブロッキング API で問題が発生すると、コールバックメソッドの引数として例外オブジェクトが渡されます。問題が発生していない場合、例外オブジェクトは null となります。

user.register(new KiiUserCallBack() {
  @Override
  public void onRegisterCompleted(int token, KiiUser user, Exception exception) {
    if (exception != null) {
      // Handle the error.
      return;
    }
  }
}, password);

例外の型

アプリでは、サンプルコードで特に明示されているものを除いて以下の 2 通りの例外を扱います。

  • java.lang.IOException

    ネットワークアクセスを行う API での IOException の発生は、主にサーバーとの間の通信エラーを表します。多くは、デバイスの通信状況や通信設定に問題がある場合に発生します。

  • AppException

    AppException は、Kii Cloud で扱う例外のうち、クラス階層のルートとなるものです。AppException のスーパークラスに CloudException がありますが、これは拡張用の例外です。通常はサンプルコードのとおり AppException を catch すれば問題ありません。

    API の実行に失敗した場合、基本的に、Kii Android SDK は AppException のサブクラスで定められた例外オブジェクトを送出します。特に、内部で REST API を実行した場合は HTTP ステータスにより、下記のサブクラスの例外を使用します(表に該当しないものは UndefinedException となります)。

    HTTP ステータス 例外の型
    400 BadRequestException
    401 UnauthorizedException
    403 ForbiddenException
    404 NotFoundException
    409 ConflictException

これらの例外は、以下のような階層を持っているため、ブロッキング API では、IOException と AppException の catch で例外をキャッチすることができます。SDK で扱う例外の完全なリストや、Kii Cloud SDK for Android の各 API が送出する例外の詳細については、Javadoc を参照してください。

入力された文字列の長さや文字種などはアプリ側で事前チェックしたものを API に渡す必要があります。通常、パラメータエラーの関連はクライアント SDK でチェックされ、問題がある場合は java.lang.IllegalArgumentException などの RuntimeException 系の例外によって異常が通知されます。特に、ブロッキング API を使う場合、入力パラメータをアプリ側で事前にチェックしておかないと、アプリが異常終了することになります(一部のノンブロッキング API でも、パラメータ不正によって RuntimeException 系の例外が発生するものがあります)。なお、AppException のサブクラスで BadRequestException が定義されていますが、IllegalArgumentException で扱っているパラメータエラーのケースでは送出されません。

アプリケーション側で発生した例外に応じて細かく対処を行う場合、たとえば以下のようにこれらの詳細な例外を一つ一つチェックして然るべき対応を行うことになります。

ブロッキング API

try {
  user.register(password);
} catch (ConflictException e) {
  // Registration failed because the user already exists.
  return;
} catch (AppException e) {
  // Registration failed because of another exception in the app layer.
  return;
} catch (IOException e) {
  // Registration failed because of an exception in the network layer.
  return;
}

ノンブロッキング API

user.register(new KiiUserCallBack() {
  @Override
  public void onRegisterCompleted(int token, KiiUser user, Exception exception) {
    if (exception instanceof ConflictException) {
      // Registration failed because the user already exists.
      return;
    } else if (exception instanceof AppException) {
      // Registration failed because of another exception in the app layer.
      return;
    } else if (exception instanceof IOException) {
      // Registration failed because of an exception in the network layer.
      return;
    } else if (exception != null) {
      // Registration failed because of one of the other exceptions.
      return;
    }
  }
}, password);

各 API において、送出される例外やエラーコードのすべてを事前に定義しておくことは困難です。ユーザーの重複や通信エラーのように対処したい例外のみ特別に扱い、それ以外は一般的なエラーとして扱うことをおすすめします。例外やエラーコードは、SDK やサーバーのバージョンアップによって追加、変更されることがあります。

詳細情報の取得

AppException またはそのサブクラスからは、例外の詳細情報を取得できる場合があります。クライアント SDK は、REST API 経由で Kii Cloud のサーバーに接続します。サーバーからエラーが返された場合、その情報を例外のメソッドを通して取得することができます。詳細情報は、特に開発時において問題の原因を特定する作業に役立ちます。

たとえば、ユーザー登録でユーザー user_123456 がすでに登録されていた場合、以下のコードを実行すると、ユーザーの重複によってエラーとなります。この場合に取得できる詳細情報は次のとおりです。

try {
  KiiUser user = KiiUser.builderWithName("user_123456").build();
  user.register(password);
} catch (ConflictException e) {
  // Handle the error.

  // Print the cause of the error.
  System.out.println("-- getReason");
  System.out.println(e.getReason());
  // Print the error message.
  System.out.println("-- getMessage");
  System.out.println(e.getMessage());
  return;
} catch (AppException e) {
  // Handle the error.
  return;
} catch (IOException e) {
  // Handle the error.
  return;
}
-- getReason
USER_ALREADY_EXISTS
-- getMessage
Error: null
HTTP Response Status: 409
HTTP Response Body: {
"errorCode" : "USER_ALREADY_EXISTS",
"message" : "User with loginName user_123456 already exists",
"value" : "user_123456",
"field" : "loginName"
}

この例はユーザー重複が発生した場合の実行結果です。記載されているすべての例外情報はすべての状況で常に取得できるとは限らないため、プログラムから利用する場合は null 等の応答も想定してください(たとえば、クライアント SDK 内でエラーと判断された場合、REST API 関連は null となります)。

  • getReason メソッド

    クライアント SDK の内部で REST API を呼び出した際、失敗した理由に応じて enum を返すメソッドです。BadRequestException、NotFoundException、ConflictException の各例外クラスで使用することができます。

  • getMessage メソッド

    デバッグ用に例外情報を出力したい場合に使用できます。getBody メソッドの情報を含みますが、スタックトレースは含みません。単純にデバッグ情報を出力したい場合は printStackTrace メソッドも利用できます。

    開発時に原因不明のエラーが発生した場合、HTTP Response BODY の message フィールドからエラーの詳細情報を取得できます。今回の例では、指定されたユーザーがすでに存在していたことが読み取れます。

これ以外にも情報取得用のメソッドがありますが、後述のように例外の型と、getReason メソッドだけで判別できます。

サポートに問い合わせを行う際には、これらの情報も提示することをおすすめします。

エラーの詳細については AppException クラスのリファレンス(スーパークラスとサブクラスを含む)をご参照ください。

エラーの原因によってプログラムを制御したい場合、Android では次の順序で問題解決することをおすすめします。

  1. 例外の型を確認します。たとえば ConflictException によってユーザー重複を検出します。

  2. 上記 1 の方法で判別できない場合(複数の状況で同じ例外が発生する場合)、getReason メソッドによってエラーの発生理由を取得して比較します。

API 共通のエラー

リフレッシュトークンや負荷集中時のエラーなど、API に依存せず、共通して発生するエラーがあります。

アクセストークンのリフレッシュエラー

アクセストークンのリフレッシュ 機能を使用する設定にした場合、アプリ全体の設計として RefreshTokenFailedException 例外への対処方法を検討しておくことをおすすめします。

こちら に示すとおり、RefreshTokenFailedException 例外を受け取った場合は、アプリ側でユーザー名とパスワードを使った再ログイン処理が必要な状態になっています。例外に適切に対処するには、初期画面に戻る遷移や、再ログイン画面を提示する処理などが必要です。これらの処理は、ネットワークアクセスを伴う API すべてで共通するため、事前に対処方法を検討しておくのが適切です。

負荷集中時のエラー

サーバーに対して、一定時間内に通常の負荷を大きく超えるアクセスが発生した場合、そのアプリケーションでは API がエラーを返します。この制限値は、利用契約に基づいてアプリケーションごとに定められます。

この制限値には余裕があるため、通常の運用負荷の変動では問題なく動作する設計ですが、例えば特定の時刻にアクティブユーザーが一斉にリクエストを行うような機能はエラーの発生につながります。

上限に達した場合、各 API は UndefinedException クラス(AppException のサブクラス)でエラーを返します。この際、UndefinedException インスタンスの getStatus() メソッドは 429 を返します。なお、UndefinedException クラスには、getReason() メソッドがないため、詳細情報を取得できません。

通常、モバイルアプリでは、このエラーをサーバーでの想定外エラーとして処理できますが、輻輳を防止するため、API の再試行は避けるように実装してください。

各種ツールによる障害の調査

モバイルアプリの側で例外処理を行うのと同時に、Kii Cloud のいくつかの機能によって問題の原因を調査できます。

開発者ログ

開発者ログには、API を呼び出したときのエラー情報が記録されます。

開発工程での問題発生時や、アプリ公開後のトラブルの調査で詳細な情報が必要になった際は、開発者ログの閲覧 の情報をご覧ください。

たとえば、上記のユーザー重複の場合、開発者ログには以下のような情報が出力されます。

2015-04-21T11:45:11.783+09:00 [ERROR] user.register description:User registration failed userID: login-name:user_123456 email-address:user_123456@example.com phone-number:+819012345678 exception:User with loginName user_123456 already exists

データブラウザ

データブラウザを使って、プログラムから書き込まれた値が正しく更新されていることを確認できます。詳細は、データ、ユーザー、グループの確認と更新 をご覧ください。