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?
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.