如何正确处理位置服务切换

时间:2019-07-17 13:22:32

标签: android kotlin android-location android-fusedlocation

在我的应用中,我有一个按钮,可以使用FusedLocationProviderClient获取用户的当前位置。在关闭位置服务然后再在设备上重新打开之前,此方法可以正常工作。发生这种情况时,FusedLocationProviderClient.getLastLocation() 总是返回null 。我应该如何处理这种情况?我需要以某种方式重新初始化FusedLocationProviderClient吗?

我从documentation得知,关闭位置服务后,将刷新最后一个缓存的位置。这告诉我,我需要强制执行位置请求,以便刷新缓存。但是,我不确定如何执行此操作。我还遵循this Medium article来设置FusedLocationProviderClient,但是它所做的只是检查位置结果是否为空,并且在定位结果为空时不做任何处理。

我正在用Kotlin编写应用程序。这是我初始化FusedLocationProviderClient并在片段中获取用户当前位置的方式:

class LocationFragment : Fragment() {

    private lateinit var mFusedLocationProviderClient: FusedLocationProiderClient

    override fun onCreate(savedInstanceState: Bundle?) {
        mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(activity)
    }

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        // Initialize button on-click callback.
    }

    private fun getCurrentLocation() {
        // Check that the location service is enabled.
        // Check coarse and fine location permissions.

        mFusedLocationProviderClient.lastLocation.addOnSuccessListener {
            if (it != null) { // Always false if location services are toggled.
                print(it.latitude, it.longitude)
            }
        }
    }
}   

1 个答案:

答案 0 :(得分:1)

我对FusedLocationProviderClient并没有做很多事情,但是据我了解(和文档似乎同意),使用lastLocation(Java人员使用getLastLocation())并不能真正解决问题,并且“提取”当前设备位置,但仅返回设备提取的最后位置。通常,设备上的任何数量的服务都在请求位置,因此缓存中始终有一个合理准确的值供lastLocation使用。但是,如果您关闭并重新打开定位服务,然后立即致电lastLocation,则很可能会返回null。另请注意,lastLocation的文档状态为:

  

它特别适合于不需要精确位置的应用程序

从文档中进行getLocationAvailability()调用:

  

请注意,即使此方法返回true(例如,两次调用之间都禁用了位置设置),getLastLocation()也始终可能返回null。

这也使该方法实际上不执行位置查找,而是仅从高速缓存返回最新查找,并且当您打开/关闭服务时,高速缓存为空。

如果您需要确保获取设备的当前位置并确保它相当准确,则可能需要使用requestLocationUpdates()来获取位置客户端以实际计算出设备的当前位置并将其返回您提供的回调,然后在完成后删除该回调。

自从我与位置提供者合作以来已经有一段时间了,但是我认为您可以执行以下操作,假设您只需要一个位置:

    private val locationRequest: LocationRequest by lazy {
        LocationRequest.create().apply {
            interval = (LOCATION_UPDATE_INTERVAL_SECONDS * 1000).toLong()
            fastestInterval = (LOCATION_FAST_INTERVAL_SECONDS * 1000).toLong()
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
            smallestDisplacement =
                MINIMUM_DISPLACEMENT_METERS
        }
    }

    private fun isValidLocation(location: Location): Boolean {
        /* TODO: validate that the location meets all of your
                 requirements for accuracy, etc, and return the
                 appropriate true/false value
         */
        return true  // or false, for example if the location accuracy is not good enough.    
    }    


    private val locationCallback = object: LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {

            Log.d(TAG, "Location Received: $locationResult")

            val location = locationResult.lastLocation

            if (isValidLocation(location)) {
                Log.d(TAG, "Valid Location dectected: $location")

                // we have a valid location, so stop receiving further location updates.
                mFusedLocationClient.removeLocationUpdates(this)

                //TODO: we have a valid location! Use it as you wish to update the
                //      view, or emit it via a LiveData, etc.
            } else {
                // nothing to do, wait for more results.
                Log.d(TAG, "Location received was not valid: $location")
            }

        }
    }    

    private fun watchLocation() {

        // Check Location Permissions
        // Check Google Play Services Version
        // Check Location Settings
        // If all three are good, then:

        mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
    } 

上面的代码段设置了一个LocationCallback,当接收到一个位置时会调用它。它检查位置(通过isValidLocation方法),如果位置有效,它将自身从提供程序中删除,这样您就不会再获得其他位置更新了。在它自行删除的行(mFusedLocationClient.removeLocationUpdates(this))的后面,您可以执行location对象需要做的任何工作。如果收到的位置无效,它将在收到位置时继续获取其他回调,直到获得有效的位置为止。要从头开始,只需致电watchLocation()