I want to continuously get location updates in the background using service even app is paused I tried much but when the app is paused it won't get any of the user's locations.
Here is my service.java
package com.app.testservices;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
public class MyService extends Service {
private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 3000;
private FusedLocationProviderClient mFusedLocationClient;
private LocationRequest locationRequest;
private LocationSettingsRequest locationSettingsRequest;
Notification notification;
NotificationCompat.Builder builder;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
prepareNotification(intent);
startLocationUpdates();
return START_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
initData();
}
//Location Callback
private LocationCallback locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
Location currentLocation = locationResult.getLastLocation();
Log.d("Locations", currentLocation.getLatitude() + "," + currentLocation.getLongitude());
//Share/Publish Location
builder.setContentText(currentLocation.getLatitude() + "," + currentLocation.getLongitude());
Notification nm = builder.build();
startForeground(2, nm);
}
};
private void startLocationUpdates() {
mFusedLocationClient.requestLocationUpdates(this.locationRequest,
this.locationCallback, Looper.getMainLooper());
}
public void prepareNotification(Intent intent) {
String input = intent.getStringExtra("input");
Intent notificationIntent = new Intent(this, MyService.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
builder = new NotificationCompat.Builder(this, MyChannel.CHANNEL_ID)
.setContentTitle("My notification")
.setContentText(input)
.setSmallIcon(R.drawable.ic_android_black_24dp)
.setContentIntent(pendingIntent);
notification = builder.build();
startForeground(1, notification);
}
@Override
public void onDestroy() {
super.onDestroy();
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartservice");
broadcastIntent.setClass(this, Restarter.class);
this.sendBroadcast(broadcastIntent);
}
private void initData() {
locationRequest = LocationRequest.create();
locationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setNumUpdates(Integer.MAX_VALUE);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(getApplicationContext());
}
@Override
public void onTaskRemoved(Intent rootIntent){
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 10,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
}
Channel.java
package com.app.testservices;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
public class MyChannel extends Application {
public static final String CHANNEL_ID = "serviceChannel";
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Notification service",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
I also tried to write a restarter to restart the service to fetch the location Restarter.java
package com.app.testservices;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;
public class Restarter extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("Broadcast Listened", "Service tried to stop");
Toast.makeText(context, "Service restarted", Toast.LENGTH_SHORT).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, MyService.class));
} else {
context.startService(new Intent(context, MyService.class));
}
}
}
Here is my MainActivity.java
package com.app.testservices;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Looper;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
public class MainActivity extends AppCompatActivity {
private EditText etInput;
protected static final int REQUEST_CHECK_SETTINGS = 0x1;
FusedLocationProviderClient mFusedLocationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etInput = findViewById(R.id.et_input_text);
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
ActivityCompat.requestPermissions(MainActivity.this, new String[]{
Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION
}, 1);
return;
}
}
public void startService(View view) {
String input = etInput.getText().toString();
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("input", input);
startService(serviceIntent);
}
public void stopService(View view) {
Intent serviceIntent = new Intent(this, MyService.class);
stopService(serviceIntent);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// If request is cancelled, the result arrays are empty.
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(MainActivity.this);
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
mFusedLocationClient.getLastLocation().addOnCompleteListener(new OnCompleteListener<Location>() {
@Override
public void onComplete(@NonNull Task<Location> task) {
Location location = task.getResult();
LocationCallback mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
Location mLastLocation = locationResult.getLastLocation();
}
};
if (location == null) {
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(5);
mLocationRequest.setFastestInterval(0);
mLocationRequest.setNumUpdates(2);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(MainActivity.this);
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
ActivityCompat.requestPermissions(MainActivity.this, new String[]{
Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION
}, 1);
return;
}
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
} else {
System.out.println("Location: " + location);
}
}
});
}
}
}
public void enableLocationSettings() {
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
LocationRequest locationRequest = LocationRequest.create()
.setInterval(5)
.setFastestInterval(0)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
LocationServices.getSettingsClient(this).checkLocationSettings(builder.build())
.addOnSuccessListener(this, (LocationSettingsResponse response) -> {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(MainActivity.this);
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
mFusedLocationClient.getLastLocation().addOnCompleteListener(task -> {
Location location = task.getResult();
LocationCallback mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
Location mLastLocation = locationResult.getLastLocation();
}
};
if (location == null) {
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(5);
mLocationRequest.setFastestInterval(0);
mLocationRequest.setNumUpdates(2);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(MainActivity.this);
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
ActivityCompat.requestPermissions(MainActivity.this, new String[]{
Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION
}, 1);
return;
}
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
} else {
System.out.println("Location: " + location);
}
});
}
}).addOnFailureListener(this, ex -> {
if (ex instanceof ResolvableApiException) {
// Location settings are NOT satisfied, but this can be fixed by showing the user a dialog.
try {
startActivityForResult(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS), 1);
ResolvableApiException resolvable = (ResolvableApiException) ex;
resolvable.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException sendEx) {
// Ignore the error.
}
}
});
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (REQUEST_CHECK_SETTINGS == requestCode) {
if (Activity.RESULT_OK == resultCode) {
}
}
}
}
Any help or guidance will be appreciated. Moreover, I also tried Android How to get user location continuously even your app is killed this but it didn't work for me.
You should use a Foreground Service with a partial wakelock, in order to prevent the phone from sleeping. Here below an example of Foreground Service Class that implements a Wakelock:
public class ICService extends Service {
private static final int ID = 1; // The id of the notification
private NotificationCompat.Builder builder;
private NotificationManager mNotificationManager;
private PowerManager.WakeLock wakeLock; // PARTIAL_WAKELOCK
/**
* Returns the instance of the service
*/
public class LocalBinder extends Binder {
public ICService getServiceInstance(){
return ICService.this;
}
}
private final IBinder mBinder = new LocalBinder(); // IBinder
@Override
public void onCreate() {
super.onCreate();
// PARTIAL_WAKELOCK
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"INSERT_YOUR_APP_NAME:wakelock");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
startForeground(ID, getNotification());
return START_NOT_STICKY;
}
@SuppressLint("WakelockTimeout")
@Override
public IBinder onBind(Intent intent) {
if (wakeLock != null && !wakeLock.isHeld()) {
wakeLock.acquire();
}
return mBinder;
}
@Override
public void onDestroy() {
// PARTIAL_WAKELOCK
if (wakeLock != null && wakeLock.isHeld()) {
wakeLock.release();
}
super.onDestroy();
}
private Notification getNotification() {
final String CHANNEL_ID = "YOUR_SERVICE_CHANNEL";
builder = new NotificationCompat.Builder(this, CHANNEL_ID);
//builder.setSmallIcon(R.drawable.ic_notification_24dp)
builder.setSmallIcon(R.mipmap.YOUR_RESOURCE_ICON)
.setColor(getResources().getColor(R.color.colorPrimaryLight))
.setContentTitle(getString(R.string.app_name))
.setShowWhen(false)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setOngoing(true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentText(composeContentText());
final Intent startIntent = new Intent(getApplicationContext(), ICActivity.class);
startIntent.setAction(Intent.ACTION_MAIN);
startIntent.addCategory(Intent.CATEGORY_LAUNCHER);
startIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 1, startIntent, 0);
builder.setContentIntent(contentIntent);
return builder.build();
}
}
Obviously you still have also to disable battery optimization for your app. You can browse a real working example of this service on a GPS Logging app here.
Don't forget to declare your service type as "location" into your AndroidManifest.xml
, in order to allow your application to receive the GPS updates also after a momentary signal loss when running in background, and to add the FOREGROUND_SERVICE and WAKE_LOCK permissions:
In your manifest you should declare:
...
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
...
<!-- Recommended for Android 9 (API level 28) and lower. -->
<!-- Required for Android 10 (API level 29) and higher. -->
<service
android:name="MyGPSService"
android:foregroundServiceType="location" ... >
</service>
...
As a note, if the location requests start when the app is in the foreground, you don't need to request the android.permission.ACCESS_BACKGROUND_LOCATION
permission to continue to receive locations in background, also in case of momentary signal loss.
the android.permission.ACCESS_FINE_LOCATION
(and, if you target API31, also android.permission.ACCESS_COARSE_LOCATION
) is enough for your use.