広告IDとgoogle play Services

はじめに

よくゲームアプリとか広告で、端末ユニークにする場合

どうやってるんだろうみたいなのが気になったので、ちょっと調べてみた

広告IDについて

  • google play services 4.0.30から対応
    • 最近の広告ライブラリは広告IDを内部で使っているので、その手のやつを使うとエラーになる*1
  • google_play_services_froyo には入ってない

* よくあるサンプルコード

public void getAdId() {
    Thread adIdThread = new Thread(new Runnable() {
        @Override
        public void run() {
            Info adInfo = null;
            try {
                adInfo = AdvertisingIdClient
                        .getAdvertisingIdInfo(getApplicationContext());//★
                final String id = adInfo.getId();//★1
                final boolean isLAT = adInfo.isLimitAdTrackingEnabled();//★2
                Log.d("DEBUG", "AndroidAdID : " + id);
                Log.d("DEBUG", "OptoutFlag : " + String.valueOf(isLAT));
            } catch (Exception e) { //◎
            }
        }
    });
    adIdThread.start();
}
  • ★の引数は、getApplicationContext() じゃないと値が取れない
  • ◎の書き方だとExceptionが拾えないみたいな話が実はあり*2
  • ★2 は インタレストベース広告をオプトアウト のチェックボックスのフラグだが
    • ★1 の値は常に取れてしまう *3
       } catch (IllegalStateException e1) {
            Log.e(TAG,e1.getMessage(),e1);
        } catch (GooglePlayServicesRepairableException e2) {
            Log.e(TAG,e2.getMessage(),e2);
        } catch (IOException e3) {
            Log.e(TAG,e3.getMessage(),e3);
        } catch (GooglePlayServicesNotAvailableException e4) {
            Log.e(TAG,e4.getMessage(),e4);
        } catch (Exception e) { //◎
            Log.e(TAG,e.getMessage(),e);
        }

Exceptionを羅列すると何故か拾える・・

OutOfMemoryError / RealmError 拾う時とおなじようなもんなんだろうか・・

 }catch(OutOfMemoryError oe){
 } catch (RealmError e) {
   //致命的なRealmのエラー
} catch (RealmException e) {
   //通常のRealmのエラー

qiita.com

規約的な話

  1. AndroidIDの代わりに Googleが用意した「広告用のID」*4
    1. 広告以外に使う場合は、ユーザーに許諾を得ましょう*5
    2. 端末IDとか端末が特定できそうな情報と紐付けてはダメです
    3. インタレストベース広告をオプトアウトの状態を見て、ONのときは保存しないようにしましょう
  2. 利用している広告を使っている場合
    1. 広告会社側のページに書いてあるから、特に記載は要らない?

みたいな話がGoogleの広告関連のページにかいてあったりも。

でも 1-2/1-3 あたりってどうやって確認してるんでしょうね・・

某高◎先生みたいな人がチェックして通報された場合、G様としてはアプリの公開を停止する という意味なんでしょうか・・

広告が同梱されている場合は、特にバイナリチェックだとわかんないかと思うんですが・・

google play services for froyo あたりの話(2017/3/19追記)

一応 6.5.+ 相当らしいんですけど

  • AndroidManifest.xml
<meta-data android:name="com.google.android.gms.version"
 android:value="@integer/google_play_services_version" />

の記載対応しちゃうと、marketの方でapkアップロード時に弾かれてしまう・・><

一応 4.+ にすればgradleベースでも可能なよう。

確かに重いですね。。split対応は6.X系からだった記憶がありますが・・

でもまあ、gcm.jarとかの対応も対応サーバーをG様の方が廃止したようで

通信エラー出まくりですしね。過去互換は難しいと思います・・


google play service自体に関して

dependencyコンフリクト問題

google play servicesのLibrary自体が、

  • resが豊富
  • com.android.support ライブラリに強依存
    • support-v4 とか
    • recyclerview-v7 とか

でビルドが通らなかったり、実行時に落ちたりする

まあだから、◎のバージョンを揃えろみたいなASのLintエラーが煩かったりするわけですが・・・

android {
    compileSdkVersion '24'//◎
    buildToolsVersion "25.0.2" //<=これはASに最新強いられる?
}   


compile 'com.android.support:support-v4:24.1.+.' //◎
compile 'com.android.support:recyclerview-v7:24.1.+' //◎

で、そのリソースがリリースされていた時期のgoogle play serviceを使いましょう

みたいな話になるのですが、対応表みたいな情報がないんですよね・・

で対策的な書き方

  • app/build.gradle
compile ('com.google.android.gms:play-services:6.5.87'){
    exclude group: 'com.android.support'
}
compile 'com.android.support:support-v4:24.1.+.' //★
compile 'com.android.support:recyclerview-v7:24.1.+' //★
compile 'com.android.support:design:24.1.+'//★

な書き方が必要。 特に★は後ろの方で宣言する必要があり

コンフリクトをこじらせると・・・

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    //〜中略〜

    compile "net.vvakame:jsonpullparser-core:1.6.2"
    annotationProcessor "net.vvakame:jsonpullparser-apt:1.6.2" //★

}

★とかが書くとエラーになります。

ただこちらは内部のaptのライブラリが問題起こしているのはわかりますが

exclude指定がわからないので対処法がないんですよね・・*6

この状態だとaptのplugin指定でもエラーになるのでaptのライブラリを使っているライブラリを外すしかないという嫌な対処になります

  • data-bindingとか
  • org.androidannotations とか

google play servicesのバージョンにこだわる理由

  1. 現在地情報のLatlng を取るだけであれば、今だと
    1. LocationManager + requestLocationUpdates
    2. GoogleApiClient + FusedLocationProviderApi

のにパターンになるわけですが、細かい情報等を取得しようとすると、やはり細かいAPIには差分が有り、バージョン依存等が発生したりするわけです。

1-1に関して

  • オフラインでも位置情報取得可能
  • ただGPSだけ自体では、元から精度が大雑把なので信頼性的に厳しい面も有る

1-2に関して

  • オンライン前提。NWがつながらないと動かない
  • NW/3G・LTE基地局情報で補正すること前提*7
  • FakeGPSとかをWifi運用のみの端末で使うと、現在地の位置情報が戻せなくなる
    • AGPSデータは3G/LTE運用であることが前提SimフリーでData通信のみだとSim自体に制限があり

とかあったりします

で 1-2の実装前提であえてオフライン対策をしようとすると

  • 正常に位置情報が取得できた時にPrefrence等に保存しておく
  • オフライン時はその値をつかって現在地情報を語る
Location loc = new Location(LocationManager.GPS_PROVIDER);

Double latitude =<<プリファレンスから取得>>;
Double longitude =<<プリファレンスから取得>>;

if(latitude == null || longitude == null ){
     //東京駅の座標
     latitude = new Double(35.681298);
     longitude = new Double(139.766247);
}

loc.setLatitude(latitude);
loc.setLongitude(longitude);

//位置情報を受信する関数に投げる
onLocationChanged(loc);

これをFuseAPIのMockLocationでしようとすると

    Location mockLocation = new Location("network");
    mockLocation.setLatitude(50.120089);
    mockLocation.setLongitude(18.993206);
    mockLocation.setAccuracy(4.0f);
    mockLocation.setTime(System.currentTimeMillis());
    LocationServices.FusedLocationApi.setMockMode(mGoogleApiClient, true);
    LocationServices.FusedLocationApi.setMockLocation(mGoogleApiClient, mockLocation);

    mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
            mGoogleApiClient);

なコードになり、

  • mGoogleApiClientがつながっていないからException発生

という本末転倒なコードになるので意味が無いという。。

というかMockModeのときぐらいAPI利用頻度チェックやめようや。。

とかいいたい*8

*1:取れないときに内部で乱数生成してみたいなことまで遣ってくれてるAdの方が少ないみたい。AdMob自体もしていませんし・・・

*2:デバッカーで動かしたときに確かに体感した。なぜだかすごい疑問。。

*3:★2のチェックがついている時は、アプリやLibrary側で保存処理するな みたいな規約ページがあったりも・・

*4:セキュリティが騒がれた時期に急遽用意されたものらしい

*5:コナミのアプリとかは書いているみたいですね

*6:AS 2.3.Xの最新版だとandroid apt pluginを使っていると、annotationProcesser記述使え! と WarningをLintで吐くようになったが、未だにDaggerとか対応していないので意味がない。。

*7:FusedLocationApiは高精度でしか動きません。端末のみ とか バッテリーセーブモード とか無かったことになってる<苦笑

*8:MockModeってDebugやtestコードぐらいでしか使わないのですし・・