0

I am trying to get the current location of the device in foreground service using the Fused Location Provider Client in Android O. But the service is killed right after location request is made. I am thinking the service is killed because the fused location provider client gets the location in another background thread and when looking at the perspective of the service, it's task is done. The issue is after service is destroyed no location update is received. How can I keep the service alive until service is specifically stopped?

My Service Class :

    private val TAG = LocationService::class.java.simpleName

    companion object {
        val STOP_ACTION = "STOP_ACTION"
    }

    private lateinit var mFusedLocationClient: FusedLocationProviderClient
    private lateinit var mLocationCallback: LocationCallback
    private lateinit var notificationManager: NotificationManager
    private val notificationId = 1

    private val UPDATE_INTERVAL_IN_MILLISECONDS = 5000L
    private val FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 500L
    private val CHANNEL_ID = TAG

    override fun onHandleIntent(intent: Intent?) {
        Log.d(TAG, "onHandleIntent start")
        if (intent?.action.equals(STOP_ACTION)) {
            Log.d(TAG, "stop service")
            stopForeground(true)
            stopSelf()
        } else {
            Log.d(TAG, "start location update")
            createNotificationChannel()
            val notificationIntent = Intent(this, OnboardingActivity::class.java)
            val pendingIntent = PendingIntent.getActivity(
                this,
                0, notificationIntent, 0
            )
            val notification = NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Tracking realtime location")
                .setContentText("Tap to open the application")
                .setSmallIcon(R.drawable.logo)
                .setContentIntent(pendingIntent)
                .setOngoing(true)
                .build()

            startForeground(notificationId , notification)
            startLocationUpdates()
        }
    }

    override fun onCreate() {
        super.onCreate()
        notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
        mLocationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                locationResult ?: return
                for (location in locationResult.locations) {
                    Log.d(TAG, "$location")
                }
            }

            override fun onLocationAvailability(p0: LocationAvailability?) {
                super.onLocationAvailability(p0)
            }
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.e(TAG, "onStartCommand")
        super.onStartCommand(intent, flags, startId)
        return START_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        mFusedLocationClient.removeLocationUpdates(mLocationCallback)
        Log.e(TAG, "location service destroyed")
    }

    private fun startLocationUpdates() {
        val mLocationRequest = LocationRequest()
        mLocationRequest.interval = UPDATE_INTERVAL_IN_MILLISECONDS
        mLocationRequest.fastestInterval = FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS
        mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY

        try {
            mFusedLocationClient.requestLocationUpdates(
                mLocationRequest,
                mLocationCallback,
                Looper.myLooper()
            )
        } catch (e: SecurityException) {
            Log.e(TAG, "Lost location permission. Could not request updates. $e")
        }
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val nChannel = notificationManager.getNotificationChannel(CHANNEL_ID)

            if (nChannel == null) {
                val serviceChannel = NotificationChannel(
                    CHANNEL_ID,
                    "Foreground Service Channel",
                    NotificationManager.IMPORTANCE_DEFAULT
                )
                notificationManager.createNotificationChannel(serviceChannel)
            }
        }
    }
}

Any help is highly appreciated.

1 Answers1

1

Try this

public class BackgroundLocationUpdateService extends Service {

    /**
    * Author:Hardik Talaviya
    * Date:  2019.08.3 2:30 PM
    * Describe:
    */

    private static final String TAG = "BackgroundLocation";
    private Context context;
    private FusedLocationProviderClient client;
    private LocationCallback locationCallback;
    private boolean stopService = false;
    private Handler handler;
    private Runnable runnable;
    private NotificationCompat.Builder builder = null;
    private NotificationManager notificationManager;

    @Override
    public void onCreate() {
        Log.e(TAG, "Background Service onCreate :: ");
        super.onCreate();
        context = this;

        handler = new Handler();
        runnable = new Runnable() {

            @Override
            public void run() {
                try {
                    requestLocationUpdates();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    handler.postDelayed(this, TimeUnit.SECONDS.toMillis(2));
                }
            }
        };
        if (!stopService) {
            handler.postDelayed(runnable, TimeUnit.SECONDS.toMillis(2));
        }
    }

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

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Log.e(TAG, "onTaskRemoved :: ");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand :: ");
        StartForeground();
        if (client != null) {
            client.removeLocationUpdates(locationCallback);
            Log.e(TAG, "Location Update Callback Removed");
        }
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "BackgroundService onDestroy :: ");
        stopService = true;
        if (handler != null) {
            handler.removeCallbacks(runnable);
        }
        if (client != null) {
            client.removeLocationUpdates(locationCallback);
            Log.e(TAG, "Location Update Callback Removed");
        }
    }

    private void requestLocationUpdates() {
        LocationRequest request = new LocationRequest();
        request.setFastestInterval(100)
                .setInterval(200)
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        client = LocationServices.getFusedLocationProviderClient(this);


        final int[] permission = {ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)};
        if (permission[0] == PackageManager.PERMISSION_GRANTED) {

            final Location[] location = {new Location(LocationManager.GPS_PROVIDER)};
            locationCallback = new LocationCallback() {
                @Override
                public void onLocationResult(LocationResult locationResult) {

                    location[0] = locationResult.getLastLocation();

                    if (location[0] != null) {
                        Log.d(TAG, "location update " + location[0]);
                        Log.d(TAG, "location Latitude " + location[0].getLatitude());
                        Log.d(TAG, "location Longitude " + location[0].getLongitude());
                        Log.d(TAG, "Speed :: " + location[0].getSpeed() * 3.6);

                        if (notificationManager != null && client != null && !stopService) {
                            builder.setContentText("Your current location is " +  location[0].getLatitude() + "," + location[0].getLongitude());
                            notificationManager.notify(101, builder.build());
                        }
                    }
                }
            };
            client.requestLocationUpdates(request, locationCallback, null);
        }
    }

    /*-------- For notification ----------*/
    private void StartForeground() {
        Intent intent = new Intent(context, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, PendingIntent.FLAG_ONE_SHOT);

        String CHANNEL_ID = "channel_location";
        String CHANNEL_NAME = "channel_location";

        notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
            notificationManager.createNotificationChannel(channel);
            builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID);
            builder.setColorized(false);
            builder.setChannelId(CHANNEL_ID);
            builder.setColor(ContextCompat.getColor(this, R.color.colorPrimaryDark));
            builder.setBadgeIconType(NotificationCompat.BADGE_ICON_NONE);
        } else {
            builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID);
        }
        builder.setOnlyAlertOnce(true);
        builder.setContentTitle(context.getResources().getString(R.string.app_name));
        builder.setContentText("Your current location is ");
        Uri notificationSound = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_NOTIFICATION);
        builder.setSound(notificationSound);
        builder.setAutoCancel(true);
        builder.setSmallIcon(R.mipmap.ic_notification_app_icon);
        builder.setContentIntent(pendingIntent);
        startForeground(101, builder.build());
    }
} 

I hope this can help you!

Hardik Talaviya
  • 1,396
  • 5
  • 18
  • 1
    Thanks for your reply, but unfortunately this did not work for me. Instead, I used the main looper for the fused location provider client and made the service sleep until it's specifically killed. – Harith Sankalpa Feb 27 '20 at 10:34