GoogleApiClientのライフサイクル管理を自動化してみる


 Google Play Services のAPIを使う上で必須となる `GoogleApiClient` の接続/切断を自動で管理できる設定があったことに気づいたので、実際に使ってみました。

Activityクラスなどから `GoogleApiClient` を使用する時、`#onResume()` で接続し、 `#onPause()` で切断するようなコードを書くかと思います。
自分は今まこのようにしていましたが、改めて `GoogleApiClient` のドキュメントを見ていたらこのように書いてありました。

> You should instantiate a client object in your Activity's onCreate(Bundle) method and then call connect() in onStart() and disconnect() in onStop(), regardless of the state.

* [GoogleApiClient APIリファレンス](https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient)

ので、Google的には `#onStart()` ~ `#onStop()` 間で接続と切断を管理するような使い方を想定していたようでした。
自動的にライフサイクル管理をするように設定すると、上記のタイミングで接続、切断されるようになります。

確認のために作成したサンプルアプリはGitHubにあります。

* [GitHub](https://github.com/droibit/AutoManagedGoogleApiClient)

## 設定方法

このライフサイクル管理を自動化を有効にする方法は簡単です。`GoogleApiClient` を作成する際に一行追加するだけです。

```java
public class MainActivity extends AppCompatActivity
        implements ConnectionCallbacks, OnConnectionFailedListener {

    private GoogleApiClient mGoogleApiClient;

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

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                           .addApi(Wearable.API)
                           .addConnectionCallbacks(this)
                           .enableAutoManage(this, this)  // ← この一行を追加するだけです
                           .build();
    }

	// 実装は省略
}
```

`GoogleApiClient.Builder` で新しくコールしたメソッドは次のように宣言されています。

```java
public GoogleApiClient.Builder enableAutoManage (
	FragmentActivity fragmentActivity, 
	OnConnectionFailedListener unresolvedConnectionFailedListener)
```

* [API リファレンス](https://developers.google.com/android/reference/com/google/android/gms/common/api/GoogleApiClient.Builder.html#enableAutoManage(android.support.v4.app.FragmentActivity, com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener))

第一引数は、 `FragmentActivity` なので、使用時はサポートライブラリが必須になります。
第二引数は接続失敗時のイベントリスナーです（使ってみた感じ、 `addOnConnectionFailedListener` は不要でこちらだけでよさそう）

あとは自動的にGoogle Play Servicesに接続されて、 `#onConnected(Bundle)` が呼ばれるような流れになります。

## 所感

以下使ってみての所感になります。

### いいところ

ライフサイクル管理を自動化してくれるということで、イベントリスナーの実装のみになり、処理がシンプルになります。これは嬉しいですね。

```java

 @Override
 public void onConnected(Bundle connectionHint) {
    // 接続された時の処理
    // Wearable.MessageApi.addListenerなどよぶ
 }

 @Override
 public void onConnectionSuspended(int cause) {
     // 切断された時の処理
     // Wearable.MessageApi.removeListenerなどよぶ
 }

 /**
  * {@inheritDoc}
  */
 @Override
 public void onConnectionFailed(ConnectionResult connectionResult) {
    // エラー処理
    // Google Play Servicesがインストールされていない場合などの案内を表示
}
```

他にもGoogle Play Servicesがインストールされていない場合のエラー処理の簡略化できます。まじめに実装しようとすると結構面倒くさくて、

```java
// Google Play Servicesへの接続結果をもらって何かの処理を行う
public void onConnectionResult(ConnectionResult result) {
	// 接続に成功
    if (result.isSuccess()) {
	    return
     }

     // 接続失敗の解決策がない場合
     if (!result.hasResolution()) {
		 // Google Play Serviceをインストールするための画面を表示する
		 GoogleApiAvailability.getInstance()
                             .showErrorDialogFragment(context, result.getErrorCode(), REQUEST_PLAY_SERVICE_ERROR)
         return
     }

     try {
         result.startResolutionForResult(context, REQUEST_RESOLUTION_PLAY_SERVICE)
     } catch(IntentSender.SendIntentException e) {
	     // 接続に失敗した上、解決も失敗
         Log.e(BuildConfig.BUILD_TYPE, "Play service error: ", e)
     }
}
```

さらに、インストールした後の結果をアクティビティの `onActivityResult()` で受け取って、というような処理を自前で書かないといけないのですが、その必要はなくなります。上記のインストールを促すようなダイアログを自動表示してくれる様になります。

その後インストールされなかった場合も含め失敗系の処理を`#onConnectionFailed()` にを集約することができるのでシンプルに書くことができます。
（接続失敗で1回、インストール出来なかったで1回、計2回おなじエラーコードの結果が帰ってくることがありますが。。）

### 気になったところ

今までに `#onResume()`, `#onPause()` で接続/切断するようなコードを書いてきていると、一行追加して自動管理だ、と変更すると影響が出てくるところがあるかもしれない点です。

極端な例ですが、 MessageApiでAndroid Wearとメッセージをやり取りする処理を書いて、 `onPause()` で切断するので問題ないとそこにUI系の処理を直接書いていたら問題になります。

あと、MessageApiのようにリスナーを追加削除のバランスを考えなければいけません。
切断は `#onStop()` で呼ばれますが、接続が `#onStart()` で呼ばれるため、追加と削除が1対1になりません。
（バックグラウンドから復帰する度にイベントリスナーが追加さていきます）

そのため例えば次のようなコードを追加してバランスが取れるようにします。

```java
private boolean connected = false;

/** {@inheritDoc} */
@Override
public void onConnected(Bundle connectionHint) {
    // 接続されていない状態から接続された場合のみイベントリスナーを追加する。
    if (!connected) {
        Wearable.MessageApi.addListener(mGoogleApiClient, this);
    }
    connected = true;
}

/** {@inheritDoc} */
@Override
public void onConnectionSuspended(int cause) {
    connected = false;
    Wearable.MessageApi.removeListener(mGoogleApiClient, this);
}
```

新規開発する際はこのように少し意識を切りかえれば、自動管理の設定を使っていけるなという感じです。
既存のアプリはライフサイクル管理のタイミングが異なるので、変えた場合に問題なければ切り替えていこうかなと思っています。

以上、 `GoogleApiClient` のライフサイクル管理を自動化してみてでした。

> Written with [StackEdit](https://stackedit.io/).