Search code examples
androidservicegoogle-apiintentservicegoogle-location-services

"GoogleApiClient is not connected yet" error in IntentService


I'm creating an app that uses an AlarmManager in an IntentService to check for user's location every 30 minutes with the help of GoogleAPIClient.

I'm getting this error when onHandleIntent() fires in my service:

FATAL EXCEPTION: IntentService[FindLocationService]
Process: ir.imanirt.remindmeathome, PID: 1382
java.lang.IllegalStateException: GoogleApiClient is not connected yet.
at com.google.android.gms.internal.zzqb.zzd(Unknown Source)
at com.google.android.gms.internal.zzqf.zzd(Unknown Source)
at com.google.android.gms.internal.zzqd.zzd(Unknown Source)
at com.google.android.gms.location.internal.zzd.requestLocationUpdates(Unknown Source)
at ir.imanirt.remindmeathome.FindLocationService.getCurrentLocation(FindLocationService.java:122)
at ir.imanirt.remindmeathome.FindLocationService.onHandleIntent(FindLocationService.java:105)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:66)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.os.HandlerThread.run(HandlerThread.java:61)

Here is my FindLocationService.java class:

import android.Manifest;
import android.app.AlarmManager;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.NotificationCompat;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

public class FindLocationService extends IntentService {

    private static final String TAG = "FINDLOCATIONSERVICE";
    private static final int SERVICE_REQUEST_CODE = 101;
    private GoogleApiClient mClient;
    private NotificationCompat.Builder mNotificationBuilder;

    public FindLocationService() {
        super(FindLocationService.class.getName());
    }

    public static Intent newIntent(Context context) {
        return new Intent(context, FindLocationService.class);
    }

    public static boolean isServiceAlarmOn(Context context) {
        Intent i = FindLocationService.newIntent(context);
        PendingIntent pendingIntent = PendingIntent
                .getService(context, SERVICE_REQUEST_CODE, i, PendingIntent.FLAG_NO_CREATE);
        return pendingIntent != null;
    }

    public static void setServiceAlarm(Context context, boolean isOn) {
        final int GPS_TIME = 1000 * 60; //every 1 minute (for the sake of testing)

        Intent i = FindLocationService.newIntent(context);
        PendingIntent pendingIntent = PendingIntent.getService(context, SERVICE_REQUEST_CODE, i, 0);

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

        if (isOn) {
            alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,
                    SystemClock.elapsedRealtime(), GPS_TIME, pendingIntent);
        } else {
            alarmManager.cancel(pendingIntent);
            pendingIntent.cancel();
        }
    }

    @Override
    public void onDestroy() {
        mClient.disconnect();
        super.onDestroy();
    }

    @Override
    public void onCreate() {
        super.onCreate();

        mNotificationBuilder = new NotificationCompat.Builder(this);
        mNotificationBuilder.setSmallIcon(android.R.drawable.star_on);
        mNotificationBuilder.setContentTitle("Got a Location!!");   //set properties for notification

        mClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
                    @Override
                    public void onConnected(@Nullable Bundle bundle) {
                        Log.d(TAG, "Connected to Google location API");
                    }

                    @Override
                    public void onConnectionSuspended(int i) {

                    }
                })
                .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
                    @Override
                    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
                        Log.d(TAG, "Failed to connect to Google API");
                    }
                })
                .build();

        mClient.connect();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        getCurrentLocation();
    }

    private void getCurrentLocation() {
        LocationRequest request = new LocationRequest();
        request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        request.setNumUpdates(1);
        request.setInterval(0);

        if (ActivityCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Log.d(TAG, "Location permission denied.");
            return;
        }

        //I believe this is the line that causes the error:
        LocationServices.FusedLocationApi.requestLocationUpdates(mClient, request, new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                Log.d(TAG, "Got a location fix: " + location);

                //Notify user about the new location
                mNotificationBuilder.setContentText("Location is " + location);
                NotificationManager mNotificationManager = (NotificationManager)
                        getSystemService(Context.NOTIFICATION_SERVICE);

                mNotificationManager.notify(1, mNotificationBuilder.build());
            }
        });
    }
}

Also, there is a button in my fragment which calls setServiceAlarm to turn on the alarm manager.

As the error suggests, my GoogleAPIClient instance (mClient) is not connected when onHandleIntent fires up. I put the mClient.connect() inside onHandleIntent, but it still doesn't work. I don't understand why? Am I using a wrong logic to write my code? Should I place my client builder or my mClient.connect() method somewhere else?


Solution

  • I think I figured out the problem; mClient takes some time to connect to google services, therefore getCurrentLocation() must be executed when mClient is connected. Thanks to @Maxim Berezovsky answer, I added an onConnected(Bundle bundle) callback to solve this, but that raises another issue: onHandleIntent() exits faster than mClient can connect, so the service is killed before mClient is even connected. To solve this I used this code in my service:

    @Override
        public void onDestroy() {
            if (mClient.isConnected())
                mClient.disconnect();
            super.onDestroy();
        }
    

    this will make sure that mClient will not be disconnected even if the service is killed.

    I'm not sure if this method is power consuming, but that's the best I've got so far. Hope it helps someone.