I am trying to create a service which gives location updates when app is in background specially for Android 8 and above as they have optimized the background service. I read some of the answers on the stackOverflow and Google API doc, they have mention that passing PendingIntent to onRequestLocationUpdates will give location even when app is in background. As mention in the doc. Here is my Service class:
public class LocationMonitoringService extends Service implements
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
LocationListener {
private static final String TAG = LocationMonitoringService.class.getSimpleName();
GoogleApiClient mLocationClient;
LocationRequest mLocationRequest = new LocationRequest();
public static final String ACTION_LOCATION_BROADCAST = LocationMonitoringService.class.getName() + "LocationBroadcast";
public static final String EXTRA_LATITUDE = "extra_latitude";
public static final String EXTRA_LONGITUDE = "extra_longitude";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mLocationClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mLocationRequest.setInterval(Constants.LOCATION_INTERVAL);
mLocationRequest.setFastestInterval(Constants.FASTEST_LOCATION_INTERVAL);
int priority = LocationRequest.PRIORITY_HIGH_ACCURACY; //by default
//PRIORITY_BALANCED_POWER_ACCURACY, PRIORITY_LOW_POWER, PRIORITY_NO_POWER are the other priority modes
mLocationRequest.setPriority(priority);
mLocationClient.connect();
//Make it stick to the notification panel so it is less prone to get cancelled by the Operating System.
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
/*
* LOCATION CALLBACKS
*/
@Override
public void onConnected(Bundle dataBundle) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
Log.d(TAG, "== Error On onConnected() Permission not granted");
//Permission not granted by user so cancel the further execution.
return;
}
Intent intent = new Intent(this, LocationMonitoringService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
LocationServices.FusedLocationApi.requestLocationUpdates(mLocationClient, mLocationRequest, pendingIntent);
Log.d(TAG, "Connected to Google API");
}
/*
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
@Override
public void onConnectionSuspended(int i) {
Log.d(TAG, "Connection suspended");
}
//to get the location change
@Override
public void onLocationChanged(Location location) {
Log.d(TAG, "Location changed");
if (location != null) {
Log.d(TAG, "== location != null");
//Send result to activities
sendMessageToUI(String.valueOf(location.getLatitude()), String.valueOf(location.getLongitude()));
}
}
private void sendMessageToUI(String lat, String lng) {
Log.d(TAG, "Sending info...");
Intent intent = new Intent(ACTION_LOCATION_BROADCAST);
intent.putExtra(EXTRA_LATITUDE, lat);
intent.putExtra(EXTRA_LONGITUDE, lng);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(TAG, "Failed to connect to Google API");
}
}
In the above code when I am passing PendingIntent to requestLocationUpdates its throwing me an error GoogleAPIClient not connected.
Below is the error I am getting
java.lang.IllegalStateException: GoogleApiClient is not connected yet.
at com.google.android.gms.internal.zzbcd.zze(Unknown Source)
at com.google.android.gms.internal.zzbcx.zze(Unknown Source)
at com.google.android.gms.internal.zzbcp.zze(Unknown Source)
at com.google.android.gms.internal.zzccb.requestLocationUpdates(Unknown Source)
at com.locationtracker.LocationMonitoringService.onConnected(LocationMonitoringService.java:93)
at com.google.android.gms.common.internal.zzac.zzn(Unknown Source)
at com.google.android.gms.internal.zzbcp.zzm(Unknown Source)
at com.google.android.gms.internal.zzbcd.zzpY(Unknown Source)
at com.google.android.gms.internal.zzbcd.onConnected(Unknown Source)
at com.google.android.gms.internal.zzbcx.onConnected(Unknown Source)
at com.google.android.gms.internal.zzbbi.onConnected(Unknown Source)
at com.google.android.gms.common.internal.zzaa.onConnected(Unknown Source)
at com.google.android.gms.common.internal.zzn.zzrj(Unknown Source)
at com.google.android.gms.common.internal.zze.zzs(Unknown Source)
at com.google.android.gms.common.internal.zzi.zzrk(Unknown Source)
at com.google.android.gms.common.internal.zzh.handleMessage(Unknown Source)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
I can see that you have instatiated your GoogleApiClient
instance in onStartCommand()
and as service life cycle says, onStartCommand()
will gets called every time when you start your service. So, every time it instatiates your GoogleApiClient instance.
I think this is creating conflict in your code. Let's consider a scenario to understand problem.
You have started your service once and after a second you have started it again. So, it initiates mLocationClient
both time because you have initiated it in onStartCommand() method. suppose 1st instance has value mLocationClient1 and other is mLocationClient2.(This values are only for simplicity in explanation).
After mLocationClient1
connected successfully it fires onConnected() method and inside it you have used mLocationClient
which is initialized by a new value mLocationClient2
which is not connected yet. Hence it returns error that googleapiclient is not connected.
If you think that you have started your service only once then why it calls onStartCommand() multiple times than here is an answer. whenever you remove your app from recent apps list is will stop your service but you have created START_STICKY
service so, os restart your service and call onStartCommand() method again.
So, i suggest you to instatiate mLocationClient
in onCreate()
method instead of onStartCommand()
because onCreate()
method will only gets called whenever service is created first time after it's destroyed.