1

I want to wait for the callback intent in FusedLocationProviderClient.requestLocationUpdates() to finish. Is a while loop with a timeout the best solution? Below is an example, sans timeout.

Normally I would use startWakefulService() to wait for an asynchronous service to complete, but I don't know a way to use it with requestLocationUpdates().

Task t = flpc.requestLocationUpdates(locationRequest, pe);
while (! t.isSuccessful()) {
    Log.v(TAG, "Waiting for result...");
}
paperduck
  • 1,175
  • 1
  • 16
  • 26

3 Answers3

1

If you're familiar with RxJava/RxAndroid a bit you can wrap your method call in an Observable for example (or Callable, Single, whatever you see best), run it on another thread than the UI and then once you have a result from your method you receive it on the main thread, and do whatever you want with it.

In code it will look like this:

Observable.fromCallable(() -> flpc.requestLocationUpdates(locationRequest, pe))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(task -> onLocationUpdatesSuccess(task), throwable -> Log.e(TAG, throwable.getMessage()));

Otherwise another solution would be to wrap your method in an AsyncTask for example, you'd end up with something like this:

    new AsyncTask<Void, Void, Task>() {
            @Override
            protected Task doInBackground(Void... params) {
                return flpc.requestLocationUpdates(locationRequest, pe);
            }

            @Override
            protected void onPostExecute(Task task) {
                onLocationUpdatesSuccess(task)
            }
    }.execute();
Husayn Hakeem
  • 4,184
  • 1
  • 16
  • 31
0

Here is what I ended up doing:

Use a manual wakelock. Declare a static wakelock variable in your class. Also define two methods: acquire() and release().

acquire() instantiates a wakelock and assigns it the static wakelock variable in your class. It is an instance method (not static) because it uses PowerManager which needs a context.

release() is a static method so other classes can call it. It releases the wakelock. I used a singleton wakelock with a wakelock counter, decrement the counter when this is called, and release the wakelock when the counter is zero. But the counter was completely optional.

The "trick" to all this was creating an instance wakelock and assigning it to a static variable. This allows the wakelock to be modified in the service which didn't have access to an instance of my class.

Instead of calling finish() from outside the class, a cleaner solution is to use Task.OnCompleteListener() to call finish(). requestLocationUpdates() returns a Task object.

paperduck
  • 1,175
  • 1
  • 16
  • 26
0

Here is my solution for getting just 1 location:

private fun getNewLocation(): Single<Location> =
    Single.create { emitter ->
        locationClient.requestLocationUpdates(locationRequest, object : LocationCallback() {
            override fun onLocationResult(result: LocationResult) {
                locationClient.removeLocationUpdates(this)

                val location = result.locations.firstOrNull()
                if (location != null) {
                    emitter.onSuccess(location)
                } else {
                    emitter.onError(NullPointerException("Null location"))
                }
            }

            override fun onLocationAvailability(result: LocationAvailability) {
                if (!result.isLocationAvailable) {
                    locationClient.removeLocationUpdates(this)
                    emitter.onError(IllegalStateException("Location not available"))
                }
            }
        }, Looper.getMainLooper())
    }

Where locationRequest looks like:

private val locationRequest =
    LocationRequest()
        .setExpirationDuration(TimeUnit.SECONDS.toMillis(30L))
        .setInterval(TimeUnit.SECONDS.toMillis(15L))
        .setNumUpdates(1)
Gnzlt
  • 4,383
  • 2
  • 22
  • 24