Androidでの位置情報の取得の今と昔

はじめに

のお話を読んでて

位置情報的な話のコラム的な話を書きたくなったので書いてみた

最新の Android Studio 2.3 Canary 2 に関する言及

  • InstantRun + DataBinding の併用が動くようになった
  • Built-in shrinker と InstantRun の併用可と書いてあるけど
    • 確かにビルドは通る
    • ビルドはまともに長時間かかる*1
    • アプリは必ず再起動
  • MultiDex + InstantRun

    • とりあえずまともに動く感じ?*2
    • アプリは必ず再起動は変わらず
      • Option2の差分適応のボタンは、うまく動かせないっぽい?*3
  • ユーザー定義style系が読み込まれなくなったので、レイアウトpreviewが死にました。

    • なんか一個バグ潰したら新しいバクが発生するの止めてほしい
  • rm -rf ~/Library/Caches/AndroidStudioPreview あたりを消して、立ち上げ直したら治りました

    • Invalidate Cachesが効かなかったorz
    • AS2.2.2 => AS 2.3 Canary 2 のルートでOK*4

古い取得方法 と OS 2.3で動いていたバージョン(v1)と現在のAPI(v2)が何が違うか?

古い取得方法

  • LOCATION_SERVICE から位置情報を取得
  • LocationListener 経由で位置情報を取得
    • ただし利点的にはオフラインでもGPS情報が取得できる(GPS_PROVIDERの指定で)
  • 現在でも、compileSdkVersionをGoogle APIs:24” 等に指定すれば利用可能
    • api 26でも問題なく動きます!LocationManagerはちゃんとLimit Location対象になってる!!
android {
   //compileSdkVersion "Google APIs:24" //<= ココ関係ないです><
}
LocationManager mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

//〜中略〜

LocationProvider provider =
        locationManager.getProvider(LocationManager.GPS_PROVIDER);
        
// LocationListenerを登録
mLocationManager.requestLocationUpdates(provider, 0, 0, this);

@Override
public void onLocationChanged(Location location) {

}   

v1.5 ベース

  • LocationClient で取得
    • google_play_services_froyo 相当(5.xベース)
  • OS2.3でもちゃんと動く
  • NW接続が必須
    • APIの利用回数(マネタイズ)を厳格化するため*5
  • 正確にはGooglePlay開発者サービスが、GooglePlayService 8.4 ぐらいからOS4.1以下では現状更新されていないため最新のものを使っても情報が取得できない
  • 2系、3系端末が殆ど使われていないみたいな話のレトリックはココらへんから
    • Google Analiticsも古いから勿論端末利用情報が最新サーバに送られていない
LocationClient  mClient = new LocationClient(this, this, this);

//〜中略〜

Location loc = mClient.getLastLocation();

現在のAPI(v2)

  • GoogleApiClient で取得
  • 新し目のGooglePlay開発者サービスが入っていないと勿論正常に動かない
  • NW接続が必須

    • APIの利用回数(マネタイズ)を厳格化するため*6
  • LocationServices.FusedLocationApi.requestLocationUpdates の利用

    • この関数なんですが、位置情報モード[高精度] でないとまともに動きません。
    • そのため、 status.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS); 辺りのコードは必須

GooglePlayServicesを使うとした場合のオフライン対策的な話

最後の位置情報をPreference等で保持しておくしか無い

あと

LocationServices.FusedLocationApi.getLastLocation

もかなり怪しいのであてにならない*7

G様Mapアプリ では最後にMapを開いた位置動いているようなので、別途権限が必要なのかも

電池消耗の話に関して

設定>位置情報 で表示される

  • 最近の位置情報リクエス
    • 低い電池使用量

と表示させるには

LocationRequestの設定は、あんま意味なくて

一定時間における位置情報リクエスト数*8で判定しているだけのようなので

  • 高精度、PRIORITY_HIGH_ACCURACY で取得
  • Interval は最低が1000ms(1秒) らしい
  • 取得したらすく切断する*9

がベストプラクティスになるのかなと。

あと PRIORITY_HIGH_ACCURACY は、

  • 現在GPS多用というよりアンテナ局の位置等(3G/LTE)で位置情報を取得している*10
    • GPS OnlyでもA-GPS*11がしっかりしていれば動くという話があるが・・
    • SMSなしMVNO だと精度が落ちるあたりはA-GPSの情報がうまく利用できないからあたりの話があったかと
  • WiFi Onlyだと精度が落ちる
  • 一番確実に取れる形が 3G/LTE + Wifi +GPS というお話も*12
    • Wifiで通信しているときは、3G/LTE は勿論通信はしていないが、アンテナ局とはやり取りしているらしい

最近の v9.8/v10.0 ベース

  • 位置情報をOFFにした状態でも、LocationServices.SettingsApi.checkLocationSettings の関数が
    • LocationSettingsStatusCodes.SUCCESS として通過してしまうことが有り*13
    • エミュレータは古い開発者サービスしか搭載されていないので、テストは現状不可
      • 設定から入れられる位置情報の入力も整数2桁までなので、日本のテストはできない*14

  • v10.0ベース

すべてのGooglePlayServicesのライブラリがv10.0 に上がっているわけではないので上げられない場合があり*15

android update sdkのタイミングで、短時間ですが minsdk-14でリリースされていて、すぐminsdk-9に戻された経緯があり、

G様内部では minsdk-14 前提で開発されている可能性が高*16


  • x86用GooglePlayServices

zenfone2とかこちら側のはずなんですが、自分も使っていて

  • GooglePlayStoreアプリでアプリのアップデートでクラッシュOS再起動*17
  • G様エミュレータですら最新のGooglePlay開発者サービスが載っていないので動かせない
    • まあversionさげてもエラーが出ないぐらいの確認しかできないのですが。。(汗

juggly.cn

辺の動きもあるようですけど、実際どうなるんだろう。。

x86 Androidのサポートは基本低らしいですからね。。


最近の compileSdkVersion 25

  • IntentServices::onHandleIntent で下記の関数が「UIスレッドで使え」とExceptionを吐くようになった
    • getSystemService(LOCATION_SERVICE);

この手の変更点は、android.jarに変更が入っているんでしょうけど

どこにも記載されていないので正直すごく困りますね。。

むだに new Handlerで囲まないと駄目な感じになっています*18


  • PendngIntent経由のActivityに対して SINGLE_TOP等を指定していても単一インスタンス扱いで一番上に表示されなくなった*19

これ compileSdkVersion 24 からかもしれないんですが

NotificationCompat経由からのActivity起動とかで困ってる。

  1. ActivityLifecycleCallbacks辺りを実装、ActivityStackを監視
    1. ActivityGroupが非推奨なので。。。
  2. 既存のActivityStackを終了させてから
  3. activity起動

とかしかないのかな〜(汗

support-v4 が低いosを殺しに来てる

target-25にすると、support-libraryも25使えといわれるのですが、

バラバラにされたaar単位で、下記を定義しましょうと言われます

<uses-sdk tools:overrideLibrary="XXX"> 
  • ただこの手のタグのvalue値が255文字まで
  • 重複タグ定義不可
    • support-v4 だけで溢れます

なのでまず死にます。逃れられませんorz


Geofencingの低電力という話に関して

Geofencing自体は、低消費電力という話になっていますが*20

これはロケーション履歴のデータを利用しているからとのことらしいんですけど*21

ロケーション履歴を参照するAPIが公開されていないのでどうにもならないかなーと。

G様戦略だと

  • 位置情報ON時
    • GooglePlay開発者サービス=>位置情報を取得しつつ、ロケーション履歴 をG様鯖に送信、作成
    • GooglePlay開発者サービス は高い電池使用量になる*22
    • ただ他のG様アプリは、ロケーション履歴を利用するので 低い電池使用量

なんだろうな。。というのが所感。

まあ GoogleNow API も未だメーカー契約でしか公開されていないからなー

そこら辺の契約すれば、使えているメーカーさんもあるかもしれないですけど。。。

Google Nowといえば

というソフトがありましたね・・

GoogleNowランチャー使うときは便利なんですが

大概はメーカー製のLancherアプリが入っているので、

Google純正端末/genymotion等のエミュぐらいにしか導入しないという奴だったりも。。


過渡期的な位置情報系APIのdeprecated対応

Settings.Secure.LOCATION_PROVIDERS_ALLOWED

端末のGPSが使えるかどうかの判定

private boolean canGpsProvider() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
            //Equal or higher than API 19/KitKat
            try {
                int locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
                if (locationMode == Settings.Secure.LOCATION_MODE_HIGH_ACCURACY){
                    return true;
                }
            } catch (Settings.SettingNotFoundException e) {
                e.printStackTrace();
            }
        }
        else{
            // 位置測位プロバイダー一覧を取得
            String providers = Secure.getString(
                    context.getContentResolver(), Secure.LOCATION_PROVIDERS_ALLOWED);
            // "gps"が含まれているか
            boolean isGps = !(providers.indexOf("gps", 0) < 0);
            return isGps;
        }

        return false;
    }

*1:ビルド60〜180秒

*2:ビルド30〜90秒

*3:必ずアプリが再起動かかる

*4:Canary1にした段階で変なデータ作成されたのかもorz

*5:オフラインでは動かない

*6:オフラインでは動かない

*7:普通に使うと必ず北米を指す。

*8:GoogleAPIコール数

*9:継続して位置情報を取得する必要が無い場合

*10:補正が正しい?

*11:補助GPS情報

*12:これがズバリ 高精度モード

*13:OSのversion依存? Firebase互換辺りにした関係のデグレ

*14:これ北米でしかテストしてないから??

*15:Appindexingとかつかっていると駄目。。。9.8.0までしかリリースされていない

*16:というかそういうバージョンがリリースされてしまうのはリリース優先のスケジュールなんでしょうか。。

*17:PokemonGoとかndkでチェック厳しくしたのにx86サポートしていないやつとか

*18:OkHttp/Retrofit化しても new Handler()で囲むことにより階層が深くなる的な

*19:Service等を挟んで、FLAG_ACTIVITY_NEW_TASK 指定で上に重ねることは出来るけど。。

*20:G社様製アプリ と同様に

*21:GoogleFit APIもそうらしい

*22:たまに低い電池使用率 とか騙りますけど<苦笑