[解決済み】Androidでユーザーの位置情報を取得する良い方法
質問
問題点
ユーザーの現在地を閾値以内で早急に取得し、同時にバッテリーを節約すること。
なぜ問題になるのか
まず、アンドロイドにはネットワークとGPSという2つのプロバイダーがあります。ネットワークの方が優れている場合もあれば、GPSの方が優れている場合もあります。
より良いというのは、速度対精度の比率のことです。
GPSをオンにしなくても、ほぼ瞬時に位置がわかるなら、数メートルの精度は犠牲にしてもいいと思っています。
次に、位置情報の更新を要求しても、現在地が安定している場合は何も送信されません。
Googleは、ここに"best"ロケーションを決定する例を示しています。
http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
しかし、本来あるべき/あるべき姿にはほど遠いと思います。
開発者は位置情報がどこから来たのか気にする必要はないはずで、ただ欲しいものを指定すれば、携帯が選んでくれるはずです。
助けてほしいこと
私はquot;best"ロケーションを決定する良い方法を見つける必要があります、多分いくつかのヒューリスティックまたは多分サードパーティのライブラリを通して。
これは、最適なプロバイダを決定するという意味ではありません
おそらく、すべてのプロバイダーを使って、その中からベストなものを選ぶと思います。
アプリの背景
このアプリは、ユーザーの位置情報を一定時間毎(仮に10分毎など)に収集し、サーバーに送信します。
アプリはできるだけバッテリーを節約し、位置情報はX(50-100?)メートルの精度が必要です。
将来的には、日中のユーザーの移動経路を地図上にプロットできるようにすることが目標なので、そのために十分な精度が必要なのです。
その他
求められる精度、受け入れられる精度について、妥当な値はどの程度だと思われますか?
今まで100mを合格、30mを希望としていましたが、これは無理があるのでしょうか?
ユーザーの軌跡を後で地図上にプロットできるようにしたいのですが。
希望は100m、受入は500mが良いのでしょうか?
また、今は1回の位置更新で最大60秒GPSをオンにしていますが、これでは屋内にいて200m程度の精度で位置情報を取得するには短すぎませんか?
これは私の現在のコードです。フィードバックがあればありがたいです(エラーチェックがないことは別として、これはTODOです)。
protected void runTask() {
final LocationManager locationManager = (LocationManager) context
.getSystemService(Context.LOCATION_SERVICE);
updateBestLocation(locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER));
updateBestLocation(locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
if (getLocationQuality(bestLocation) != LocationQuality.GOOD) {
Looper.prepare();
setLooper(Looper.myLooper());
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
updateBestLocation(location);
if (getLocationQuality(bestLocation) != LocationQuality.GOOD)
return;
// We're done
Looper l = getLooper();
if (l != null) l.quit();
}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
public void onStatusChanged(String provider, int status,
Bundle extras) {
// TODO Auto-generated method stub
Log.i("LocationCollector", "Fail");
Looper l = getLooper();
if (l != null) l.quit();
}
};
// Register the listener with the Location Manager to receive
// location updates
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 1000, 1, locationListener,
Looper.myLooper());
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1000, 1,
locationListener, Looper.myLooper());
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
Looper l = getLooper();
if (l != null) l.quit();
// Log.i("LocationCollector",
// "Stopping collector due to timeout");
}
}, MAX_POLLING_TIME);
Looper.loop();
t.cancel();
locationManager.removeUpdates(locationListener);
setLooper(null);
}
if (getLocationQuality(bestLocation) != LocationQuality.BAD)
sendUpdate(locationToString(bestLocation));
else Log.w("LocationCollector", "Failed to get a location");
}
private enum LocationQuality {
BAD, ACCEPTED, GOOD;
public String toString() {
if (this == GOOD) return "Good";
else if (this == ACCEPTED) return "Accepted";
else return "Bad";
}
}
private LocationQuality getLocationQuality(Location location) {
if (location == null) return LocationQuality.BAD;
if (!location.hasAccuracy()) return LocationQuality.BAD;
long currentTime = System.currentTimeMillis();
if (currentTime - location.getTime() < MAX_AGE
&& location.getAccuracy() <= GOOD_ACCURACY)
return LocationQuality.GOOD;
if (location.getAccuracy() <= ACCEPTED_ACCURACY)
return LocationQuality.ACCEPTED;
return LocationQuality.BAD;
}
private synchronized void updateBestLocation(Location location) {
bestLocation = getBestLocation(location, bestLocation);
}
// Pretty much an unmodified version of googles example
protected Location getBestLocation(Location location,
Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return location;
}
if (location == null) return currentBestLocation;
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use
// the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return location;
// If the new location is more than two minutes older, it must be
// worse
} else if (isSignificantlyOlder) {
return currentBestLocation;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and
// accuracy
if (isMoreAccurate) {
return location;
} else if (isNewer && !isLessAccurate) {
return location;
} else if (isNewer && !isSignificantlyLessAccurate
&& isFromSameProvider) {
return location;
}
return bestLocation;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
解決方法は?
同じアプリケーションをコーディングしているようですね ;-)
これが私の現在の実装です。GPSアップロードアプリはまだベータテスト段階なので、いろいろと改良の余地があるかもしれませんが、今のところうまく機能しているようです。
/**
* try to get the 'best' location selected from all providers
*/
private Location getBestLocation() {
Location gpslocation = getLocationByProvider(LocationManager.GPS_PROVIDER);
Location networkLocation =
getLocationByProvider(LocationManager.NETWORK_PROVIDER);
// if we have only one location available, the choice is easy
if (gpslocation == null) {
Log.d(TAG, "No GPS Location available.");
return networkLocation;
}
if (networkLocation == null) {
Log.d(TAG, "No Network Location available");
return gpslocation;
}
// a locationupdate is considered 'old' if its older than the configured
// update interval. this means, we didn't get a
// update from this provider since the last check
long old = System.currentTimeMillis() - getGPSCheckMilliSecsFromPrefs();
boolean gpsIsOld = (gpslocation.getTime() < old);
boolean networkIsOld = (networkLocation.getTime() < old);
// gps is current and available, gps is better than network
if (!gpsIsOld) {
Log.d(TAG, "Returning current GPS Location");
return gpslocation;
}
// gps is old, we can't trust it. use network location
if (!networkIsOld) {
Log.d(TAG, "GPS is old, Network is current, returning network");
return networkLocation;
}
// both are old return the newer of those two
if (gpslocation.getTime() > networkLocation.getTime()) {
Log.d(TAG, "Both are old, returning gps(newer)");
return gpslocation;
} else {
Log.d(TAG, "Both are old, returning network(newer)");
return networkLocation;
}
}
/**
* get the last known location from a specific provider (network/gps)
*/
private Location getLocationByProvider(String provider) {
Location location = null;
if (!isProviderSupported(provider)) {
return null;
}
LocationManager locationManager = (LocationManager) getApplicationContext()
.getSystemService(Context.LOCATION_SERVICE);
try {
if (locationManager.isProviderEnabled(provider)) {
location = locationManager.getLastKnownLocation(provider);
}
} catch (IllegalArgumentException e) {
Log.d(TAG, "Cannot acces Provider " + provider);
}
return location;
}
編集する は、ロケーションプロバイダに定期的な更新を要求する部分です。
public void startRecording() {
gpsTimer.cancel();
gpsTimer = new Timer();
long checkInterval = getGPSCheckMilliSecsFromPrefs();
long minDistance = getMinDistanceFromPrefs();
// receive updates
LocationManager locationManager = (LocationManager) getApplicationContext()
.getSystemService(Context.LOCATION_SERVICE);
for (String s : locationManager.getAllProviders()) {
locationManager.requestLocationUpdates(s, checkInterval,
minDistance, new LocationListener() {
@Override
public void onStatusChanged(String provider,
int status, Bundle extras) {}
@Override
public void onProviderEnabled(String provider) {}
@Override
public void onProviderDisabled(String provider) {}
@Override
public void onLocationChanged(Location location) {
// if this is a gps location, we can use it
if (location.getProvider().equals(
LocationManager.GPS_PROVIDER)) {
doLocationUpdate(location, true);
}
}
});
// //Toast.makeText(this, "GPS Service STARTED",
// Toast.LENGTH_LONG).show();
gps_recorder_running = true;
}
// start the gps receiver thread
gpsTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Location location = getBestLocation();
doLocationUpdate(location, false);
}
}, 0, checkInterval);
}
public void doLocationUpdate(Location l, boolean force) {
long minDistance = getMinDistanceFromPrefs();
Log.d(TAG, "update received:" + l);
if (l == null) {
Log.d(TAG, "Empty location");
if (force)
Toast.makeText(this, "Current location not available",
Toast.LENGTH_SHORT).show();
return;
}
if (lastLocation != null) {
float distance = l.distanceTo(lastLocation);
Log.d(TAG, "Distance to last: " + distance);
if (l.distanceTo(lastLocation) < minDistance && !force) {
Log.d(TAG, "Position didn't change");
return;
}
if (l.getAccuracy() >= lastLocation.getAccuracy()
&& l.distanceTo(lastLocation) < l.getAccuracy() && !force) {
Log.d(TAG,
"Accuracy got worse and we are still "
+ "within the accuracy range.. Not updating");
return;
}
if (l.getTime() <= lastprovidertimestamp && !force) {
Log.d(TAG, "Timestamp not never than last");
return;
}
}
// upload/store your location here
}
考慮すべきこと
-
は、あまりにも頻繁にGPSの更新を要求しない、それはバッテリーの電力を消耗します。私は現在 私のアプリケーションでは、デフォルトで30分を使用しています。
-
最後の既知の位置までの最小距離」チェックを追加します。 GPSが利用できず、位置が三角測量されている場合、quot;jump around" となります。 または、新しい位置が精度の範囲外であるかどうかをチェックすることもできます。 の値は、最後に確認した位置からのものです。
関連
-
[解決済み】react-native: コマンドが見つかりません。
-
[解決済み】新しいAVDを作成すると、CPU/ABIフィールドに「システムイメージがインストールされていません」と表示される。
-
[解決済み】Build Tools リビジョン 23.0.1 の検索に失敗しました。
-
[解決済み】IllegalStateException: ViewPager で onSaveInstanceState の後にこのアクションを実行できません。
-
[解決済み] Androidのgravityとlayout_gravityの違いは何ですか?
-
[解決済み] setBackgroundDrawable() 非推奨
-
[解決済み] Androidのソフトキーボードをプログラムで閉じる/隠すにはどうすればよいですか?
-
[解決済み] AndroidでPythonを実行する方法はありますか?
-
[解決済み] Androidで'Context'を取得する静的な方法?
-
[解決済み] Android EmulatorでGPS位置情報をエミュレートする方法を教えてください。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】まだ警告が出る:設定 'compile' は時代遅れで 'implementation' に置き換わった。
-
[解決済み] シンボル 'context' を解決できない
-
[解決済み】Android: Intent エラーを処理するアクティビティは見つかりませんでしたか?どのように解決するのか
-
[解決済み】インストールエラー。インストールエラー:install_failed_older_sdk
-
[解決済み] [Solved] Unsupported method: ベースコンフィグ.getApplicationIdSuffix()
-
[解決済み】フラグメントMyFragmentがアクティビティにアタッチされない。
-
[解決済み】アクティビティにない場所でのgetLayoutInflater()の呼び出し
-
[解決済み】getCheckedRadioButtonId()が無駄なintを返す?
-
[解決済み] Android Studioで「URIが登録されていません」と報告されるのはなぜですか?[クローズド]
-
[解決済み] APKのインストール中にDELETE_FAILED_INTERNAL_ERRORエラーが発生する。