代码之家  ›  专栏  ›  技术社区  ›  ablack89

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

  •  0
  • ablack89  · 技术社区  · 14 年前

    我有一个类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;
        }
    
    
    }
    
    1 回复  |  直到 14 年前
        1
  •  0
  •   David Wasser    14 年前

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

    当你说这是在一个 后台服务 .你说的“糟透了”是什么意思?