Background service with location listener in android

137,301

Solution 1

First you need to create a Service. In that Service, create a class extending LocationListener. For this, use the following code snippet of Service:

public class LocationService extends Service {
public static final String BROADCAST_ACTION = "Hello World";
private static final int TWO_MINUTES = 1000 * 60 * 2;
public LocationManager locationManager;
public MyLocationListener listener;
public Location previousBestLocation = null;

Intent intent;
int counter = 0;

@Override
public void onCreate() {
    super.onCreate();
    intent = new Intent(BROADCAST_ACTION);
}

@Override
public void onStart(Intent intent, int startId) {
    locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    listener = new MyLocationListener();
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 4000, 0, (LocationListener) listener);
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 4000, 0, listener);
}

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

protected boolean isBetterLocation(Location location, Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return true;
    }

    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;

    // If it's been more than two minutes since the current location, use the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return true;
        // If the new location is more than two minutes older, it must be worse
    } else if (isSignificantlyOlder) {
        return false;
    }

    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;

    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());

    // Determine location quality using a combination of timeliness and accuracy
    if (isMoreAccurate) {
        return true;
    } else if (isNewer && !isLessAccurate) {
        return true;
    } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
        return true;
    }
    return false;
}



/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) {
        return provider2 == null;
    }
    return provider1.equals(provider2);
}



@Override
public void onDestroy() {
    // handler.removeCallbacks(sendUpdatesToUI);     
    super.onDestroy();
    Log.v("STOP_SERVICE", "DONE");
    locationManager.removeUpdates(listener);
}

public static Thread performOnBackgroundThread(final Runnable runnable) {
    final Thread t = new Thread() {
        @Override
        public void run() {
            try {
                runnable.run();
            } finally {

            }
        }
    };
    t.start();
    return t;
}
public class MyLocationListener implements LocationListener
{

    public void onLocationChanged(final Location loc)
    {
        Log.i("*****", "Location changed");
        if(isBetterLocation(loc, previousBestLocation)) {
            loc.getLatitude();
            loc.getLongitude();
            intent.putExtra("Latitude", loc.getLatitude());
            intent.putExtra("Longitude", loc.getLongitude());
            intent.putExtra("Provider", loc.getProvider());
            sendBroadcast(intent);

        }
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    public void onProviderDisabled(String provider)
    {
        Toast.makeText( getApplicationContext(), "Gps Disabled", Toast.LENGTH_SHORT ).show();
    }


    public void onProviderEnabled(String provider)
    {
        Toast.makeText( getApplicationContext(), "Gps Enabled", Toast.LENGTH_SHORT).show();
    }
}

Add this Service any where in your project, the way you want! :)

Solution 2

I know I am posting this answer little late, but I felt it is worth using Google's fuse location provider service to get the current location.

Main features of this api are :

1.Simple APIs: Lets you choose your accuracy level as well as power consumption.

2.Immediately available: Gives your apps immediate access to the best, most recent location.

3.Power-efficiency: It chooses the most efficient way to get the location with less power consumptions

4.Versatility: Meets a wide range of needs, from foreground uses that need highly accurate location to background uses that need periodic location updates with negligible power impact.

It is flexible in while updating in location also. If you want current location only when your app starts then you can use getLastLocation(GoogleApiClient) method.

If you want to update your location continuously then you can use requestLocationUpdates(GoogleApiClient,LocationRequest, LocationListener)

You can find a very nice blog about fuse location here and google doc for fuse location also can be found here.

Update

According to developer docs starting from Android O they have added new limits on background location.

If your app is running in the background, the location system service computes a new location for your app only a few times each hour. This is the case even when your app is requesting more frequent location updates. However if your app is running in the foreground, there is no change in location sampling rates compared to Android 7.1.1 (API level 25).

Solution 3

Background location service. It will be restarted even after killing the app.

MainActivity.java

public class MainActivity extends AppCompatActivity {
    AlarmManager alarmManager;
    Button stop;
    PendingIntent pendingIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (alarmManager == null) {
            alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
            Intent intent = new Intent(this, AlarmReceive.class);
            pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
            alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 30000,
                    pendingIntent);
        }
    }
}

BookingTrackingService.java

public class BookingTrackingService extends Service implements LocationListener {

    private static final String TAG = "BookingTrackingService";
    private Context context;
    boolean isGPSEnable = false;
    boolean isNetworkEnable = false;
    double latitude, longitude;
    LocationManager locationManager;
    Location location;
    private Handler mHandler = new Handler();
    private Timer mTimer = null;
    long notify_interval = 30000;

    public double track_lat = 0.0;
    public double track_lng = 0.0;
    public static String str_receiver = "servicetutorial.service.receiver";
    Intent intent;

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

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

        mTimer = new Timer();
        mTimer.schedule(new TimerTaskToGetLocation(), 5, notify_interval);
        intent = new Intent(str_receiver);
    }

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

        this.context = this;


        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy <<");
        if (mTimer != null) {
            mTimer.cancel();
        }
    }

    private void trackLocation() {
        Log.e(TAG, "trackLocation");
        String TAG_TRACK_LOCATION = "trackLocation";
        Map<String, String> params = new HashMap<>();
        params.put("latitude", "" + track_lat);
        params.put("longitude", "" + track_lng);

        Log.e(TAG, "param_track_location >> " + params.toString());

        stopSelf();
        mTimer.cancel();

    }

    @Override
    public void onLocationChanged(Location location) {

    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    /******************************/

    private void fn_getlocation() {
        locationManager = (LocationManager) getApplicationContext().getSystemService(LOCATION_SERVICE);
        isGPSEnable = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        isNetworkEnable = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        if (!isGPSEnable && !isNetworkEnable) {
            Log.e(TAG, "CAN'T GET LOCATION");
            stopSelf();
        } else {
            if (isNetworkEnable) {
                location = null;
                locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0, this);
                if (locationManager != null) {
                    location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                    if (location != null) {

                        Log.e(TAG, "isNetworkEnable latitude" + location.getLatitude() + "\nlongitude" + location.getLongitude() + "");
                        latitude = location.getLatitude();
                        longitude = location.getLongitude();
                        track_lat = latitude;
                        track_lng = longitude;
//                        fn_update(location);
                    }
                }
            }

            if (isGPSEnable) {
                location = null;
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, this);
                if (locationManager != null) {
                    location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                    if (location != null) {
                        Log.e(TAG, "isGPSEnable latitude" + location.getLatitude() + "\nlongitude" + location.getLongitude() + "");
                        latitude = location.getLatitude();
                        longitude = location.getLongitude();
                        track_lat = latitude;
                        track_lng = longitude;
//                        fn_update(location);
                    }
                }
            }

            Log.e(TAG, "START SERVICE");
            trackLocation();

        }
    }

    private class TimerTaskToGetLocation extends TimerTask {
        @Override
        public void run() {

            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    fn_getlocation();
                }
            });

        }
    }

//    private void fn_update(Location location) {
//
//        intent.putExtra("latutide", location.getLatitude() + "");
//        intent.putExtra("longitude", location.getLongitude() + "");
//        sendBroadcast(intent);
//    }
}

AlarmReceive.java (BroadcastReceiver)

public class AlarmReceive extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("Service_call_"  , "You are in AlarmReceive class.");
        Intent background = new Intent(context, BookingTrackingService.class);
//        Intent background = new Intent(context, GoogleService.class);
        Log.e("AlarmReceive ","testing called broadcast called");
        context.startService(background);
    }
}

AndroidManifest.xml

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

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

        <receiver
            android:name=".ServiceAndBroadcast.AlarmReceive"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

Solution 4

Very easy no need create class extends LocationListener 1- Variable

private LocationManager mLocationManager;
private LocationListener mLocationListener;
private static double currentLat =0;
private static double currentLon =0;

2- onStartService()

@Override public void onStartService() {
    addListenerLocation();
}

3- Method addListenerLocation()

 private void addListenerLocation() {
    mLocationManager = (LocationManager)
            getSystemService(Context.LOCATION_SERVICE);
    mLocationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            currentLat = location.getLatitude();
            currentLon = location.getLongitude();

            Toast.makeText(getBaseContext(),currentLat+"-"+currentLon, Toast.LENGTH_SHORT).show();

        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }

        @Override
        public void onProviderEnabled(String provider) {
            Location lastKnownLocation = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            if(lastKnownLocation!=null){
                currentLat = lastKnownLocation.getLatitude();
                currentLon = lastKnownLocation.getLongitude();
            }

        }

        @Override
        public void onProviderDisabled(String provider) {
        }
    };
    mLocationManager.requestLocationUpdates(
            LocationManager.GPS_PROVIDER, 500, 10, mLocationListener);
}

4- onDestroy()

@Override
public void onDestroy() {
    super.onDestroy();
    mLocationManager.removeUpdates(mLocationListener);
}
Share:
137,301

Related videos on Youtube

Nilanchala Panigrahy
Author by

Nilanchala Panigrahy

A blogger, a bit of a tech freak, and a full stack developer.

Updated on July 05, 2022

Comments

  • Nilanchala Panigrahy
    Nilanchala Panigrahy almost 2 years

    I am creating a background service that will run in its own process. It should allow me to listen if the device location has changed. I should be able to change criteria like the distance moved before notifying the UI.

    How can I do the same? I have a little knowledge of service and LocationListener implementations. Any tutorials around the net would be appreciated.

    I got one back-link from stack overflow, but I didn't understand much of it.

  • Inactivist
    Inactivist about 11 years
    Please note: That code looks like it could be sourced from a GPL'd project (or, it might be from this MIT licensed project. Anyone know the license status of the above code?
  • Usman Kurd
    Usman Kurd over 10 years
    @Nilanchala if this works for you than mark it correct so that i can help others having such problem
  • PankajAndroid
    PankajAndroid over 10 years
    @UsmanKurd can you help me about this.. how to start and stop this service and how can i get this location in acitivity
  • Usman Kurd
    Usman Kurd over 10 years
    its all up to your Development Logic how you want to use it But to Start The Service do the Following Things 1 register broadCast Receiver 2 start Service 3 stop service to register the receiver do the following private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { yourClassName.this.MethodName(intent); } };
  • Usman Kurd
    Usman Kurd over 10 years
    after that its Up to You how you want to Update the date Example is Following private Location polledLocation = null; public void MethodName(Intent intent){final double lat = intent.getDoubleExtra("Latitude", 0.00); final double lng = intent.getDoubleExtra("Longitude", 0.00); final String provider = intent.getStringExtra("Provider"); polledLocation = new Location(provider); polledLocation.setLatitude(lat); polledLocation.setLongitude(lng);}
  • Usman Kurd
    Usman Kurd over 10 years
    to Start the Service you can do it in that way private void startService(){private void initService() { // TODO Auto-generated method stub Intent intent = new Intent(this, YourClassName.class); startService(intent); registerReceiver(broadcastReceiver, new IntentFilter(YourLocationServiceClass.BROADCAST_ACTION)); }
  • Usman Kurd
    Usman Kurd over 10 years
    And to Stop Service you can do it in that way @Override protected void onDestroy() { // TODO Auto-generated method stub super.onPause(); if(intent != null) { unregisterReceiver(broadcastReceiver); stopService(intent); } }
  • Dawood Awan
    Dawood Awan over 10 years
    @UsmanKurd You have added these two lines here: locationManager.requestLocationUpdates(LocationManager.NETWO‌​RK_PROVIDER, 4000, 0, listener); locationManager.requestLocationUpdates(LocationManager.GPS_P‌​ROVIDER, 4000, 0, listener); Does thie mean that it will give updates for both LocationProviders, GPS and NETWORK?
  • Usman Kurd
    Usman Kurd over 10 years
    not from both will pick from the Connected if both are connected will pick the one with priority
  • Martin Mlostek
    Martin Mlostek over 10 years
    what is the "performOnBackgroundThread" function for? is it used somewhere?
  • Michael Alan Huff
    Michael Alan Huff almost 10 years
    I'm getting a null pointer exception on this line locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); Can you recommend a fix?
  • ridoy
    ridoy almost 10 years
    This code isnot fetching update locations right now. I start the service from location A and it detects successfully, then i moved to location B, 450 meter far from location A.Even after one hour, it still shows my current location as location A! How weird!!
  • Nirmal
    Nirmal over 9 years
    Since Google Play Services includes location services, it's advisable to use it instead: github.com/nickfox/GpsTracker/blob/master/phoneClients/andro‌​id/…
  • Chen
    Chen about 9 years
    I think you are right, but some Custom Rom removed google play service so you can not use the fuse location provider.
  • gprathour
    gprathour about 9 years
    public Location previousBestLocation = null; I think its value remains always null as we are not changing it any where in the Service then in isBetterLocation() only the following condition if (currentBestLocation == null) { return true; } will run, then what is point of having rest of the code in this method to find out better location?
  • StuStirling
    StuStirling over 8 years
    If you just want to know roughly where you are at a certain time or for less accurate tracking then FusedLocationProvider is ok but for apps that require high accuracy tracking it's just not good enough
  • Adrian C.
    Adrian C. over 8 years
    A solution for those Custom Roms, is to use LOST from Mapzen. Offers same functionalities with FusedLocationProviderAPI from Google, without the need to register with Google Play Service
  • Totalys
    Totalys about 8 years
    Part of the answer is copied but not all the answer. I would recommend to add the credit for that part. Thank you for the answer.
  • Totalys
    Totalys about 8 years
    Does any one knows why if you implement LocationListener at the service it doesn't work? It only worked for me after I followed this answer creating a class and making this class implement LocationListener instead of the service it self.
  • Darek Deoniziak
    Darek Deoniziak almost 8 years
    Use onStartCommand() instead of onStart() and return START_STICKY. The above code will also not run in separate process (for these there are different solutions), so whenever app will be killed (mostly after going background) by Android system the Service will stop - although it will start again if there will be available resources thanks to START_STICKY. There are also different solutions to keep Service alive when application is in background.
  • Makalele
    Makalele over 6 years
    How can I track user route in a running app in android Oreo if it can update location only few times each hour?
  • Shailendra Madda
    Shailendra Madda over 6 years
    if (isNetworkEnable) { it could be if (!isNetworkEnable) { missing NOT in else case. ;)
  • Shailendra Madda
    Shailendra Madda over 6 years
    Also, need to check permission availability. if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return;}
  • Mandeep Kumar
    Mandeep Kumar almost 6 years
    I was able to fetch location when app was running using this code , but when app was killed it did not work , i'm trying to update location of user in database even when app is killed
  • tej shah
    tej shah almost 6 years
    @MandeepKumar which device are you using. i also facing some issue related with device. but i very well working with Nexus and Samsung phone
  • Mandeep Kumar
    Mandeep Kumar almost 6 years
    I was able to develop a project where I can fetch the location on of user even if app is killed , it was some work around , but it worked , my mail - [email protected] if you need the project.
  • tikhpavel
    tikhpavel over 5 years
    @DarekDeoniziak I used onStartCommand() with START_STICKY, but my service is still getting killed
  • Darek Deoniziak
    Darek Deoniziak over 5 years
    @glue_stick Service can get killed by the system (usually by lack of memory). START_STICKY only tells the system that it should restart the service whenever possible, typically it will happen when user will restart the app. If you want a service that will not get killed when system is low on resources, then read about ForegroundService (it will display sticky notification). If you want to periodically do job in the background then you could use JobScheduler to start IntentService, which should run short task and finish its job until JobScheduler will start its next instance again.
  • Anice Jahanjoo
    Anice Jahanjoo over 4 years
    why you write 'stopSelf()' ?? by this code service run just once.
  • David Callanan
    David Callanan over 4 years
    For some reason the location updates only happen when app is in view...
  • M. Usman Khan
    M. Usman Khan about 4 years
    In new Android versions, tons of permissions are required for location service to keep running. This simple service wont work, i m pretty sure
  • Anuj Raghuvanshi
    Anuj Raghuvanshi about 4 years
    Agree with @M.UsmanKhan becauase in latest Android versions startService is never going to keep app in background state. All Stuff needed to use foreground service and keep app running throught this. For more details - developer.android.com/about/versions/oreo/background#service‌​s