如何使方法在返回之前等待足够准确的位置?

时间:2012-07-27 10:52:19

标签: android android-service android-location

我有一个类LocationHelper,我正在设计它以获取用户位置。在创建时,我指定所需的准确度,以及该位置的当前时间。如果它必须是新获得的位置,或者之前没有足够的位置,那么我想开始从位置管理器监听更新。我的问题是我希望在其中一个方法中发生这种情况,并且方法在找到足够准确的位置之前不要返回。在获得我想要的位置之前,如何停止返回方法?我只在后台服务中使用LocationHelper类,所以我不关心它需要很长时间才能返回。 我想过在返回之前放一个while循环来检查position是否为null(直到找到一个足够准确的位置),如果是,那么它会执行一个等待,但我是否认为这会阻止侦听器接收更新?

如果我的解释不够清楚(我的问题在于底部的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;
    }


}

1 个答案:

答案 0 :(得分:0)

位置侦听器回调是在主线程上进行的,因此您无法阻止主线程(否则您的侦听器回调将永远不会开心)。如果您的服务在与您的活动相同的过程中运行,那么这也是UI线程。为了完成这项工作,您需要确保LocationHelper在单独的线程中运行。然后,您可以添加一个while循环,在检查新位置之间稍微休息一下。正如我之前所说,由于位置监听器回调发生在主线程上,因此不会阻塞。

当您说这是在后台服务中运行时,您能否更加具体。你的意思是什么?