Java
Android
googleapi
GooglePlayServices
android開発

AndroidアプリでGoogle Play Serviceの使用可否を検証する

More than 1 year has passed since last update.

AndroidアプリでGoogle APIやFirebaseを利用するうえでは、Google Play Service(=Google Play開発者サービス)を介してGoogleの各サービスにアクセスします。このときアプリは端末に入っているGoogle Play Serviceのバージョンをチェックすることが推奨されています。

例えば以前のエントリでFirebase Cloud Messagingの実装方法について書きましたが、Firebase公式のドキュメントにはこんなことが書かれていたりします。


Play開発者サービスのSDKに依存するアプリについては、Google Play開発者サービス機能にアクセスする前に、互換性のあるGoogle Play開発者サービスのAPKが端末にインストールされているかどうかをアプリが必ずチェックする必要があります。


Google Play Serviceを介して各サービスにアクセスするためには必須と思われるこの作業について、色々検証してみました。せっかくなのでここでまとめておこうと思います。


Google Play Serviceについて

出だしにもちょろっと書きましたが、Androidユーザーであれば一度は目にしたことのあるはずの「Google Play開発者サービス」というアレがその正体です。詳細についてはこの記事など、探せば色々出てきますので気になる方は調べてみてください。

アプリがGoogle MapとかGoogle Driveなどのサービスにアクセスするための仲介をしてくれるものだと考えればだいたい間違いないと思います。



ちなみにGoogle的には「Playストアで配布してるので、アプリ側は端末がGoogle Play Serviceをサポートしているかなんて気にしなくて大丈夫だぜ」(←超意訳)ということらしいのですが、ストアでの今現在の評価は3.9点で酷評が多いです。

縁の下の力持ちって悲しいですね…。


環境

このページに書かれているコード等は以下の環境下で動作・検証しています。


  • macOS High Sierra バージョン10.13.1

  • Android Studio 3.0

  • Nexus5 + Android5.0

テスト可能な環境について書かれている内容をテストするには、「PlayストアがインストールされたAndroid4.0以降の端末」か「Google APIsプラットフォームが含まれるAndroid4.2.2以上のバーチャルデバイス(エミュレータ)」のいずれかが必要です。


サンプル

Githubで公開しています。

https://github.com/outerlet/GoogleServiceVerifier


Google Play Serviceのセットアップ

Android Studioのメニューから、Tools > Android > SDK Manager の順にクリック。

SDK Managerが起動したら「SDK Tools」タブを選択すると現れる項目のうち「Support Repository」を展開します。その下に「Google Repository」というのがありますので、これをアップデートしてください。

googleservice01.png


APIの追加

app/build.gradleのdependenciesに以下を追加します。

なお、2行目はテストに使う目的で入れたものです。Google Play Serviceのバージョンを検証するだけなら、1行目だけでOKです。


build.gradle

implementation 'com.google.android.gms:play-services-base:11.4.2'

implementation 'com.google.android.gms:play-services-location:11.4.2'

ちなみに以下のようにAPIをまとめて入れてもよいのですが、メソッド数は64Kを超えてはいけないという制限に引っかかると厄介ですので、必要なAPIを個々に指定したほうが適切だと思います。


build.gradle

implementation 'com.google.android.gms:play-services:11.4.2'



バージョンを検証する

バージョンを検証する方法は2つあります。GoogleApiClientを使う方法と、GoogleApiAvailabilityを使う方法です。


1.GoogleApiClientを使う

GoogleApiClientクラスのインスタンスを生成し、Google Play Serviceとの接続を試みたうえで適切なバージョンかどうかを判定する方法です。GoogleApiClientクラスのインスタンスは、Builderを使って生成します。

GoogleApiClient apiClient = new GoogleApiClient.Builder(this)

.addApi(LocationServices.API)
.addConnectionCallbacks(new ConnectionCallback())
.addOnConnectionFailedListener(new ConnectionFailedCallback())
.build();

apiClient.connect();

ここでは「Google Location and Activity Recognition API」を使うためにGoogle Play Serviceに接続しようとしていますが、同時に十分なバージョンのGoogle Play Serviceが端末に入っているかも検証しています。

なお、addApiメソッドで使いたいAPIを指定する必要があります。指定しなければ以下の例外が発生します。

Caused by: java.lang.IllegalArgumentException: must call addApi() to add at least one API

接続の結果はコールバックオブジェクトに伝達されます。上記のコードでaddConnectionCallbacksにセットしたオブジェクトには接続に成功したか保留扱いになったときのコールバックが、addOnConnectionFailedListenerにセットしたオブジェクトには接続に失敗したときのコールバックが返ります。それぞれの実装は以下のようになります。


ConnectionCallback.java

private class ConnectionCallback implements GoogleApiClient.ConnectionCallbacks {

@Override
public void onConnected(@Nullable Bundle bundle) {
// サービスへの接続に成功した
}

@Override
public void onConnectionSuspended(int cause) {
// サービスとの接続が一時的に失われた
// リモートサービスに問題がある場合など。クラッシュしたとか
}
}



ConnectionFailedCallback.java

private class ConnectionFailedCallback implements GoogleApiClient.OnConnectionFailedListener {

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
// サービスへの接続に失敗した
}
}


2.GoogleApiAvailabilityを使う

GoogleApiAvailabilityクラスのメソッドisGooglePlayServicesAvailableからの戻り値で、バージョンの是非を検証する方法です。

「is〜」で始まるメソッドですが、返ってくるのはboolean値でなく状態を示すint値です。このint値は、ConnectionResultクラスの定数に対応します。

int resCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this);

switch (resCode) {
case ConnectionResult.SUCCESS:
// Google Play Serviceとの接続に成功
break;
case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
case ConnectionResult.SERVICE_MISSING:
case ConnectionResult.SERVICE_DISABLED:
// Google Play Serviceの更新が必要
break;
default:
// Google Play Serviceとの接続に失敗
}


バージョンを検証したあとの対応

適切なバージョンのGoogle Play Serviceが端末になければPlayストアで更新するよう促す必要があります。

検証した結果得られるエラーコードを使えば、ユーザーにPlayストアを起動させるためのダイアログを表示することができます。

GoogleApiAvailability.getInstance().showErrorDialogFragment(this, [エラーコード], [リクエストコード]);

表示されるダイアログはこのようなものです。『更新』を押すと、PlayストアのGoogle Play開発者サービスのページが開きます。

エラーコードとして使えるのは以下のどちらかです。


  • GoogleApiAvailability#isGooglePlayServicesAvailable()で得られるint値

  • GoogleApiClient#connect()の失敗時に呼ばれるonConnectionFailed()の引数として与えられるConnectionResultからgetErrorCode()で得られるint値

リクエストコードはstartActivityForResult()に与えるのと同じ、任意のint値です。

Playストアに遷移してから戻ってくると、onActivityResult()が呼び出されます。このときのリクエストコードで、ダイアログから遷移した結果なのかどうかを判定できます。


バージョンを検証するタイミング

上記の検証をいつ実施するかについて、Firebaseのドキュメントには以下のように書かれています。

ホームキーを押してアプリをバックグラウンドに回し、Playストアを起動して開発者サービスを更新してからアプリに戻ってきた場合、なども考慮しているんだと思います。


メインアクティビティのonCreate()メソッドとonResume()メソッドの2か所で行うことをおすすめします。onCreate()では、チェックにパスしないとアプリを使用できないようにします。onResume()では、ユーザーが[戻る]ボタンなどの他の手段を使って実行中のアプリに戻った場合にもチェックされるようにします。



Google Play開発者サービスの初期化について

ここにある内容をテストするために、Google Play開発者サービスをダウングレードする必要があるかも知れません。以前はアプリ画面からGoogle Play開発者サービスを初期化することができたのですが、Android5.0あたりから『アップデートのアンインストール』ボタンが押せなくなっており、初期化できないようになっていました。

初期化したい場合は、Android5.0なら設定画面からセキュリティ > 端末管理者と遷移して、『端末を探す』のチェックを外すと『アップデートのアンインストール』ボタンが押せるようになります。

Android8.0で確認したところ、設定画面のセキュリティと現在地情報 > 端末管理アプリで『端末を探す』が変更できます。Android6と7は私の手元にないので、お持ちの方は探してみてください。


まとめ

Google Play Serviceのバージョンを検証する方法についてまとめてみました。

余談ですが、Firebase Cloud MessagingはGoogle Play開発者サービスが適切なバージョンでなくてもトークンは取得できるし、メッセージも受信できてしまいます。

使う機能によってはこの判定要らないのかも…とは思うのですが、Google御大が「確認せよ」と言ってるのですから、検証した方がいいのでしょう、たぶん。