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

Posted

技术标签:

【中文标题】如何正确处理位置服务切换【英文标题】:How to properly handle location service toggling 【发布时间】:2019-11-26 08:25:00 【问题描述】:

在我的应用程序中,我有一个使用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】:

我没有对 FusedLocationProviderClient 做很多事情,但据我了解(文档似乎也同意),使用 lastLocation(Java 人员的 getLastLocation())并没有真正出去“获取”当前设备位置,但仅返回设备获取的最后一个位置。通常,设备上的任意数量的服务都在请求位置,因此缓存中总是有一个相当准确的值供lastLocation 使用。但是,如果您关闭并重新打开位置服务,然后立即调用lastLocation,您很可能会返回null。另请注意lastLocation 状态的文档:

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

来自getLocationAvailability() 调用的文档:

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

这些也证明了该方法实际上并不进行位置查找,而是仅从缓存中返回最近的查找,并且当您关闭/打开服务时,缓存是空的。

如果您需要确保获取设备的当前位置并确保它相当准确,您可能需要使用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())
     

上面的 sn-p 设置了一个LocationCallback,当接收到一个位置时它会被调用。它检查位置(通过isValidLocation 方法),如果它有效,它会从提供程序中删除自己,这样您就不会获得额外的位置更新。在它删除自身的行 (mFusedLocationClient.removeLocationUpdates(this)) 之后,您可以对 location 对象执行任何您需要执行的工作。如果接收到的位置无效,它会在接收到位置时不断收到额外的回调,直到你得到一个有效的回调。要开始这一切,只需致电watchLocation()

【讨论】:

嗨布拉德利康。您能否解释一下答案中的“完成后删除回调”部分?我已经尝试按照您的建议调用requestLocationUpdates(),但onLocationResult 回调永远不会被击中。 @ChaseFarmer,我编辑了答案以添加一些示例代码。这有帮助吗? 这很好用!谢谢你为我拼写出来?。我认为我之前的问题是我没有正确初始化LocationRequest。还在尝试理解 Kotlin 中的代表。

以上是关于如何正确处理位置服务切换的主要内容,如果未能解决你的问题,请参考以下文章

在MVVM架构Android中启动服务的正确位置是什么

切换应用内 GPS UISwitch,当用户切换应用的位置服务时

如何在 15 秒后从服务中启动处理程序以防止应用程序睡眠并将位置发送到 Web 服务?

如何通过 Handler 更新地图图标

在启动时请求位置更新

在 MVVM 架构 Android 中启动服务的正确位置是啥