Search code examples
androidlocationmanagergcmtaskservice

Wait for an async callback in a periodic task?


I'd like to check the user's location every 4 hours or so, and I don't want to leave my app running to do this. It looks like using a GcmTaskService with a PeriodicTask will let my service get called (WakefulBroadcastReceiver has restrictions against starting tasks when the app is stopped in Android 6+), and it will be compatible back to Android 4.4 (unlike JobScheduler) - my lowest supported Android version.

The issue is that GcmTaskService's onRunTask method is synchronous, but I want to use that to ask for a location and process the result (LocationManager will call my LocationListener implementation asynchronously).

How should this be handled?


Solution

  • Use a simple wait/notify:

    private interface RunnableLocationListener extends Runnable, LocationListener {}
    
    @Override
    public int onRunTask (TaskParams params) {
        final Object monitor = new Object();
        final AtomicBoolean located = new AtomicBoolean();
        new Thread(new RunnableLocationListener() {
            Context context = PeriodicCollector.this;
            public void run() {
                Criteria criteria = new Criteria();
                criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
                LocationManager locationManager = locationManager = (LocationManager)PeriodicCollector.this.getSystemService(Context.LOCATION_SERVICE);
                locationManager.requestSingleUpdate(criteria, this, Looper.getMainLooper());
            }
    
            @Override
            public void onLocationChanged(Location location) {
                // handle location here
    
                synchronized (monitor) {
                    located.set(true);
                    monitor.notifyAll();
                }
            }
    
            @Override public void onProviderDisabled(String s) {}
            @Override public void onProviderEnabled(String s) {}
            @Override public void onStatusChanged(String s, int i, Bundle b) {}
        }).start();
    
        int status = GcmNetworkManager.RESULT_FAILURE;
        try {
            synchronized (monitor) {
                if (!located.get()) {
                    monitor.wait();
                }
            }
        } catch (InterruptedException e) {
            status = GcmNetworkManager.RESULT_SUCCESS;
        }
        return status;
    }