近年O2O(Online to Offline)が販売促進のマーケティングなど様々な分野で脚光を浴びていますが、O2Oを絡めた機能をアプリで提供するために不可欠な要素として 位置情報サービス があります。 今回はAndroidアプリで位置情報サービスを効率よく使うための2つのTipsと実装例を紹介していきます。
1,『最新の位置情報サービスの罠』を対策しアンインストールを回避
2,最新機能『Google Play ServicesのLocation API』を使って効率UP
Tips1:『最新の位置情報サービスの罠』を対策しアンインストールを回避
Android4.4(KitKat)から位置情報サービスの設定画面が変更されました。大きな変更は以下の2点になります。
- 高精度・バッテリー節約・GPSのみ の3つのモードから選択できるようになった
- 位置情報サービスを直近で利用したアプリが一覧表示されるようになった
特に後者の「アプリが一覧で表示される」変更に関しての対策は、今後の位置情報サービスを使うアプリ開発においては非常に重要な要素になってくると考えております。 実はこのアプリ一覧の各アプリの項目はタップすることができ、タップするとアプリ情報の詳細ページにIntentされる仕組みになってます。 ご存知の通り、このページに来るとアプリのアンインストールができてしまいます...
電池の減りの早さに悩んでいるときに「高い電池使用量」という文言を見かけ、アンインストールできるページにIntentしたら普通ならどうするでしょう? 位置情報サービスを使う際には 仕様にあわせて最適な選択をし、必要以上に位置情報サービスを使わない ということが今後は鉄則になってきます。 そうすることで アンインストールのリスクを回避 することができます。
iQONでの位置情報サービスを利用した事例
先日iQONでは、Androidで先行して天気コーデ機能をリリースしました。設定したエリアの天気予報と、天気予報にマッチしたコーデが見れる機能になっています。
当たり前な話ですが天気は地域によって全く異なってくるものです。そのためユーザーに自分が住んでいる場所を選択してもらう必要があります。 ただ、膨大な地域情報から自分が住んでいる地域を選択していく操作は手間がかかり、機能の利用率向上の足かせになります。 そこでiQONでは手動での位置情報設定に並行して、位置情報サービスを使った自動位置情報設定の機能を提供しました。位置情報の取得に関しては従来の仕組みである
- android.location.LocationManager
- android.location.LocationListener
を使っています。 先ほども記載しましたが、位置情報サービスは 仕様にあわせて最適な選択をし、必要以上に位置情報サービスを使わない という鉄則が大事になってきます。 これを頭に入れた上で、以下の2つのポイントに気をつけて実装を行いました。
- 天気エリアは大まかな位置情報が取得できれば十分な機能を提供できるため精度は多少落ちても良い
- 一度位置情報が取得できれば機能に利用できるので位置情報を継続して取得し続ける必要はない
public class WeatherSpotAutoSetActivity extends Activity implements LocationListener { LocationManager locationManager; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.weather_spot_auto_set); locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); String gpsStatus = android.provider.Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED); if (gpsStatus.indexOf(LocationManager.NETWORK_PROVIDER) > 0) { // 位置情報の取得処理開始 // 大まかな位置情報が取得できれば要件を満たせるのでネットワークで取得する処理を走らせる(省電力) locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this); } else { // 位置情報サービスがOFFの場合は処理を終了 finish(); } } @Override protected void onDestroy() { locationManager.removeUpdates(this); super.onDestroy(); } @Override public void onProviderDisabled(String provider) { } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public void onLocationChanged(Location location) { String latitude = String.valueOf(location.getLatitude()); String longitude = String.valueOf(location.getLongitude()); // 【重要】緯度経度が1度でも取得できた時点で処理を終了 locationManager.removeUpdates(this); // 取得した緯度経度を使って処理を行う(処理の詳細は省略) } }
iQONではネットワークからの取得に絞っているため、位置情報サービスの設定画面には「低い電池使用量」としか表示されません。
Tips2:最新機能『Google Play ServicesのLocation API』を使って効率UP
前項でiQONは現状
- android.location.LocationManager
- android.location.LocationListener
を利用して位置情報を取得していると紹介させていただきましたが実はこれは古い仕組みで、2014年3月現在では Google Play ServicesのLocation API を利用するのが最も新しい形になります。 昨年のGoogle I/Oで新しく発表された機能です。 OSのバージョンがAndroid2.2以上、「Google Play 開発者サービス」がユーザーの端末にインストールされている必要がありますが、リアルタイムで位置情報を取得しないといけないアプリに関してはLocation APIを使用した方が圧倒的に効率が良くなります。 iQONにおいても今後の展開を考えて、近日中にLocation APIに乗り換える予定です。 Location APIは従来の仕組みと比べていくつか優れた仕組みがありますが、その中でも実際に使ってみて一番良いと思った仕組みは Fused Location Provider というものになります。 従来の仕組みでは位置情報を取得する際には LocationManager.GPS_PROVIDER や LocationManager.NETWORK_PROVIDER と指定する必要がありました。 Criteriaクラスをオプション的に用いて精度や消費電力を指定して組み合わせる方法もありましたが正直使い勝手はあまり良くありませんでした。 Fused Location Provider は、従来の仕組みとCriteriaクラスが統合されたような仕組みになっていて、精度と消費電力のどちらを優先させるかを選択するだけでProviderの指定は行う必要がなくなりました。
利用シーン1:高精度でリアルタイムに位置情報を取得したときの実装例
public class TestActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener { /* * implementsするLocationListenerはcom.google.android.gms.locationのもの * 既存の仕組みから移行する場合にはこれに気をつけないとハマります */ private LocationClient mLocationClient; private LocationRequest mLocationRequest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_activity); // Google Play Services が利用できるか確認 int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); if (ConnectionResult.SUCCESS == resultCode) { mLocationClient = new LocationClient(this, this, this); } else { // 従来の仕組みで代用するなりのプランを考える } } @Override protected void onStart() { super.onStart(); mLocationClient.connect(); } @Override protected void onStop() { mLocationClient.disconnect(); super.onStop(); } @Override public void onConnectionFailed(ConnectionResult connectionResult) { // 接続が失敗したときの処理はここに記載 finish(); } @Override public void onConnected(Bundle bundle) { mLocationRequest = LocationRequest.create(); mLocationRequest.setInterval(5000); // 5秒ごとに位置情報を取得 mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // 高精度で取得 if (mLocationClient.isConnected()) { mLocationClient.requestLocationUpdates(mLocationRequest, this); } } @Override public void onDisconnected() { // 切断したときの処理はここに記載 if (mLocationClient.isConnected()) { mLocationClient.removeLocationUpdates(this); } } @Override public void onLocationChanged(Location location) { String latitude = String.valueOf(location.getLatitude()); String longitude = String.valueOf(location.getLongitude()); // 取得したLat,Lonを使って処理を行う(詳細の処理は省略) } } }
利用シーン2:ある程度の精度で常時位置情報を取得したいときの実装例
public class TestActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener { ... /* 他の箇所は利用シーン1と同様のため省略 */ @Override public void onConnected(Bundle bundle) { mLocationRequest = LocationRequest.create(); mLocationRequest.setInterval(3600000); // 1時間ごとに位置情報を取得 // setIntervalはあくまでも目安のため、状況によって間隔が変わる // そのため長時間取得取得する場合はsetFastestIntervalも設定する mLocationRequest.setFastestInterval(300000); // 取得の頻度は最低でも5分は空ける mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); // 精度と消費電力のバランス取って取得 if (mLocationClient.isConnected()) { mLocationClient.requestLocationUpdates(mLocationRequest, this); } } ... }
利用シーン3:大まかな位置情報を1度だけ取得したい場合の実装例
public class TestActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener { ... /* 他の箇所は利用シーン1と同様のため省略 */ @Override public void onConnected(Bundle bundle) { mLocationRequest = LocationRequest.create(); mLocationRequest.setPriority(LocationRequest.PRIORITY_NO_POWER); // 電力を使わない範囲で取得 // PRIORITY_NO_POWER よりも精度が高い PRIORITY_LOW_POWER でもテストをしてどちらを使うか決める if (mLocationClient.isConnected()) { mLocationClient.requestLocationUpdates(mLocationRequest, this); } } @Override public void onLocationChanged(Location location) { String latitude = String.valueOf(location.getLatitude()); String longitude = String.valueOf(location.getLongitude()); // 取得できた時点で処理を終了させる if (mLocationClient.isConnected()) { mLocationClient.removeLocationUpdates(this); } // 取得したLat,Lonを使って処理を行う(詳細の処理は省略) } ... }
パラメータの値は実装する機能にあわせて細かくテストして最適な形にする。
まとめ
位置情報サービスは非常に便利ですが、今後は利用するアプリは一覧表示されて晒されることを理解した上で利用してください。 そのため 仕様にあわせて最適な選択をし、必要以上に位置情報サービスを使わない ことを徹底しないとアンインストールのリスクに繋がります。 また位置情報取得は従来の仕組みでもいいですが、Google Play ServicesのLocation API はより細かい設定ができ使いやすいのでオススメです。
さいごに
日々成長している 日本最大級ファッションサービスiQON を一緒に成長させてくれるエンジニアを絶賛募集しております。 我こそはという方お待ちしております。 【Android】世界で勝ちたいAndroidアプリエンジニア募集 日本最大級のファッションサービスiQONをぶっちぎりにするエンジニア募集!