Google I/O 2013 で発表された Fused Location Provider を使ってみる
Activity Recognition に続いて使ってみました。
Fused Location Provider とは? #
GPS と WiFi とセンサー(加速度など) を組み合わせて、その状況に応じた最適な方法で位置を取得出来ます。今までよりも低消費電力で、精度のよい位置情報を。
実際どんな感じかは、Google I/O 2013 のセッション動画にある Fused Location Provider のデモ を見てください。
次から使い方です。
1. SDK の Google Play Services を更新する #
Activity Recognition と同じく Google Play services として提供されているので、SDK Manager でライブラリを更新します。
2. Eclipse でプロジェクトを作る #
Android Studio は使ってません(まだよくわからないので)
Eclipse で、いつもどおりに Android のプロジェクトを作ります。
Fragment も使いませんよ、古きよき、BlankActivity なプロジェクトです。
名前はここでは FusedLocationProviderSample とします。
3. プロジェクトに google-play-services_lib を追加する #
Google Play services を使うために、SDK のフォルダにある google-play-services_lib が必要です。
Ecplise の Import で {your sdk location}/extras/google/google_play_services/libproject/google-play-services_lib
を選択します。自分の Workspace に Copy しておいた方が無難でしょう。
コピーしたら、FusedLocationProviderSample で、 google-play-services_lib をライブラリ参照します。
次から FusedLocationProviderSample の実装です。
4. AndroidManifest.xml の編集 #
Fused Location Provider を使うのには今まで通り、ACCESS_FINE_LOCATION
と ACCESS_COARSE_LOCATION
を設定します。
LocationClient は、指定した PERMISSION に応じてよしなに動いてくれるそうです。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.fusedlocationprovidersample"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.fusedlocationprovidersample.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
5. Fused Location Provider を使う #
ActivityRecognition は IntentService を使う必要があったために少々長いコードになりましたが、こちらは今までの LocationProvider
が(も)使えるので、画面一つだけのシンプルなコードです。
FusedLocationProvider を使うには、LocationClient クラスを使います。
大雑把な流れは:
- インスタンスを生成する
connect を呼ぶ -> ConnectionCallbacks.onConnected がコールバックされる - LocationRequest を指定して、requestLocationUpdates を呼ぶ。 -> LocationListener.onLocationChanged が呼ばれる
です。connect を呼んで onConnected を待つのと、位置取得条件が LocationRequest
クラスになった以外は LocationManager
と同じです。
では、全コードです。
package com.example.fusedlocationprovidersample;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import android.location.Location;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;
public class MainActivity extends Activity {
// FusedLocationProvider 用の Client
private LocationClient _locationClient;
private TextView _textResult;
// 以前と変わらない LocationListener
private final LocationListener _locationListener = new LocationListener() {
@Override
public void onLocationChanged(final Location location) {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
String text = _textResult.getText().toString();
text = DateFormat.format("hh:mm:ss.sss", location.getTime()) + " - "
+ location.getLatitude() + "/" +
+ location.getLongitude() + "/" +
+ location.getAccuracy() +
"\n" + text;
_textResult.setText(text);
}
});
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_textResult = (TextView)findViewById(R.id.text_result);
final Button buttonLocate = (Button)findViewById(R.id.button_locate);
buttonLocate.setOnClickListener(new OnClickListener() {
private boolean _isStarted = false;
@Override
public void onClick(View v) {
if (!_isStarted) {
startLocate();
buttonLocate.setText("Stop");
} else {
stopLocate();
buttonLocate.setText("Start");
}
_isStarted = !_isStarted;
}
});
}
@Override
protected void onDestroy() {
stopLocate();
super.onDestroy();
}
private void startLocate() {
_locationClient = new LocationClient(this, new ConnectionCallbacks() {
@Override
public void onConnected(Bundle bundle) {
// 2. 位置の取得開始!
LocationRequest request = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(5000); // 5秒おき
_locationClient.requestLocationUpdates(request, _locationListener);
}
@Override
public void onDisconnected() {
_locationClient = null;
}
}, new OnConnectionFailedListener() {
@Override
public void onConnectionFailed(ConnectionResult result) {
}
});
// 1. 位置取得サービスに接続!
_locationClient.connect();
}
private void stopLocate() {
if (_locationClient == null || !_locationClient.isConnected()) {
return;
}
_locationClient.removeLocationUpdates(_locationListener);
_locationClient.disconnect();
// ConnectionCallbacks.onDisconnected が呼ばれるまで待った方がいい気がする
}
}
6. 動くのか!? #
HTC J(not蝶) で動かしてみました。
室内での結果ですが、最初 27m の精度だったのが、放っておくとどんどん精度が上がって行きました。が、地図に重ねてみないと実際合ってるのかよくわかりませんね。
そのうち、地図に載せて検証してみたいです。
Permission と Priority と精度の話 #
Permission と Priority の組み合わせで、位置の精度がどう変わるか、少し調べました。
FINE_LOCATION+COARSE_LOCATION with PRIORITY_HIGH_ACCURACY #
GPS と WiFi と センサーフル活用。GPS が捕捉できなくても数十mの位置精度が概ね出るようです。
COARSE_LOCATION with PRIORITY_HIGH_ACCURACY #
使えない。FINE_LOCATION が必要ってエラーになりました。
COARSE_LOCATION with PRIORITY_BALANCED_POWER_ACCURACY #
位置の精度が数km程度になりました。WiFi測位(従来の NETWORK_PROVIDER)よりも悪いです。うーんこれは期待はずれだなあ。
結局、例えば屋内測位でしか使わないからGPS要らねって FINE_LOCATION を外すと、かえって精度が落ちるという事になります。(GPS使いませんPERMISSIONが欲しいな。。。)
おまけ #
によると、Google Maps Android API v2 の [setMyLocationEnabled(true)](https://developers.google.com/maps/documentation/android/reference/com/google/android/gms/maps/GoogleMap#setMyLocationEnabled(boolean) でも FusedLocationProvider が使われるようになったとのことです。
まとめ #
公式のコンプリートな Getting Started は
にありますので、こちらを読まれた方が確実です。
ここで作ったサンプルは、
に置いておきます。
published at tags: Android GoogleMapsAPI