【问题标题】:Android Location sometimes is too oldAndroid 位置有时太旧了
【发布时间】:2014-01-25 00:05:53
【问题描述】:

当我尝试在 android 中获取设备位置时遇到了一个奇怪的问题。

有时它可以很好地获取当前位置,但有时它会不断返回旧位置。旧的意思是几天前,距离它应该位于的地方几十甚至几百公里。

我不知道我做错了什么,我尝试了几种不同的方法来获取位置,但似乎每种方法都有同样的问题。

最奇怪的是,它并非在所有设备中都会发生……或者至少看起来是这样。三星 S3 和 S4 受影响最大,但在我的 nexus 4 中从未发生过。

这是我的代码,可能你看错了:

    public void startSearchingLocation() {

        // Define a listener that responds to location updates
        locationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                // Called when a new location is found by the network location
                // provider.


                makeUseOfNewLocation(location);

            }

            @Override
            public void onStatusChanged(String provider, int status,
                    Bundle extras) {
            }

            @Override
            public void onProviderEnabled(String provider) {
            }

            @Override
            public void onProviderDisabled(String provider) {
            }

        };

        String provider = getProvider();

        /*I ignore the passive provider*/
        if (provider == null
                || provider.contains(LocationManager.PASSIVE_PROVIDER)) {
            this.validProvider = false;
        } else {
            this.validProvider = true;
        }
        if (validProvider) {
            locationManager.requestLocationUpdates(provider, time, distance,
                    locationListener);

        }
    }

    protected void makeUseOfNewLocation(Location location) {
        JSONObject obj = new JSONObject();

        try {
            obj.put("latitude", location.getLatitude());
            obj.put("longitude", location.getLongitude());
            obj.put("provider", location.getProvider());
            locationResult = obj;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public JSONObject getLastKnownLocation() {
        if (locationResult != null) {
            return locationResult;
        } else {
            String provider = getProvider();
            Location location = locationManager.getLastKnownLocation(provider);

            if (location != null) {
                JSONObject obj = new JSONObject();

                try {
                    obj.put("latitude", location.getLatitude());
                    obj.put("longitude", location.getLongitude());
                    obj.put("provider", location.getProvider());
                    return obj;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public String getProvider() {
        Criteria criteria = new Criteria();

        criteria.setAccuracy(Criteria.ACCURACY_FINE);
        criteria.setPowerRequirement(Criteria.POWER_LOW);
        criteria.setAltitudeRequired(false);
        criteria.setBearingRequired(false);
        criteria.setSpeedRequired(false);

        String provider = locationManager.getBestProvider(criteria, true);

        return provider;

    }

谢谢

注意:这不是糟糕的互联网连接。一些用户已经开始抱怨这个问题。而其他使用地理定位的应用程序工作正常。它一定是我的代码中的某些东西,但我不知道是什么。 最奇怪的是,它似乎可以正常运行一两个星期,然后它开始同时在很多用户中发生

【问题讨论】:

    标签: android android-location


    【解决方案1】:

    我会说问题出在

    criteria.setPowerRequirement(Criteria.POWER_LOW);
    

    这可能只是返回被动位置提供程序。我会删除它,用户现在知道这种应用会消耗电池,所以它不会是一个强制标准。

    无论如何,我有一个更复杂的类来检索位置。让我解释一下。

    首先,from here我使用这个很棒的功能来确定一个位置读数是否比当前位置更好。

     /** 
     * @param location
     *            The new Location that you want to evaluate
     * @param currentBestLocation
     *            The current Location fix, to which you want to compare the new
     *            one
     */
    protected boolean isBetterLocation(Location location,
            Location currentBestLocation) {
        if (currentBestLocation == null) {
            // A new location is always better than no location
            return true;
        }
    
        // 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 true;
            // If the new location is more than two minutes older, it must be
            // worse
        } else if (isSignificantlyOlder) {
            return false;
        }
    
        // 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 true;
        } else if (isNewer && !isLessAccurate) {
            return true;
        } else if (isNewer && !isSignificantlyLessAccurate
                && isFromSameProvider) {
            return true;
        }
        return false;
    }
    
    /** Checks whether two providers are the same */
    private boolean isSameProvider(String provider1, String provider2) {
        if (provider1 == null) {
            return provider2 == null;
        }
        return provider1.equals(provider2);
    }
    

    然后,我创建了自己的实现 LocationListener 的类(活动或助手类,由您决定)。我只考虑 gps 和网络提供商,但它很容易扩展

    public class LocationObserver implements LocationListener {
    
        LocationManager lm;
        boolean gps_enabled = false;
        boolean network_enabled = false, network_connected = false;
    
    
        Context context;
        Long timeout; 
        int minTime;
        int minDistance;
        Handler handler = new Handler();
        private Location currentLocation = null;
    
        public LocationObserver(Context pContext, int minTime,
                int minDistance, long timeout) {
    
                this.context = pContext;
            this.timeout = timeout; 
            this.minDistance = minDistance;
            this.minTime = minTime;
        }
    

    我创建一个方法开始,我会考虑哪个是最好的提供者,或者我是否可以收听多个

        public void start() {
            if (lm == null)
                lm = (LocationManager) context
                        .getSystemService(Context.LOCATION_SERVICE);
    
            // exceptions will be thrown if provider is not permitted.
            try {
                gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
            } catch (Exception ex) {
                    //dont worry yet
            }
            try {
                network_enabled = lm
                        .isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    
                ConnectivityManager cm = (ConnectivityManager) context
                        .getSystemService(Context.CONNECTIVITY_SERVICE);
    
                NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
                network_connected = activeNetwork != null
                        && activeNetwork.isConnectedOrConnecting();
            } catch (Exception ex) {
                  //dont wory yet
            }
    
                //now, lets see what happend,  
    
                 //don't start listeners if no provider is enabled
            if (!gps_enabled && !network_enabled) {
    
                //nothing enabled, alert the user to enable any provide 
                       //Settings.ACTION_LOCATION_SOURCE_SETTINGS;
    
    
    
            }
    
            else { // one provider is enabled, and the other one is not
    
    
                if (gps_enabled) { // gps enabled, network disabled
                    lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                            minTime, minDistance, this);
    
                    //Consider asking the user to also enable network location ( Settings.ACTION_LOCATION_SOURCE_SETTINGS;=
    
    
                } else { // gps disabled, network enabled
    
    
                    // check if actually there is a conection
                    if (network_connected) {
                        lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                                minTime, minDistance, this);
    
                        //OK, Network is working, 
                        //consider asking the user to enable also gps (Settings.ACTION_LOCATION_SOURCE_SETTINGS;)
    
                    } 
    
                    //if network is not enabled, having the network location enabled is useles
    
                    else  {
    
                         //ask the user to connect to any network ( Settings.ACTION_WIRELESS_SETTINGS)
                    }
    
                }
            }
    
    
                //retrieve the lastKnownLocation (we will see this function later) and call the callback to start using it
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
    
                    Location l = getLastKnownLocation();
                    onLocationChanged(l);
    
                }
            }, timeout);
    
        }
    

    我们制作了自己的 getLastKnownLocation(),它会考虑所有启用的提供者,并返回最佳位置

        public Location getLastKnownLocation() {
    
            Location net_loc = null, gps_loc = null;
            if (gps_enabled)
                gps_loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            if (network_enabled)
                net_loc = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
    
            // if there are both values use the best one
            if (gps_loc != null && net_loc != null) {
                if (isBetterLocation(gps_loc, net_loc))
                    return gps_loc;
                else
                    return net_loc;
    
            }
    
            else if (gps_loc != null) {
                return gps_loc;
    
            } else if (net_loc != null) {
                return net_loc;
    
            } else
                return null;
        }
    

    最后,我们编写 onLocationChanged 回调,提供者将在任何更新时调用该回调。由于所有供应商都会调用它,可能即将到来的位置比我们已经拥有的位置更差,所以我们也比较它们

    public synchronized void onLocationChanged(Location location) {
    
            if (location == null || currentLocation == null
                    || isBetterLocation(location, currentLocation)) {
                currentLocation = location;
    
    
                //do whatever you need with the new location
    
            }
        }
    
    }
    

    此外,我经常使用计时器检查位置是否在第一次调用 getlastknowlocation 之后出现。如果经过特定时间,我会再次检查所有内容,查看启用了哪些提供程序,或者再次要求用户启用禁用的提供程序。

    另外,有些人喜欢直接依赖 Play Services Location API,您不必担心提供者,也无需关注底层定位技术的细节。 https://developer.android.com/google/play-services/location.html

    【讨论】:

    • 只是对 2018 年以后的读者的说明 - 不要使用 Location.getTime() 来比较位置时间。我遵循了来自 Google Developers (isBetterLocation()) 的完全相同的示例,并且遇到了一个奇怪的问题,即某些位置更新似乎比其他位置更新早 24 小时。使用getRealTimeNanos() 是继续使用 API 17> 的方法,Google 甚至不鼓励自己使用 getTime(),但显然 4 年内没有更新该示例......
    【解决方案2】:

    我认为您正在使用getLastKnownLocation(),但您并不希望这样做。 getLastKnownLocation() 的实现使用来自位置管理器的最后一个结果(如果可用),或者使用最后一个已知位置(如果没有可用的)。因此,如果您的用户关闭定位服务,您的应用程序将继续使用它看到的最后一个位置,来自makeUseOfNewLocation(),或者手机看到的最后一个位置,来自locationManager.getLastKnownLocation(provider)。这可以解释您所看到的行为,即代码不断返回相同的旧位置。

    要解决此问题,您可以使用回调 onProviderEnabled/onProviderDisabled 来通知您的应用不应继续使用 lastKnownLocation。您还可以使用 locationManager.isProviderEnabled() 或 locationClient.isConnected() 在使用位置之前检查您的位置服务是否已连接,并提示用户重新启用它们。

    【讨论】:

      【解决方案3】:
      Location location = locationManager.getLastKnownLocation(provider); 
      

      导致问题。 getLastKnownLocation(provider) 提供用户智能手机中任何基于位置的应用程序收集或使用的最后位置。

      您可以使用 Sharedpreferences 将位置数据保存在应用程序中,以便在需要时将其用作 lastknownlocation。

      【讨论】:

        【解决方案4】:

        用这种方式试试:

        并使用 bestProvider

        // Getting LocationManager object from System Service LOCATION_SERVICE
                    LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        
                    // Creating a criteria object to retrieve provider
                    Criteria criteria = new Criteria();
        
                    // Getting the name of the best provider
                    String provider = locationManager.getBestProvider(criteria, true);
        
                    // Getting Current Location From GPS
                    Location location = locationManager.getLastKnownLocation(provider);
        
                    if(location!=null)
                    {                                       
                        onLocationChanged(location);
                    }
        
                    locationManager.requestLocationUpdates(provider, 20000, 0, this);
        

        【讨论】:

        • 所以,这是由于网络连接不好。我认为不是你的代码问题
        【解决方案5】:

        AFAIK 位置源自 Android 设备可用的“最佳”来源。这意味着浏览器报告的 GPS 功能或位置(通过 Google 位置服务)。由于每个设备不同,您将获得不同的结果。您是否有专用的支持 GPS 的设备,最好使用 3G(或更好的通信) - 这样您将获得最佳定位结果。我认为这是一个错误,只是试图通过当前连接的任何网络定位您的设备的现实。

        【讨论】:

          【解决方案6】:

          试试这个:-

           public class MainActivity extends Activity implements LocationListener{
          protected LocationManager locationManager;
          protected LocationListener locationListener;
          protected Context context;
          TextView txtLat;
          String lat;
          String provider;
          protected String latitude,longitude; 
          protected boolean gps_enabled,network_enabled;
          
          @Override
          protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          txtLat = (TextView) findViewById(R.id.textview1);
          
          locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
          locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
          }
          @Override
          public void onLocationChanged(Location location) {
          txtLat = (TextView) findViewById(R.id.textview1);
          txtLat.setText("Latitude:" + location.getLatitude() + ", Longitude:" + location.getLongitude());
          }
          
          @Override
          public void onProviderDisabled(String provider) {
          Log.d("Latitude","disable");
          }
          
          @Override
          public void onProviderEnabled(String provider) {
          Log.d("Latitude","enable");
          }
          
          @Override
          public void onStatusChanged(String provider, int status, Bundle extras) {
          Log.d("Latitude","status");
          }
          }
          

          还要确保您在清单文件中指定了以下权限:-

           <uses-permission android:name="android.permission.INTERNET"/>
              <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
              <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
              <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
              <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
          

          【讨论】:

            【解决方案7】:

            如果您需要准确的定位,请将位置提供程序明确设置为读取 GPS(是的,它会消耗更多电池)。

            criteria.setPowerRequirement(Criteria.POWER_LOW);

            这将使用您所在地区的 wifi 信号和手机信号塔的被动定位,我认为这就是问题所在!

            POWER_LOW 使用您附近的任何 wifi/手机信号的 IP 地理位置。

            根据http://www.ipligence.com/geolocation,我的静态 IP 地址的位置在我实际位置以北 170 公里处。所以,如果你用我的 wifi 信号来定位,它会偏离 170 公里。

            即使是手机信号也只能在该区域的信号塔三角测量中定位您的位置,并且不是最准确的,因为它们相互重叠以保持信号强度。

            在我制作的 Android 应用上玩过这个之后,我说被动位置提供者的想法在理论上是好的,但在实际定位中并不可行。

            并且您的标准存在冲突:

            criteria.setAccuracy(Criteria.ACCURACY_FINE);
            criteria.setPowerRequirement(Criteria.POWER_LOW);
            

            ACCURACY_FINE 表示使用 GPS 作为位置提供者(使用大容量电池)。
            POWER_LOW 表示使用低功耗的 locationprovider(因此不要使用 GPS)

            【讨论】:

              【解决方案8】:

              我建议您实现Location's APILocationClient。所有关于选择哪个提供商的算法都是为您完成的;由于融合的提供者,它总是使用最好的提供者。它还使用其他传感器来获得最准确的位置。

              【讨论】:

                【解决方案9】:

                其实我也有同样的问题。我不仅可以在代码中看到它,还可以在其他设备的行为中看到它。

                我的 HTC Sensation 上的 Google 地图向我显示上周的位置,这恰好是我最后一次运行该应用程序,并且它是唯一使用我手机上位置信息的应用程序。

                所以我得出的结论是,这笔现金在某种程度上是由司机自己管理的。

                我通过忽略这些结果解决了这个问题。我始终记得我收到的最后一个位置数据,并且知道在不相关时忽略它。

                【讨论】:

                  猜你喜欢
                  • 2016-04-04
                  • 2015-05-23
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多