GoogleApiClient を使用して Google API にアクセスする(非推奨)

GoogleApiClient(Google API クライアント)オブジェクトを使用すると、Google Play 開発者サービス ライブラリで提供される Google API(Google ログイン、ゲーム、ドライブなど)にアクセスできます。Google API クライアントは、Google Play 開発者サービスに共通のエントリ ポイントを提供し、ユーザーのデバイスと各 Google サービス間のネットワーク接続を管理します。

ただし、新しい GoogleApi インターフェースとその実装は使いやすく、Play 開発者サービスの API にアクセスする方法として推奨されています。Google API へのアクセスをご覧ください。

このガイドでは、以下の方法について説明します。

  • Google Play 開発者サービスへの接続を自動的に管理します。
  • Google Play 開発者サービスのいずれかに対して同期 API 呼び出しと非同期 API 呼び出しを実行する。
  • まれなケースですが、Google Play 開発者サービスへの接続を手動で管理する必要があります。詳細については、手動管理接続をご覧ください。
図 1: Google Play ゲームや Google ドライブなど、利用可能な Google Play 開発者サービスに接続して呼び出すための Google API クライアントのインターフェースを提供する図。

まず、Android SDK 用の Google Play 開発者サービス ライブラリ(リビジョン 15 以降)をインストールする必要があります。まだ設定していない場合は、Google Play 開発者サービス SDK の設定の手順を行ってください。

自動マネージド接続を開始する

プロジェクトが Google Play 開発者サービス ライブラリにリンクされたら、アクティビティの onCreate() メソッドで GoogleApiClient.Builder API を使用して GoogleApiClient のインスタンスを作成します。GoogleApiClient.Builder クラスには、使用する Google API と必要な OAuth 2.0 スコープを指定できるメソッドが用意されています。Google ドライブ サービスに接続する GoogleApiClient インスタンスを作成するコード例を以下に示します。

GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
    .enableAutoManage(this /* FragmentActivity */,
                      this /* OnConnectionFailedListener */)
    .addApi(Drive.API)
    .addScope(Drive.SCOPE_FILE)
    .build();

複数の API と複数のスコープを同じ GoogleApiClient に追加するには、addApi()addScope() の呼び出しを追加します。

重要: Wearable API を他の API と一緒に GoogleApiClient に追加すると、Wear OS アプリがインストールされていないデバイスでクライアント接続エラーが発生する可能性があります。接続エラーを回避するには、addApiIfAvailable() メソッドを呼び出して、Wearable API を渡すことで、クライアントが不足している API を正常に処理できるようにします。詳しくは、Wearable API を使用するをご覧ください。

自動管理接続を開始するには、OnConnectionFailedListener インターフェースの実装を指定して、解決できない接続エラーを受け取る必要があります。自動マネージドの GoogleApiClient インスタンスが Google API に接続しようとすると、解決可能な接続エラーの修正を試みる UI が自動的に表示されます(たとえば、Google Play 開発者サービスの更新が必要な場合)。解決できないエラーが発生すると、onConnectionFailed() に対する呼び出しを受け取ります。

また、自動マネージド接続が確立または停止されるタイミングをアプリが認識する必要がある場合は、ConnectionCallbacks インターフェースのオプションの実装を指定することもできます。たとえば、アプリが Google API にデータを書き込むために呼び出しを行う場合、これらの呼び出しは onConnected() メソッドが呼び出された後にのみ行う必要があります。

以下に、コールバック インターフェースを実装し、Google API クライアントに追加するアクティビティの例を示します。

import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import gms.drive.*;
import android.support.v4.app.FragmentActivity;

public class MyActivity extends FragmentActivity
        implements OnConnectionFailedListener {
    private GoogleApiClient mGoogleApiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create a GoogleApiClient instance
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this /* FragmentActivity */,
                                  this /* OnConnectionFailedListener */)
                .addApi(Drive.API)
                .addScope(Drive.SCOPE_FILE)
                .build();

        // ...
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        // An unresolvable error has occurred and a connection to Google APIs
        // could not be established. Display an error message, or handle
        // the failure silently

        // ...
    }
}

GoogleApiClient インスタンスは、アクティビティが onStart() を呼び出した後に自動的に接続され、onStop() を呼び出した後に切断されます。GoogleApiClient をビルドすると、接続の完了を待たずにアプリの読み取りリクエストをすぐに開始できます。

Google サービスと通信する

接続後、クライアントは、GoogleApiClient インスタンスに追加した API とスコープの指定に従って、アプリが認可されているサービス固有の API を使用して読み取りと書き込みを行うことができます。

注: 特定の Google サービスを呼び出す前に、まず Google Play Console でアプリを登録する必要があります。手順については、Google ドライブGoogle ログインなど、使用している API のスタートガイドをご覧ください。

GoogleApiClient を使用して読み取りまたは書き込みリクエストを実行すると、API クライアントはリクエストを表す PendingResult オブジェクトを返します。この処理は、アプリが呼び出している Google サービスにリクエストが送信される前に直ちに行われます。

たとえば、次のリクエストでは、PendingResult オブジェクトを提供する Google ドライブからファイルを読み取ります。

Query query = new Query.Builder()
        .addFilter(Filters.eq(SearchableField.TITLE, filename));
PendingResult<DriveApi.MetadataBufferResult> result = Drive.DriveApi.query(mGoogleApiClient, query);

アプリで PendingResult オブジェクトを取得したら、リクエストが非同期呼び出しとして処理されるか、同期呼び出しとして処理されるかを指定できます。

ヒント: Google Play 開発者サービスに接続していない間も、アプリは読み取りリクエストをキューに追加できます。たとえば、GoogleApiClient インスタンスがまだ接続されているかどうかにかかわらず、アプリは Google ドライブからファイルを読み取るためのメソッドを呼び出すことができます。接続が確立されると、キューに追加された読み取りリクエストが実行されます。Google API クライアントが接続されていないときにアプリが Google Play 開発者サービスの書き込みメソッドを呼び出すと、書き込みリクエストによってエラーが生成されます。

非同期呼び出しの使用

リクエストを非同期にするには、PendingResultsetResultCallback() を呼び出し、ResultCallback インターフェースの実装を提供します。たとえば、リクエストは非同期で実行されます。

private void loadFile(String filename) {
    // Create a query for a specific filename in Drive.
    Query query = new Query.Builder()
            .addFilter(Filters.eq(SearchableField.TITLE, filename))
            .build();
    // Invoke the query asynchronously with a callback method
    Drive.DriveApi.query(mGoogleApiClient, query)
            .setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() {
        @Override
        public void onResult(DriveApi.MetadataBufferResult result) {
            // Success! Handle the query result.
            // ...
        }
    });
}

アプリは、onResult() コールバックで Result オブジェクトを受け取ると、DriveApi.MetadataBufferResult など、使用している API で指定された適切なサブクラスのインスタンスとして配信されます。

同期呼び出しの使用

厳密に定義された順序でコードを実行する場合は、たとえば、ある呼び出しの結果が別の呼び出しの引数として必要になる場合に、PendingResultawait() を呼び出すことによってリクエストを同期できます。これにより、スレッドがブロックされ、リクエストが完了すると Result オブジェクトが返されます。このオブジェクトは、使用している API(DriveApi.MetadataBufferResult など)によって指定された適切なサブクラスのインスタンスとして配信されます。

await() を呼び出すと、結果が返されるまでスレッドがブロックされるため、アプリで Google API に対して UI スレッドに同期リクエストを送信しないでください。アプリでは、AsyncTask オブジェクトを使用して新しいスレッドを作成し、そのスレッドを使用して同期リクエストを行うことができます。

次の例は、Google ドライブに同期呼び出しとしてファイル リクエストを行う方法を示しています。

private void loadFile(String filename) {
    new GetFileTask().execute(filename);
}

private class GetFileTask extends AsyncTask {
    protected void doInBackground(String filename) {
        Query query = new Query.Builder()
                .addFilter(Filters.eq(SearchableField.TITLE, filename))
                .build();
        // Invoke the query synchronously
        DriveApi.MetadataBufferResult result =
                Drive.DriveApi.query(mGoogleApiClient, query).await();

        // Continue doing other stuff synchronously
        // ...
    }
}

Wearable API にアクセスする

Wearable API は、ハンドヘルド デバイスとウェアラブル デバイスで動作するアプリ用の通信チャネルを提供します。この API は、システムが送信して同期できる一連のデータ オブジェクトと、データレイヤーを使用して重要なイベントをアプリに通知するリスナーで構成されます。ウェアラブル API は、Android 4.3(API レベル 18)以降を搭載しているデバイスで、ウェアラブル デバイスが接続済みで Wear OS コンパニオン アプリがインストールされている場合に使用できます。

Wearable API スタンドアロンを使用する

アプリで Wearable API を使用しているが、他の Google API は使用していない場合、addApi() メソッドを呼び出してこの API を追加できます。次の例は、ウェアラブル APIGoogleApiClientインスタンスに追加する方法を示しています。

GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
    .enableAutoManage(this /* FragmentActivity */,
                      this /* OnConnectionFailedListener */)
    .addApi(Wearable.API)
    .build();

Wearable API が利用できない状況では、Wearable API を含む接続リクエストは API_UNAVAILABLE のエラーコードで失敗します。

次の例は、Wearable API が使用可能かどうかを判断する方法を示しています。

// Connection failed listener method for a client that only
// requests access to the Wearable API
@Override
public void onConnectionFailed(ConnectionResult result) {
    if (result.getErrorCode() == ConnectionResult.API_UNAVAILABLE) {
        // The Wearable API is unavailable
    }
    // ...
}

他の Google API で Wearable API を使用する

アプリで他の Google API に加えて Wearable API を使用する場合は、addApiIfAvailable() メソッドを呼び出して Wearable API を渡し、利用可能かどうかを確認します。このチェックにより、API が使用できないケースを適切に処理できます。

次の例は、Drive API とともに Wearable API にアクセスする方法を示しています。

// Create a GoogleApiClient instance
mGoogleApiClient = new GoogleApiClient.Builder(this)
        .enableAutoManage(this /* FragmentActivity */,
                          this /* OnConnectionFailedListener */)
        .addApi(Drive.API)
        .addApiIfAvailable(Wearable.API)
        .addScope(Drive.SCOPE_FILE)
        .build();

上記の例では、GoogleApiClientGoogle ドライブが接続できない場合、Wearable API に接続できません。GoogleApiClient インスタンスに接続したら、API 呼び出しを行う前に、Wearable API が使用可能であることを確認します。

boolean wearAvailable = mGoogleApiClient.hasConnectedApi(Wearable.API);

API 接続エラーを無視する

addApi() を呼び出し、GoogleApiClient がその API に正常に接続できない場合は、そのクライアントの接続オペレーション全体が失敗し、onConnectionFailed() コールバックがトリガーされます。

addApiIfAvailable() を使用して、API 接続の失敗を無視するように登録できます。回復不能なエラー(Wear の場合は API_UNAVAILABLE など)が原因で addApiIfAvailable() で追加された API が接続できない場合、その API は GoogleApiClient から削除され、クライアントは他の API への接続に進みます。しかし、OAuth 同意解決インテントなどの API 接続が回復可能なエラーで失敗すると、クライアント接続オペレーションは失敗します。自動管理接続を使用している場合、GoogleApiClient は、このようなエラーを可能な限り解決しようとします。手動管理接続を使用すると、解決インテントを含む ConnectionResultonConnectionFailed() コールバックに配信されます。API 接続の失敗が無視されるのは、エラーに解決法がなく、API が addApiIfAvailable() で追加されている場合のみです。手動接続エラー処理を実装する方法については、接続エラーの処理をご覧ください。

addApiIfAvailable() で追加された API は、接続済みの GoogleApiClient インスタンスに存在するとは限らないため、hasConnectedApi() を使用してチェックを追加して、これらの API の呼び出しを保護する必要があります。クライアントとの接続オペレーション全体が成功したときに特定の API が接続できなかった理由を確認するには、getConnectionResult() を呼び出して、ConnectionResult オブジェクトからエラーコードを取得します。クライアントがクライアントに接続されていないときに API を呼び出すと、呼び出しが API_NOT_AVAILABLE ステータス コードで失敗します。

addApiIfAvailable() を使用して追加する API に 1 つ以上のスコープが必要な場合は、addScope() メソッドを使用するのではなく、これらのスコープを addApiIfAvailable() メソッド呼び出しのパラメータとして追加します。OAuth 同意の取得前に API 接続が失敗した場合、この方法で追加されたスコープはリクエストできませんが、addScope() で追加したスコープは常にリクエストされます。

手動管理接続

このガイドの大部分では、enableAutoManage メソッドを使用して、自動的に解決されたエラーとともに自動管理接続を開始する方法について説明します。ほとんどの場合、Android アプリから Google API に接続するには、これが最適で簡単な方法です。ただし、いくつかの状況では、アプリで Google API への手動管理接続を使用するのが望ましい場合があります。

  • アクティビティの外部で Google API にアクセスする場合や、API 接続の制御を維持する場合
  • 接続エラーの処理と解決をカスタマイズする

このセクションでは、これらのユースケースと他の高度なユースケースの例を示します。

手動で管理する接続を開始する

GoogleApiClient への手動管理接続を開始するには、コールバック インターフェースの実装 ConnectionCallbacksOnConnectionFailedListener を指定する必要があります。これらのインターフェースは、Google Play 開発者サービスへの接続の成功、失敗、または一時停止が発生したときに、非同期の connect() メソッドに応答してコールバックを受け取ります。

    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(Drive.API)
            .addScope(Drive.SCOPE_FILE)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build()

接続を手動で管理する場合は、アプリのライフサイクルの適切なタイミングで connect() メソッドと disconnect() メソッドを呼び出す必要があります。アクティビティのコンテキストでは、アクティビティの onStart() メソッドで connect() を呼び出し、アクティビティの onStop() メソッドで disconnect() を呼び出すことをおすすめします。自動的に管理される接続を使用すると、connect() メソッドと disconnect() メソッドが自動的に呼び出されます。

GoogleApiClient を使用して、認証を必要とする API(Google ドライブや Google Play ゲームなど)に接続すると、最初の接続の試行が失敗し、アプリが SIGN_IN_REQUIRED エラーで onConnectionFailed() の呼び出しを受け取る可能性があります。

接続障害を処理する

アプリが onConnectionFailed() コールバックの呼び出しを受け取った場合は、提供された ConnectionResult オブジェクトで hasResolution() を呼び出す必要があります。true が返された場合、アプリは ConnectionResult オブジェクトに対して startResolutionForResult() を呼び出して、ユーザーに直ちに問題を解決するようリクエストできます。この場合、startResolutionForResult() メソッドは startActivityForResult() と同じように動作し、エラーの解決に役立つコンテキストに適したアクティビティ(ユーザーがアカウントを選択できるようにするアクティビティなど)を起動します。

hasResolution() が false を返した場合、アプリは GoogleApiAvailability.getErrorDialog() を呼び出し、このメソッドにエラーコードを渡す必要があります。これにより、エラーに適した Google Play 開発者サービスの Dialog が返されます。ダイアログには、エラーについて説明するメッセージだけを表示することもあれば、エラーを解決するためのアクティビティを起動するアクション(ユーザーが新しいバージョンの Google Play 開発者サービスをインストールする必要があるときなど)を提供することもあります。

たとえば、onConnectionFailed() コールバック メソッドは次のようになります。

public class MyActivity extends Activity
        implements ConnectionCallbacks, OnConnectionFailedListener {

    // Request code to use when launching the resolution activity
    private static final int REQUEST_RESOLVE_ERROR = 1001;
    // Unique tag for the error dialog fragment
    private static final String DIALOG_ERROR = "dialog_error";
    // Bool to track whether the app is already resolving an error
    private boolean mResolvingError = false;

    // ...

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        if (mResolvingError) {
            // Already attempting to resolve an error.
            return;
        } else if (result.hasResolution()) {
            try {
                mResolvingError = true;
                result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
            } catch (SendIntentException e) {
                // There was an error with the resolution intent. Try again.
                mGoogleApiClient.connect();
            }
        } else {
            // Show dialog using GoogleApiAvailability.getErrorDialog()
            showErrorDialog(result.getErrorCode());
            mResolvingError = true;
        }
    }

    // The rest of this code is all about building the error dialog

    /* Creates a dialog for an error message */
    private void showErrorDialog(int errorCode) {
        // Create a fragment for the error dialog
        ErrorDialogFragment dialogFragment = new ErrorDialogFragment();
        // Pass the error that should be displayed
        Bundle args = new Bundle();
        args.putInt(DIALOG_ERROR, errorCode);
        dialogFragment.setArguments(args);
        dialogFragment.show(getSupportFragmentManager(), "errordialog");
    }

    /* Called from ErrorDialogFragment when the dialog is dismissed. */
    public void onDialogDismissed() {
        mResolvingError = false;
    }

    /* A fragment to display an error dialog */
    public static class ErrorDialogFragment extends DialogFragment {
        public ErrorDialogFragment() { }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            // Get the error code and retrieve the appropriate dialog
            int errorCode = this.getArguments().getInt(DIALOG_ERROR);
            return GoogleApiAvailability.getInstance().getErrorDialog(
                    this.getActivity(), errorCode, REQUEST_RESOLVE_ERROR);
        }

        @Override
        public void onDismiss(DialogInterface dialog) {
            ((MyActivity) getActivity()).onDialogDismissed();
        }
    }
}

ユーザーが startResolutionForResult() によって提供されるダイアログを完了するか、GoogleApiAvailability.getErrorDialog() によって提供されるメッセージを閉じると、アクティビティは RESULT_OK の結果コードを含む onActivityResult() コールバックを受け取ります。その後、アプリで connect() を再度呼び出すことができます。次に例を示します。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_RESOLVE_ERROR) {
        mResolvingError = false;
        if (resultCode == RESULT_OK) {
            // Make sure the app is not already connected or attempting to connect
            if (!mGoogleApiClient.isConnecting() &&
                    !mGoogleApiClient.isConnected()) {
                mGoogleApiClient.connect();
            }
        }
    }
}

上記のコードでは、ブール値の mResolvingError にお気づきかもしれません。これにより、ユーザーがエラーを解決している間アプリの状態を追跡し、同じエラーを繰り返し試行することを回避できます。たとえば、ユーザーが SIGN_IN_REQUIRED エラーを解決できるようにアカウント選択ツールのダイアログが表示されている場合でも、画面を回転させてもかまいません。これにより、アクティビティが再作成され、onStart() メソッドが再度呼び出され、そこから connect() が再度呼び出されます。その結果、startResolutionForResult() が再度呼び出され、その前に既存のアカウント選択ツール ダイアログが作成されます。

このブール値は、アクティビティ インスタンス間で持続する場合にのみ、本来の目的を果たします。次のセクションでは、デバイス上で発生した他のユーザー アクションやイベントに関係なく、アプリのエラー処理状態を維持する方法について説明します。

エラー解決中も状態を維持

以前にエラーの解決が進行中に onConnectionFailed() のコードを実行しないようにするには、アプリがすでにエラーの解決を試みているかどうかを追跡するブール値を保持する必要があります。

上記のコードサンプルに示すように、アプリは startResolutionForResult() を呼び出すか、GoogleApiAvailability.getErrorDialog() のダイアログを表示するたびに、ブール値を true に設定する必要があります。 その後、アプリが onActivityResult() コールバックで RESULT_OK を受け取ったら、そのブール値を false に設定します。

アクティビティの再起動をまたいでブール値を追跡するには(ユーザーが画面を回転したときなど)、onSaveInstanceState() を使用して、アクティビティの保存済みインスタンス データにブール値を保存します。

private static final String STATE_RESOLVING_ERROR = "resolving_error";

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(STATE_RESOLVING_ERROR, mResolvingError);
}

次に、onCreate() で保存済み状態を復元します。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // ...
    mResolvingError = savedInstanceState != null
            && savedInstanceState.getBoolean(STATE_RESOLVING_ERROR, false);
}

これで、アプリを安全に実行し、手動で Google Play 開発者サービスに接続できるようになりました。