Background service with location listener in android
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);
}
Related videos on Youtube
Nilanchala Panigrahy
A blogger, a bit of a tech freak, and a full stack developer.
Updated on July 05, 2022Comments
-
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.
-
Felix over 11 yearsCheck out this blog post: android-developers.blogspot.ro/2011/06/…
-
Zar E Ahmer over 7 yearsSee latest GoogleApiClient method stackoverflow.com/a/41981246/3496570
-
-
Inactivist about 11 yearsPlease 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 over 10 years@Nilanchala if this works for you than mark it correct so that i can help others having such problem
-
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 over 10 yearsits 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 over 10 yearsafter 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 over 10 yearsto 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 over 10 yearsAnd 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 over 10 years@UsmanKurd You have added these two lines here: locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 4000, 0, listener); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 4000, 0, listener); Does thie mean that it will give updates for both LocationProviders, GPS and NETWORK?
-
Usman Kurd over 10 yearsnot from both will pick from the Connected if both are connected will pick the one with priority
-
Martin Mlostek over 10 yearswhat is the "performOnBackgroundThread" function for? is it used somewhere?
-
Michael Alan Huff almost 10 yearsI'm getting a null pointer exception on this line locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); Can you recommend a fix?
-
ridoy almost 10 yearsThis 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 over 9 yearsSince Google Play Services includes location services, it's advisable to use it instead: github.com/nickfox/GpsTracker/blob/master/phoneClients/android/…
-
Chen about 9 yearsI think you are right, but some Custom Rom removed google play service so you can not use the fuse location provider.
-
gprathour about 9 years
public Location previousBestLocation = null;
I think its value remains alwaysnull
as we are not changing it any where in theService
then inisBetterLocation()
only the following conditionif (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 over 8 yearsIf 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. over 8 yearsA 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 about 8 yearsPart 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 about 8 yearsDoes 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 almost 8 yearsUse 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 over 6 yearsHow can I track user route in a running app in android Oreo if it can update location only few times each hour?
-
Shailendra Madda over 6 years
if (isNetworkEnable) {
it could beif (!isNetworkEnable) {
missing NOT in else case. ;) -
Shailendra Madda over 6 yearsAlso, 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 almost 6 yearsI 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 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 almost 6 yearsI 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 over 5 years@DarekDeoniziak I used onStartCommand() with START_STICKY, but my service is still getting killed
-
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 over 4 yearswhy you write 'stopSelf()' ?? by this code service run just once.
-
David Callanan over 4 yearsFor some reason the location updates only happen when app is in view...
-
M. Usman Khan about 4 yearsIn 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 about 4 yearsAgree 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#services