I am trying to write an app that tracks location in background and sends data to a server -- to monitor where my family members are, for example.
Currently I am playing wi
It comes out for Android 5 Lollipop it's better (much more stable) to use third-party library for bacground location receiving, namely io.nlopez.smartlocation.SmartLocation
from 'io.nlopez.smartlocation:library:3.3.3'
, as decribed here, along with a foreground service, as Rudrik Patel mentioned. Works like a charm.
In order to get location constantly from the application, you have to use the foreground service in which you can initialize the location manager and get the location update constantly as per the parameters that have been set. Also, make sure you have background location permission given as it is the requirement after API level 29. Following is the very basic flow of how it can be achieved. Make sure to start this service after taking the location permission:
public class MyCustomService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private PowerManager.WakeLock mWakeLock;
private LocationRequest mLocationRequest;
private boolean mInProgress;
private Boolean servicesAvailable = false;
@Override
public IBinder onBind(Intent arg0) {
return null;
}
private static final int UPDATE_INTERVAL_IN_SECONDS = 120;
private static final int MILLISECONDS_PER_SECOND = 1000;
public static final long UPDATE_INTERVAL = MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN_SECONDS;
private static final int FASTEST_INTERVAL_IN_SECONDS = 60;
public static final long FASTEST_INTERVAL = MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
@Override
public void onCreate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService();
}
mInProgress = false;
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mLocationRequest.setSmallestDisplacement(100);
servicesAvailable = servicesConnected();
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
setUpLocationClientIfNeeded();
super.onCreate();
}
private void setUpLocationClientIfNeeded() {
if (mGoogleApiClient == null)
buildGoogleApiClient();
}
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
protected synchronized void buildGoogleApiClient() {
this.mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
return true;
} else {
return false;
}
}
/* Used to build and start foreground service. */
private void startForegroundService() {
Intent notificationIntent = new Intent(this, HomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
String CHANNEL_ID = "1";
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.noti_icon)
.setPriority(Notification.PRIORITY_LOW)
.setOngoing(true)
.setAutoCancel(false)
.setContentTitle("ServiceTitle")
.setContentText("Service Reason text")
.setTicker("TICKER")
.setChannelId(CHANNEL_ID)
.setVibrate(new long[]{0L})
.setContentIntent(pendingIntent);
Notification notification = builder.build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "NOTIFICATION_CHANNEL_NAME", NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("NOTIFICATION_CHANNEL_DESC");
channel.enableVibration(false);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
notificationManager.createNotificationChannel(channel);
}
startForeground(123, notification);
}
@Override
public void onDestroy() {
super.onDestroy();
this.mInProgress = false;
if (this.servicesAvailable && this.mGoogleApiClient != null) {
this.mGoogleApiClient.unregisterConnectionCallbacks(this);
this.mGoogleApiClient.unregisterConnectionFailedListener(this);
this.mGoogleApiClient.disconnect();
// Destroy the current location client
this.mGoogleApiClient = null;
}
if (this.mWakeLock != null) {
this.mWakeLock.release();
this.mWakeLock = null;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService();
}
setUpLocationClientIfNeeded();
if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Intent intent = new Intent(this, LocationReceiver.class);
PendingIntent pendingIntent = PendingIntent
.getBroadcast(this, 54321, intent, PendingIntent.FLAG_CANCEL_CURRENT);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
if (this.mGoogleApiClient != null)
LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient,
mLocationRequest, pendingIntent);
}
@Override
public void onConnectionSuspended(int i) {
// Turn off the request flag
mInProgress = false;
// Destroy the current location client
mGoogleApiClient = null;
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
mInProgress = false;
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
// If no resolution is available, display an error dialog
} else {
}
}
}
Here is the Location receiver class which you need to register in the Androidmanifest file also
public class LocationReceiver extends BroadcastReceiver {
private String TAG = "LOCATION RECEIVER";
private LocationResult mLocationResult;
private Context context;
Location mLastLocation;
@Override
public void onReceive(Context context, Intent intent) {
// Need to check and grab the Intent's extras like so
this.context = context;
if (LocationResult.hasResult(intent)) {
this.mLocationResult = LocationResult.extractResult(intent);
if (mLocationResult.getLocations().get(0).getAccuracy() < 100) {
// DO WHATEVER YOU WANT WITH LOCATION
}
}
}
}
Permission that is required:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
Note: Some of the methods like FusedLocationApi and isGooglePlayServicesAvailable in above code is deprecated