javaandroidlocationandroid-servicefusedlocationproviderclient

FusedLocationProviderClient Background Service for Android 9 and Later


I am trying to get location after every 10 second using FusedLocationProviderClient, However when i minimize the app or terminate it the location updates stops. Below is my code. Right now i am using android 9.0. I have no idea why this is happening any help will be grateful

Android Menifest

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

LocationService.java

package com.example.locationupdate;

import android.Manifest;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import com.example.locationupdate.db.Realm$Helper;
import com.example.locationupdate.shared_pref.SaveInSharedPreference;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
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.SettingsClient;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;

import java.util.List;

import static com.google.android.gms.location.LocationServices.getFusedLocationProviderClient;

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

    //private LocationRequest mLocationRequest;
    private long UPDATE_INTERVAL = 10 * 1000;  /* 10 secs */
    private long FASTEST_INTERVAL = 2000;
    int count = 0;

    List<FilterData> datafromDB;
    FusedLocationProviderClient mFusedLocationClient;
    LocationRequest mLocationRequest;


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

    LocationCallback mLocationCallback = new LocationCallback(){
        @Override
        public void onLocationResult(LocationResult locationResult) {


            for (Location location : locationResult.getLocations()) {
                //Log.e("MainActivity", "Location: " + location.getLatitude() + " " + location.getLongitude());

                if (SaveInSharedPreference.getInSharedPreference(LocationService.this).getLat() == 0.0 && SaveInSharedPreference.getInSharedPreference(LocationService.this).getLng() == 0.0) {
                    SaveInSharedPreference.getInSharedPreference(LocationService.this).setLatLong(location.getLatitude(), location.getLongitude());
                }
                SaveInSharedPreference.getInSharedPreference(LocationService.this).setCurrentLatLong(location.getLatitude(), location.getLongitude());

                count = count + 1;

                Log.e("mLocationCallbackLat", String.valueOf(location.getLatitude()));
                Log.e("mLocationCallbackLong", String.valueOf(location.getLongitude()));
                //LocationMatch(location);
            }

           /* datafromDB = Realm$Helper.getLocation$Module(LocationService.this).getAllData();

            for (FilterData str : datafromDB) {

                String name = str.getName();

            }*/
        };

    };

    public void onResume() {
        Log.e("OnResume", "OnResumeEvent");
        if (mFusedLocationClient != null) {
            requestLocationUpdates();
        }
    }

    public void requestLocationUpdates() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(UPDATE_INTERVAL); // two minute interval
        mLocationRequest.setFastestInterval(FASTEST_INTERVAL);

        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
        }
    }


    public void onPause() {
        //super.onPause();
        Log.e("ONPause", "OnPauseEvent");
        if (mFusedLocationClient != null) {
            mFusedLocationClient.removeLocationUpdates(mLocationCallback);
        }
    }

    protected void startLocationUpdates() {

        // Create the location request to start receiving updates
        mLocationRequest = new LocationRequest();
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setInterval(UPDATE_INTERVAL);
        mLocationRequest.setFastestInterval(FASTEST_INTERVAL);

        // Create LocationSettingsRequest object using location request
        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
        builder.addLocationRequest(mLocationRequest);
        LocationSettingsRequest locationSettingsRequest = builder.build();

        // Check whether location settings are satisfied
        // https://developers.google.com/android/reference/com/google/android/gms/location/SettingsClient
        SettingsClient settingsClient = LocationServices.getSettingsClient(this);
        settingsClient.checkLocationSettings(locationSettingsRequest);

        // new Google API SDK v11 uses getFusedLocationProviderClient(this)
        getFusedLocationProviderClient(this).requestLocationUpdates(mLocationRequest, new LocationCallback() {
                    @Override
                    public void onLocationResult(LocationResult locationResult) {
                        // do work here
                        onLocationChanged(locationResult.getLastLocation());
                    }
                },
                Looper.myLooper());
    }

    public void onLocationChanged(Location location) {

        if (SaveInSharedPreference.getInSharedPreference(LocationService.this).getLat() == 0.0 && SaveInSharedPreference.getInSharedPreference(LocationService.this).getLng() == 0.0) {
            SaveInSharedPreference.getInSharedPreference(LocationService.this).setLatLong(location.getLatitude(), location.getLongitude());
        }
        SaveInSharedPreference.getInSharedPreference(LocationService.this).setCurrentLatLong(location.getLatitude(), location.getLongitude());

        Toast.makeText(this,"onLocationChanged",Toast.LENGTH_LONG);
        // New location has now been determined
        String msg = "Updated Location: " +
                Double.toString(location.getLatitude()) + "," +
                Double.toString(location.getLongitude());

        Log.e ("onLocationChangedLat", String.valueOf(location.getLatitude()));
        Log.e ("onLocationChangedLong", String.valueOf(location.getLongitude()));
        // You can now create a LatLng Object for use with maps
        LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());


            /*loc1.setLatitude(location.getLatitude());
            loc1.setLongitude(location.getLongitude());

            loc2.setLatitude(str.getLat());
            loc2.setLongitude(str.getlng());

            double distanceInMeters = loc1.distanceTo(loc2);
            Log.e("Name", name);
            Log.e("Distance", "" + distanceInMeters);*/
    }

    public void getLastLocation() {
        // Get last known recent location using new Google Play Services SDK (v11+)
        FusedLocationProviderClient locationClient = getFusedLocationProviderClient(this);

        locationClient.getLastLocation()
                .addOnSuccessListener(new OnSuccessListener<Location>() {
                    @Override
                    public void onSuccess(Location location) {
                        // GPS location can be null if GPS is switched off
                        if (location != null) {
                            onLocationChanged(location);
                        }
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        Log.d("MapDemoActivity", "Error trying to get last GPS location");
                        e.printStackTrace();
                    }
                });
    }

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

    }

    @Override
    public void onConnectionSuspended(int i) {

    }

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

    }
}

MainActitvity.java

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //locationTv = findViewById(R.id.location);

        Intent startIntent = new Intent(this, LocationService.class);
        startService(startIntent);


Solution

  • You can achieve this by setting up a handler and adding the required permissions to the android app. Apperantly there is a limit to how many times you can request the location in the background according to Android Location In Background but i have not seen this. Im requesting a new location every second.

    First add these to your manifest:

    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    

    Then you can create a handler like this.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        createLocationHandler();
        ...
    }
    

    The createLocationHandler and isServiceRunning function:

    public void createLocationHandler(){
        HandlerThread locationHandler = new HandlerThread("LocationHandler"); //Creates a new handler thread
        locationHandler.start(); //Starts the thread
        Handler handler = new Handler(locationHandler.getLooper()); //Get the looper from the handler thread
        handler.postDelayed(new Runnable() {//Run the runnable only after the given time
            @Override
            public void run() {
                //Check if the location service is running, if its not. lets start it!
                if(!isMyServiceRunning(LocationService.class)){
                    getApplicationContext().startService(new Intent(getApplicationContext(), LocationService.class));
                }
                //Requests a new location from the location service(Feel like it could be done in a less static way)
                LocationService.requestNewLocation();
                createLocationHandler();//Call the create location handler again, this will not be added to the stack because of the looper.
            }
        }, 10000);//Set the delay to be 10 seconds, 1 second = 1000 milliseconds
    }
    
    private boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) this.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
    

    Remember to ask for the background_location permission before you want to start the service.