0

我有一个 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;
    }


}
4

1 回答 1

0

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

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

于 2012-07-27T12:58:45.107 回答