-1

I have a service running to track user location at a specific time interval. I have used foreground service for Oreo & post-Oreo devices and service for pre-Oreo devices. The service runs well when the app is minimised but when I remove the app from the minimised app list the service gets killed too. I have tested this on a Xiaomi device with Android 9. How I can keep the service running when the user kills the app.

Manifest:

<service android:name=".service.LocationService" />

MainActivity:

@OnClick(R.id.btn_start_tracking)
public void onClickStart(){

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        this.startForegroundService (new Intent (this, LocationService.class));
    } else {
        this.startService (new Intent (this, LocationService.class));
    }

}

Service class goes here

public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener{



public static final String TAG = LocationService.class.getSimpleName();
private static final long LOCATION_REQUEST_INTERVAL = 3000;
private static final float LOCATION_REQUEST_DISPLACEMENT = 5.0f;
private GoogleApiClient mGoogleApiClient;
private FusedLocationProviderClient mFusedLocationProviderClient;
private LocationRequest mLocationRequest;
private LocationCallback mLocationCallback;



@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

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

    Log.d ("On Create-------> ","Inside on create");
    buildGoogleApiClient();
    showNotificationAndStartForegroundService();

    mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(LocationService.this);
    mLocationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
            super.onLocationResult(locationResult);
            List<Location> locationList = locationResult.getLocations ();
            for (Location loc : locationList) {
                if (loc.getLatitude () != 0 && loc.getLongitude () != 0) {
                    Log.d (TAG,"Location -------------->>>>" + loc.getLatitude ()+ "   " + loc.getLongitude ());

                    saveUserLocation (loc.getLatitude (),loc.getLongitude (),10990816+1);
                    break;
                }
            }
        }
    };


}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d ("On Start Command---> ","Inside on Start Command");
    return START_STICKY;
}

private synchronized void buildGoogleApiClient() {
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(LocationServices.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();

    mGoogleApiClient.connect();
}


private void createLocationRequest() {
    mLocationRequest = LocationRequest.create();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationRequest.setInterval(LOCATION_REQUEST_INTERVAL);
   /* mLocationRequest.setSmallestDisplacement(LOCATION_REQUEST_DISPLACEMENT);*/
    mLocationRequest.setFastestInterval (LOCATION_REQUEST_INTERVAL/2);

    requestLocationUpdate();
}



private void requestLocationUpdate() {
    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.
        return;
    }

    mFusedLocationProviderClient.requestLocationUpdates(mLocationRequest, mLocationCallback,
            Looper.myLooper());
}

private void removeLocationUpdate() {
    mFusedLocationProviderClient.removeLocationUpdates(mLocationCallback);
}

/**
 * This Method shows notification for ForegroundService
 * Start Foreground Service and Show Notification to user for android all version
 */
private void showNotificationAndStartForegroundService() {

    final String CHANNEL_ID = BuildConfig.APPLICATION_ID.concat("_notification_id");
    final String CHANNEL_NAME = BuildConfig.APPLICATION_ID.concat("_notification_name");
    final int NOTIFICATION_ID = 100;

    NotificationCompat.Builder builder;
    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        int importance = NotificationManager.IMPORTANCE_NONE;
        assert notificationManager != null;
        NotificationChannel mChannel = notificationManager.getNotificationChannel(CHANNEL_ID);
        if (mChannel == null) {
            mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, importance);
            notificationManager.createNotificationChannel(mChannel);
        }
        builder = new NotificationCompat.Builder(this, CHANNEL_ID);
        builder.setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(getString(R.string.app_name));
        startForeground(NOTIFICATION_ID, builder.build());
    } else {
        builder = new NotificationCompat.Builder(this, CHANNEL_ID);
        builder.setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(getString(R.string.app_name));
        startForeground(NOTIFICATION_ID, builder.build());
    }
}

@Override
public void onConnected(@Nullable Bundle bundle) {

    createLocationRequest();
}

@Override
public void onConnectionSuspended(int i) {

}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

}

@Override
public void onTaskRemoved(Intent rootIntent) {

    Log.d ("On Task Removed------> ","inside on task removed");
    super.onTaskRemoved (rootIntent);
}

@Override
public void onDestroy() {

    Log.d ("On Destroy","On destroy called in location service");
    if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
        mGoogleApiClient.disconnect();
    }

    super.onDestroy();

}


private void saveUserLocation(Double lat,Double lon,Integer time){

    LocationSaveRequest locationSaveRequest = new LocationSaveRequest ();
    final List<com.vivacom.pi_sales_tracking.retrofit.locationsave.model.Location> locationList = new ArrayList<> ();
    com.vivacom.pi_sales_tracking.retrofit.locationsave.model.Location location = new com.vivacom.pi_sales_tracking.retrofit.locationsave.model.Location ();
    location.setUserId ("100");
    location.setLat (lat);
    location.setLon (lon);
    location.setClientTimestampUtc (System.currentTimeMillis()/1000);
    locationList.add (location);
    locationSaveRequest.setLocations (locationList);


    CallLocationSave.save ("KAKBDURKBJSBSKHGYBKA==", locationSaveRequest, new CallLocationSave.LocationSaveCallBack () {
        @Override
        public void onSuccess() {

            locationList.clear ();

        }

        @Override
        public void onFailure(LocationHistoryResponse locationHistoryResponse) {

            locationList.clear ();
        }
    });
}

}

Yash
  • 3,438
  • 2
  • 17
  • 33
Shadman Sakib
  • 219
  • 2
  • 10

3 Answers3

1

From Api level 26 or above service does not work in background while the app is killed due to battery consumption saving purpose. You can choose either Foreground service or Work Manager to track the location.

Here you can try how to get location in Work Manager.

Anand Kumar Jha
  • 614
  • 9
  • 23
  • Yes Work Manager is a good option. You can read more here https://stackoverflow.com/questions/50279364/android-workmanager-vs-jobscheduler Foreground-Service-Notification is a simpler way right now. – Yash Aug 20 '19 at 12:39
0

Foreground services cannot and will not run if they are not explicitly visible to the user. You need to use a foreground notification. This is a security feature.

Foreground

A foreground service performs some operation that is noticeable to the user. For example, an audio app would use a foreground service to play an audio track. Foreground services must display a Notification. Foreground services continue running even when the user isn't interacting with the app.

From : https://developer.android.com/guide/components/services

My solution, display a foreground notification in your Service:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    createNotificationChannel();
    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,
            0, notificationIntent, 0);

    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentIntent(pendingIntent)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build();

    startForeground(1, notification);

    return START_STICKY;
}

private void createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel serviceChannel = new NotificationChannel(
                CHANNEL_ID,
                "Application Service Channel",
                NotificationManager.IMPORTANCE_LOW
        );
        NotificationManager manager = getSystemService(NotificationManager.class);
        manager.createNotificationChannel(serviceChannel);
    }
}
Yash
  • 3,438
  • 2
  • 17
  • 33
  • Thanks for your help. I have already used foreground notification in my service and the respective method is called inside onCreate(). Do I need to place it inside onStartCommand(Intent intent, int flags, int startId) method? – Shadman Sakib Aug 20 '19 at 12:30
  • I have used it inside onStartCommand() . But the result is the same. The foreground notification goes away when i remove the app from the recent apps list. – Shadman Sakib Aug 20 '19 at 12:45
  • actually, I am saving lat-long in a server. After removing the app from recent apps lists no data has been saved in the remote database. – Shadman Sakib Aug 20 '19 at 13:02
  • First make sure that your service is running after the app closes, sending data is a different thing. Can you see the foreground notification after the app closes? – Yash Aug 20 '19 at 13:04
  • No, I cannot see the foreground notification after the app closes. – Shadman Sakib Aug 20 '19 at 13:06
  • Call showNotificationAndStartForegroundService() in onStartCommand() not onCreate() – Yash Aug 20 '19 at 13:08
  • Yes, I have called it inside onStartCommand(Intent intent, int flags, int startId). – Shadman Sakib Aug 20 '19 at 13:12
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/198182/discussion-between-yash-krishan-verma-and-shadman-sakib). – Yash Aug 20 '19 at 13:13
0

You should use START_STICKY flag this will restart the service once service stopped by system or from minimised apps list

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return START_STICKY;
}

test it from your side once service stop it automatically restarts after some times.

kiran boghra
  • 3,662
  • 2
  • 18
  • 23