Search code examples
android-syncadapterlocation-services

SyncAdapter onPerformSync get current location


When onPerformSync occurs I need the current location but I do not want to set up a separate service that is constantly active requesting location because my SyncAdapter period exponentially backs off such that the periods between syncs could be many hours apart. It would be wasteful to have location requests running between each sync.

I am planning on using a GoogleApiClient and LocationServices.FusedLocationApi.requestLocationUpdates then Thread.sleep(###) the onPerformSync thread until a location is found.

However I have read that requestLocationUpdates needs to be called on the main looper and that it makes callbacks on that thread in which case I expect will it fail to return location results because I am sleeping on the thread which called it.

Will I need to start my own looper thread?

Is there another/better way to get current location from onPerformSync?


Solution

  • Turns out my fears were not justified, my method does work without error. I have put together a handy example class below in case anyone else wants to do this:

    public class cSyncLocation  implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener
    {   
    
        // =======================================================
        // private vars
        // =======================================================
        private GoogleApiClient moGoogleApiClient;
        private LocationRequest moLocationRequest;
        private Location moCurrentLocation; 
        private static final int kTIMEOUT_MILLISECONDS = 2500;
    
        // =======================================================
        // public static vars
        // =======================================================
    
        // =======================================================
        // public methods
        // =======================================================
    
        public void Start(Context oContext)
        {
            if (moGoogleApiClient == null)
            {
                moGoogleApiClient = new GoogleApiClient.Builder(oContext)
                                    .addApi(LocationServices.API)
                                    .addConnectionCallbacks(this)
                                    .addOnConnectionFailedListener(this)
                                    .build();
            }
            if (moLocationRequest == null)
            {
                moLocationRequest = new LocationRequest();
    
                moLocationRequest.setInterval(1);
                moLocationRequest.setFastestInterval(1);
                moLocationRequest.setInterval(1);
                moLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            }
            // Start the connection
            if (moGoogleApiClient != null) 
            {
                if (!moGoogleApiClient.isConnecting() && !moGoogleApiClient.isConnected())
                    moGoogleApiClient.connect();
                else if (moCurrentLocation == null)
                    LocationServices.FusedLocationApi.requestLocationUpdates(moGoogleApiClient, moLocationRequest, this);
    
            }
        }
    
        public void Stop()
        {
            if (moGoogleApiClient != null && moGoogleApiClient.isConnected())
                LocationServices.FusedLocationApi.removeLocationUpdates(moGoogleApiClient, this);
            if (moGoogleApiClient != null)
                moGoogleApiClient.disconnect();     
        }
    
        public Location GetLocationBlocking(Context oContext)
        {
            if (moCurrentLocation == null)
            {
                intTimeout = kTIMEOUT_MILLISECONDS;
                Start(oContext);
                while(intTimeout > 0 && aFrmLocationActivity.IsLastLocationExpired(oContext))
                {
                    Thread.sleep(100);
                    intTimeout -= 100;
                }
                Stop();
            }
            return moCurrentLocation;
        }
    
        // =======================================================
        // Location API Events
        // =======================================================
    
        @Override
        public void onLocationChanged(Location oLocation) 
        {
            if (oLocation != null)
            {
                moCurrentLocation = oLocation;
            }
        }
    
        // =======================================================
        // Google API Connection Events
        // =======================================================
    
    
        @Override
        public void onConnected(Bundle connectionHint) 
        {
            // Connected to Google Play services! The good stuff goes here.
            if (moGoogleApiClient != null)
            {
                Location oLocation = LocationServices.FusedLocationApi.getLastLocation(moGoogleApiClient);
                if (oLocation != null)
                    moCurrentLocation = oLocation;
                else
                    LocationServices.FusedLocationApi.requestLocationUpdates(moGoogleApiClient, moLocationRequest, this);
            }
        }
    
        @Override
        public void onConnectionSuspended(int cause) 
        {
            //...
        }
    
        @Override
        public void onConnectionFailed(ConnectionResult result) 
        {
            //...
        }       
    
    
    }
    

    How to use it, in your onPerformSync method call it like this

    cSyncLocation oSyncLocation = new cSyncLocation();
    Location oLocation = oSyncLocation.GetLocationBlocking(getContext());
    

    Obviously you will want to add some exception handling and deal with null location result.