如何同步获取当前地理位置?

7
由于我们的应用程序的工作方式,我需要同步获取用户的当前位置。我们目前的实现使用 com.google.android.gms.location.LocationListener 接收位置更新。问题是,在尝试第一次调用之前,我需要在服务器端更新位置,否则用户会得到错误的数据。 LocationServices.FusedLocationApi.getLastLocation() 不适用于此,因为 a) 它似乎总是返回 null(出于某种原因),并且 b) 我们已经将用户的最后已知位置保存在服务器端,因此检索最后已知位置并将其发送到服务器是多余的。
伪代码
//this is the first call I need to make
webservice.appStart(AppStartBody body);
//then I want to retrieve the current location and send it to the server
webservice.setGeoLocation(getCurrentLocation());
//finally, I retrieve the items on the server based on the location
webservice.getListItems();

等待第一个位置更新事件是我想要避免的可能性,因为我不知道它会多快触发,而且我可能需要让我的用户在加载屏幕上等待很长时间,最终导致失去他们,因为没有人喜欢等待。


如果获取设备位置需要5秒钟,那就需要5秒钟。没有其他办法。如果你同步接收它,那么你的应用程序(或特定线程)将被卡住5秒钟。如果你异步接收,你可以利用这5秒钟做其他事情。将操作设置为同步并不能解决你的问题。getLastLocation()在开发者的测试手机上可能会返回null,因为没有其他应用程序请求位置信息。在实际用户使用带有Facebook、Google Now等应用的手机上可能会好一些,但我们当然不能依赖于此。 - Markus Kauppinen
“……我们已经将用户最后已知位置保存在服务器端,因此检索最后已知位置并将其发送到服务器是多余的。” ……除非自上次运行应用程序以来位置实际上已更改。那么它就不是多余的了。 - Markus Kauppinen
据我所了解,位置服务只会返回最后已知的位置,只要有应用程序在使用它。一旦所有应用程序从服务断开连接,它将再次返回 null,这并不是不太可能的情况。我不想依赖于像这样不可靠的东西。 - TormundThunderfist
1个回答

2
我通过将检索位置的过程放置在Rx可观察对象中,特别是Single中来解决了这个问题。调用Single对象的blockingGet方法以同步检索位置。然后将其放置在try catch块中进行重试,因为在第一次尝试期间并不总是可以获得位置信息。
(我知道这是一个旧问题,但我仍然会发布一个答案,以便我可以分享我是如何做到的。抱歉使用Kotlin和RxJava!但我认为每个人都可以理解我的思路并能够实现他们喜欢的方式。我还使用最新的Google位置API。)
// Use an executor to prevent blocking the thread where 
// this method will be called. Also, DO NOT call this on 
// the main thread!!!
fun tryRetrieveLocationSync(flc: FusedLocationProviderClient,
          executor: Executor, numOfRetries: Int = 3, 
          retryWaitTime: Long = 500): Location {

  var location: Location
  var i = 1

  while (true) {
    try {
      // Place the method call inside a try catch block
      // because `Single#blockingGet` will throw any exception
      // that was provided to the `Emitter#onError` as an argument.

      location = retrieveLocationSync(flc, executor)
    } catch (e: NoLocationDataException) {
      if (i <= numOfRetries) {
        // The value from the `FusedLocationProviderClient#lastLocation` 
        // task usually becomes available after less than second (tried
        // and tested!!), but it's really up to you.

        SystemClock.sleep(retryWaitTime * i)
        i++
      } else {
        throw e // Give up once all the retries have been used.
      }
    } catch (e: Exception) {
      // Rethrow anything else that was thrown from 
      // the `Single#blockingGet`.

      throw e
    }
  }

  return location
}

private fun retrieveLocationSync(flc: FusedLocationProviderClient, 
          executor: Executor): Location {

  return Single.create<Location> { emitter ->
    val task = flc.lastLocation
    task.addOnCompleteListener(executor, OnCompleteListener { // it ->
      if (it.isSuccessful) {
        if (it.result != null) {
          emitter.onSuccess(it.result)
        } else {
          // There is no location data available probably because
          // the location services has just been enabled, the device
          // has just been turned on, or no other applications has 
          // requested the device's location.
          emitter.onError(NoLocationDataException())
        }
      } else {
        // I haven't encountered any exception here but this is
        // just to make sure everything's catchable.
        emitter.onError(it.exception ?: RuntimeException())
      }
    })
  }.blockingGet()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接