0

I want to collect location updates for a specific time duration and do something with the locations at the end. I currently keep track of the time passed in the method onLocationChanged.

public class LocationProvider implements
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        LocationListener {
    private static final String TAG = LocationProvider.class.getSimpleName();

    private Context mContext;

    private GoogleApiClient mGoogleApiClient;
    private LocationRequest mLocationRequest;

    private long mStartTime;
    private long mDuration;

    List<Location> locations;

    public LocationProvider(Context context) {
        mContext = context;

        GoogleApiAvailability api = GoogleApiAvailability.getInstance();
        if (api.isGooglePlayServicesAvailable(mContext) == ConnectionResult.SUCCESS) {
            mGoogleApiClient = new GoogleApiClient.Builder(mContext)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        } else {
            Log.e(TAG, "Could not connect to Google Play services");
        }
    }

    @Override
    public void onConnected(Bundle bundle) {
        LocationServices.FusedLocationApi.requestLocationUpdates(
                mGoogleApiClient, mLocationRequest, this);
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.e(TAG, "onConnectionFailed: " + connectionResult.toString());
    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.e(TAG, "GoogleApiClient connection has been suspended: " + String.valueOf(i));
    }

    @Override
    public void onLocationChanged(Location location) {
        locations.add(location);

        if (System.currentTimeMillis() - mStartTime > mDuration) {
            stopTracking();
            // do stuff
        }
    }

    public void startTracking(long interval, long fastInterval, long duration) {
        mDuration = duration;
        locations = new ArrayList<>();

        mLocationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(interval)
                .setFastestInterval(fastInterval);

        if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting()) {
            mGoogleApiClient.connect();
        }

        mStartTime = System.currentTimeMillis();
    }

    private void stopTracking() {
        if (mGoogleApiClient.isConnected()) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
            mGoogleApiClient.disconnect();
        }
    }
}

However, I want to divorce this logic, because I want to do different things with the locations depending on my needs. I reasoned that if I created a new thread for registering my location listener, I could wait on the main thread until the location collection completed to use the locations list.

public class LocationUpdates {
    private LocationProvider mLocationProvider;
    private Looper mLooper;

    public List<Location> gatherUpdates(final Context context,
                                        final long interval,
                                        final long fastInterval,
                                        final long duration)
            throws InterruptedException {

        long startTime = System.currentTimeMillis();

        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                mLooper = Looper.myLooper();
                mLocationProvider = new LocationProvider(context);
                mLocationProvider.startTracking(interval, fastInterval, duration);
                Looper.loop();
            }
        }.start();

        while (System.currentTimeMillis() - startTime < duration) {

        }

        mLooper.quit();
        mLooper.getThread().join();

        return mLocationProvider.locations;
    }
}

Instead, what I observed was (interval 3 seconds, duration 10 seconds):

  • the line mLocationProvider.startTracking(interval, fastInterval, duration); is reached
  • gatherUpdates returns after however long
  • onLocationChanged is called for the first time only now

So, even though the location listener is registered, something clearly blocks it from receiving updates. I can't figure out why my logic doesn't do what I expect it to do.

Is there a way, even without a thread, to collect a bunch of location updates and work with them outside of onLocationChanged only after the collection has finished?

Nikhil PV
  • 1,014
  • 2
  • 16
  • 29
Reti43
  • 9,656
  • 3
  • 28
  • 44
  • If the downvoter could kindly point out what is lacking in this question, I could potentially address it. An answer to this question is still of interest to me. – Reti43 Jul 31 '17 at 13:48

1 Answers1

0

You could build your Location Updates request with an Intent to a BroadcastReceiver. Then remove the updates when the location object's time exceeds your duration.

int requestCode = 1;
Intent intent = new Intent(this, LocationReceiver.class); // Request updates to Receiver
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, pendingIntent);

In your broadcast receiver:

@Override
public void onReceive(Context context, Intent intent) {

    if(LocationResult.hasResult(intent)){
        LocationResult locResult = LocationResult.extractResult(intent);
        Location location = locResult.getLocations().get(0);
        // Check the location's time and maybe stop location updates
    }
}
Sammy T
  • 1,924
  • 1
  • 13
  • 20
  • I'm afraid this doesn't seem to help me. All you're showing is how to pass my location updates to a BroadcastReceiver, while I can already do that with a LocationListener. My problem is I have a method which does stuff, then needs to get some location data for a specific amount of time and once that's done, retrieve the results and carry on with its business. The question is how to make that method wait without blocking the updates. – Reti43 Mar 10 '17 at 10:28
  • @Reti43 I suggested BroadcastReceiver because they as well as IntentServices can process the location Intents without your app being in the foreground. What I'm trying to say though is that you can request updates from wherever your process finishes, store the location's values in a database, check the time on each update to see if it meets your criteria, and, if not, you can stop storing the data, remove the updates if you want, and process the stored data. I wouldn't approach it by calling wait. I'd have one task do the pre-location processing and another do the post-location processing. – Sammy T Mar 10 '17 at 20:53