• Android LBS系列05 位置策略(一)


    Location Strategies

     

    定位的实现

      在Android系统上实现定位主要是通过GPS或者是基于网络的定位方法。

      GPS是精度最高的,但是它只在户外有用,并且很耗电,并且首次定位花费时间较长。

      基于网络的定位利用通信网络蜂窝基站和Wi-Fi信号,这种定位方式在室内室外都能用,响应时间较短,耗电较少,但是精度较差。

      为了在应用中获得用户的信息,你的location provider可以是GPS或者基于网络,也可以两者都用。

     

    决定用户位置面临的挑战

      从手机上获取用户的位置是个比较复杂的问题,无论采取的数据源是什么,总是有一些因素会导致位置信息包含误差,从而不准确。

      误差来源如下:

      多种位置信息源

      GPS, Cell-ID, 和Wi-Fi都能为用户位置提供一定的线索。决定信任和采用哪些数据是一个需要权衡的过程,权衡时要考虑精度、速度、电池效率等。

      用户移动

      因为用户的位置会改变,所以应该考虑每隔一段时间重新估计用户位置。

      变化的精度

      每一个位置信息源的精度不是恒定的。也就是说位置估计即便是同一个信息源提供的,它的精确度也是不断在变化的。

     

    请求位置更新

      在Android中获取位置主要是通过回调函数。

      首先通过 LocationManager的 requestLocationUpdates()方法注册监听器,向其中传入一个实现了 LocationListener接口的对象。

      你的 LocationListener对象中必须实现一些回调函数,当用户位置改变或者当服务状态改变时,Location Manager就会调用这些回调函数。

      比如,下面的代码就展示了如何定义一个 LocationListener然后请求位置更新:

    // Acquire a reference to the system Location Manager
    LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    
    // Define a listener that responds to location updates
    LocationListener locationListener = new LocationListener() 
    {
        public void onLocationChanged(Location location) 
        {
          // Called when a new location is found by the network location provider.
          makeUseOfNewLocation(location);
        }
    
        public void onStatusChanged(String provider, int status, Bundle extras) {}
    
        public void onProviderEnabled(String provider) {}
    
        public void onProviderDisabled(String provider) {}
      };
    
    // Register the listener with the Location Manager to receive location updates
    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

       requestLocationUpdates() 中的第一个参数指明了location provider的类型,第二个参数是通知的最小时间间隔,第三个参数是通知的最小的改变距离。

      如果第二个和第三个参数都设置成0就表示要尽可能频繁地请求位置通知。

      最后一个参数是用户自己定义的实现了LocationListener接口的对象,它接收位置更新的回调。

      如果想要同时利用GPS和网络定位,可以调用 requestLocationUpdates()两次,第一个参数分别是 GPS_PROVIDER和 NETWORK_PROVIDER 

     

    权限设置

      如果没有权限设置,程序在请求位置更新时将会失败。

    <manifest ... >
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        ...
    </manifest>
     

      如果用NETWROK_PROVIDER, 那么需要声明 ACCESS_COARSE_LOCATIONINTERNET

      如果用GPS_PROVIDER, 那么需要声明 ACCESS_FINE_LOCATION

      ACCESS_FINE_LOCATION是包含了ACCESS_COARSE_LOCATION的,所以两者都用时可以只声明ACCESS_FINE_LOCATION

     

    建立一个最佳性能的模型

      为了克服获取用户位置时的种种困难,定义一个模型,具体化你的应用如何取得用户位置。

      这个模型包含了你何时开始和何时终止位置更新的监听,也包含了什么时候使用缓存的位置数据。

    获取用户位置的流程

      获取用户位置的典型流程如下:

                             

        1.首先开始应用。

        2.在某一个点,开始监听位置更新。

        3.维持一个当前位置的“最佳估计”。

        4.停止对位置更新的监听。

        5.利用最后一次的最佳位置估计。

      这个模型是一个窗口,窗口从开始监听开始,从停止监听结束。

    决定何时开始监听更新

      可以从应用一开始就启动监听,也可以在用户触发某个特性后开始。

      要清楚如果长时间监听位置会消耗很多电量,但是短时间的监听可能达不到足够的精度。

      调用requestLocationUpdates()开始监听:

    LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
    // Or, use GPS location data:
    // LocationProvider locationProvider = LocationManager.GPS_PROVIDER;
    
    locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);

    用上次定位的结果获取快速定位

      初次获得位置信息可能会花费较长时间。在获得一个比较精确的定位之前,可以利用一个缓存的位置:调用getLastKnownLocation(String)方法实现。

    LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
    // Or use LocationManager.GPS_PROVIDER
    
    Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
     

    决定何时停止监听

      位置获取和位置使用之间的时间间隔越小,对估计精度的改善越有利。

      永远记住长时间的监听位置更新将会非常费电,所以一旦你得到你需要的信息,就应该停止监听位置更新,调用removeUpdates(PendingIntent)方法实现:

    // Remove the listener you previously added
    locationManager.removeUpdates(locationListener);
     

    维护一个当前最佳估计

      因为定位精度是实时变化的,所以很可能最新的位置并不是最准确的。

      你应该制定一些标准并包含一套逻辑判断,来选择出最佳估计。

      这套标准也是随着使用情景而变化的。

      下面是你验证一个location fix的精确度时可以采取的步骤:

        1.检查是否当期位置数据比之前的估计数据要新很多;

        2.检查当前数据和之前数据的精度,谁更好;

        3.检查当前数据是从哪个provider处获得的,然后决定是否更加信任它;

      一个例子如下:

    
    
    选择最佳位置估计的一个例子
    private static final int TWO_MINUTES = 1000 * 60 * 2;
    
    /** Determines whether one Location reading is better than the current Location fix
      * @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);
    }
     

    调整模型以节约电量和进行数据交换

      当你测试模型的时候,可能发现你的模型需要做一些调整,以平衡它的准确度和性能。下面是一些你可以改变的地方:

      减小流程的窗口尺寸

      窗口尺寸减小,意味着与GPS和网络的交互减少,这样就可以节约电量。但是这样也就减少了获得最佳位置估计可以利用的位置数。

      降低location provider返回更新的频率

      在窗口中降低更新频率也可以改善电池效率,但是会导致精度的丢失。requestLocationUpdates() 通过其中两个参数就可以设定更新的时间和空间间隔,从而设定频率。

      限制provider

      根据应用的特定环境或者目标精度等级,可以选择只利用基于网络的定位或者只利用GPS定位,而不是同时利用两者。只与其中的一者交互将减少电量使用,但是会有潜在的精度丢失。

     

    参考资料:

      API Guides:Location Strategies

      http://developer.android.com/guide/topics/location/strategies.html

     

     

  • 相关阅读:
    C# is 与 as 运算符
    C# dynamic类型
    C# 数组
    C# 泛型
    C# 事件
    C# 委托
    C# DateTime类,TimeSpan类
    C# 获取当前应用程序的绝对路径支持asp.net
    C# 父子类_实例_静态成员变量_构造函数的执行顺序
    C# System.Uri类_获取Url的各种属性_文件名_参数_域名_端口等等
  • 原文地址:https://www.cnblogs.com/mengdd/p/2857859.html
Copyright © 2020-2023  润新知