我有一个 LocationHelper 类,我正在设计它来获取用户位置。在创建时,我会指定所需的准确度,以及位置的最新程度。如果它必须是新获得的位置,或者没有足够近的上一个位置,那么我想开始监听来自位置管理器的更新。我的问题是我希望在其中一种方法中发生这种情况,并且在找到足够准确的位置之前,该方法不会返回。在获得所需位置之前,如何阻止该方法返回?我只在后台服务中使用 LocationHelper 类,所以我不在乎它需要很长时间才能返回。我曾想过在返回之前放置一个 while 循环来检查位置是否为空(直到找到足够准确的位置),
如果我的解释不够清楚,代码如下(我的问题是底部的 getCurrentFix),谢谢。
package com.s0812532.AutoBagger;
import java.util.Date;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
/**
* Class LocationHelper
* Class for obtaining the user's location. Capable of obtaining the current
* position, or the most recent fix available fix without getting a new one, in
* order to conserve battery power. Will automatically decide whether to use
* GPS/network based on required accuracy. Must not be called directly from the UI
* thread or the app will become unresponsive.
*/
public class LocationHelper {
//
// Fields
//
/**
* The maximum accuracy, in metres, for the location fix to be considered accurate enough.
*/
private int accuracyRequired;
/**
* The maximum age, in seconds, for the location fix to be considered recent enough.
*/
private int ageRequired;
/**
* The Location obtained by the constructor
*/
private Location position;
/**
* An instance of the system location manager.
*/
private LocationManager lm;
/**
* The context passed in, in order to be able to access system resources like GPS.
*/
private Context mCtx;
/**
* The current time, set by the constructor.
*/
private long time;
/**
* A scaling factor which is applied to the accuracy of any network
* based location fix to compensate for the inaccuracy of its reporting.
*/
private static final float NETWORK_ACCURACY_SCALE = 5;
//
// Constructors
//
/**
* Constructor which will get a location fix (current or recent) which meets the
* criteria provided so that it can be acted on.
* @return
* @param age The maximum age, in seconds, for the location fix to be
* considered recent enough.
* @param accuracy The maximum accuracy, in metres, for the location fix to
* be considered accurate enough.
*/
public LocationHelper( Context ctx, int age, int accuracy )
{
mCtx = ctx;
setAgeRequired(age);
setAccuracyRequired(accuracy);
//Get the time (in milliseconds since UNIX epoch)
//TODO - need to test what happens when phone clock is wrong
Date now = new Date();
time = now.getTime();
//TODO - handle when LocationManager returns as null
lm = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);
//Finally, get a fix to satisfy conditions (if a new fix is required then
//getCurrentFix() will detect this and call getCurrentFix() itself.
position = getRecentFix();
}
//
// Methods
//
//
// Accessor methods
//
/**
* Set the value of accuracyRequired
* The maximum accuracy, in metres, for the location fix to be considered accurate
* enough.
* @param newVar the new value of accuracyRequired
*/
private void setAccuracyRequired ( int acc ) {
//TODO - Test that value is not negative
accuracyRequired = acc;
}
private void setAgeRequired ( int age ) {
//TODO - Test that the value is not negative
ageRequired = age;
}
//
// Other methods
//
/**
* Returns the location fix obtained by the constructor.
* @return Location
*/
public Location getLocation( )
{
return position;
}
/**
* Returns the distance between the user's location and a location provided as an
* argument.
* @return float
* @param location
*/
public float getDistance( Location location )
{
return position.distanceTo(location);
}
/**
* Obtains the most recent fixes from the GPS and Network location managers, and
* stores the more accurate of the two if it is accurate/recent enough, otherwise
* it will call getCurrentFix in order to get an accurate enough fix.
* @return Location
*/
private Location getRecentFix( )
{
//TODO - need to check if location sources are enabled, and deal with it if they aren't
Location GPSFix = lm.getLastKnownLocation("GPS");
Location networkFix = lm.getLastKnownLocation("network");
//Adjust the stated accuracy of the network location fix due to errors in reporting
networkFix.setAccuracy(networkFix.getAccuracy()/NETWORK_ACCURACY_SCALE);
if( (GPSFix.getAccuracy() < accuracyRequired ) & ( time - GPSFix.getTime() < ageRequired ) )
{
//Last GPS fix is good enough, so return it.
return GPSFix;
}
else if ( ( networkFix.getAccuracy() < accuracyRequired ) & ( time - networkFix.getTime() < ageRequired ) )
{
//Last network fix is good enough, so return it.
return networkFix;
}
else {
//none of the available fixes are good enough, so obtain a new one.
return getCurrentFix();
}
}
/**
* Obtains either a GPS or Network based LocationManager, and gets a new fix, if
* possible. GPS/Network is decided upon based accuracy required.
* @return Location
*/
private Location getCurrentFix( )
{
//TODO - need to put in some sort of timeout (perhaps getting status updates will do)
LocationListener networkListener = new LocationListener() {
public void onLocationChanged(Location loc) {
//check to see if new position is accurate enough
if (loc.getAccuracy() < accuracyRequired) {
position = loc;
}
else if (new Date().getTime() - time > 15000 ) { //if it has been looking for network location updates for 15 seconds or more
//Get updates from GPS instead
lm.requestLocationUpdates("gps", 0, 0, GPSListener);
}
}
public void onProviderDisabled(String provider) {
// TODO - network location is/has been disabled by user - do something like warn user it is a bad idea
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
};
LocationListener GPSListener = new LocationListener() {
public void onLocationChanged(Location loc) {
// TODO - check to see if new position is accurate enough
if (loc.getAccuracy() < accuracyRequired) {
position = loc;
}
else if (new Date().getTime() - time > 60000 ) { //if it has been looking for GPS location updates for 60 seconds or more
//TODO Report an error getting location
}
}
public void onProviderDisabled(String provider) {
// TODO - GPS is/has been turned off by user - do something like warn user it is a bad idea
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
};
if(accuracyRequired < 200) {
//First try network updates, then use GPS if sufficient accuracy not obtained in time
lm.requestLocationUpdates("network", 0, 0, networkListener);
}
else {
//GPS updates required for accuracy
lm.requestLocationUpdates("gps", 0, 0, GPSListener);
}
while (position == null){
//Do nothing, this is just to stop the method returning the position until it has been set
try {
wait(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return position;
}
}