1

I need to convert async FusedLocationApi.getLastLocation call to the sync one. I'm not a big expert in Java and I'm not sure I've did it right.

I want to connect to GoogleApiClient and then block calling thread until location information or error is received. Timeout more than 10 seconds should be treated as an error.

Will it work this way or onConnected will be called on main thread as well and it will be locked at that moment?

I also noticed that lock.wait(TimeUnit.SECONDS.toMillis(10)); continues instantly in regular runs, but in step-by-step debugging it does wait 10 seconds as intended.

public class SyncLocationService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

    public GoogleApiClient mGoogleApiClient;

    public SyncLocationService(Context context) {
        mGoogleApiClient = new GoogleApiClient.Builder(context)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    private final Object lock = new Object();
    public android.location.Location mLocation;

    @Override
    public android.location.Location lastLocation() {
        mGoogleApiClient.connect();
        synchronized (lock) {
            try {
                lock.wait(TimeUnit.SECONDS.toMillis(10));
            } catch (InterruptedException e) {
                Log.i("NativeLocationService", "InterruptedException");
            }
        }
        return mLocation;
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        try {
            mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            mGoogleApiClient.disconnect();
        } catch (SecurityException e) {
            Log.i("NativeLocationService", "SecurityException");
        } finally {
            synchronized (lock) {
                lock.notify();
            }
        }
    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        synchronized (lock) {
            lock.notify();
        }
    }
}

Basically I'm trying to rewrite similar iOS code which does it's job using semaphore.

var semaphore: DispatchSemaphore!
var location: CLLocation!

func lastLocation() -> location? {
    self.semaphore = DispatchSemaphore(value: 0)
    self.locationManager.startUpdatingLocation()
    _ = self.semaphore!.wait(timeout: .now() + 10)  // seconds
    self.semaphore = nil
    return self.location
}

// MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    guard let semaphore = self.semaphore else {
        return
    }
    guard let first = locations.first else {
        return
    }
    self.location = first
    self.locationManager.stopUpdatingLocation()
    semaphore.signal()
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Nik
  • 9,063
  • 7
  • 66
  • 81
  • Have a look at this answer: https://stackoverflow.com/questions/36740758/how-to-return-value-on-googleapiclient-onconnected – Entreco Jun 06 '17 at 19:05

1 Answers1

1

You can use GoogleApiClient.blockingConnect() to wait until the connection is established, then call FusedLocationApi.getLastLocation().

xizzhu
  • 895
  • 6
  • 9