I am trying to have an app give location updates while the app is in the background. When the app is open the service works fine. When the app is in the background the service continues running but the location updates stop. I have tried using a foreground service but this did not help.
I am using google's fusedLocationProviderClient
public int onStartCommand(Intent intent, int flags, int startId) {
client.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
}
With a location request defined in onCreate()
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(500);
locationRequest.setFastestInterval(500);
And callback defined as:
private final LocationCallback locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
List<Location> locationList = locationResult.getLocations();
if (locationList.size() != 0) {
Location location = locationList.get(0);
Log.e("AppLocationService", "Latitude - " +location.getLatitude()+", longitude - " +location.getLongitude() );
}
}
I'm really new to android studio so any help is much appreciated!
UPDATE:
Service started through startService()
public class BackgroundService2 extends Service {
private FusedLocationProviderClient client;
private LocationRequest locationRequest;
#Override
public void onCreate() {
super.onCreate();
client = LocationServices.getFusedLocationProviderClient(this);
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(500);
locationRequest.setFastestInterval(500);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent,flags,startId);
try {
if ( Build.VERSION.SDK_INT >= 23 &&
ContextCompat.checkSelfPermission( getBaseContext(), android.Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission( getBaseContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
client.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
}
} catch (SecurityException ignore) {
Log.e("AppLocationService", "SecurityException - " + ignore.toString(), ignore);
}
return START_STICKY;
}
private final LocationCallback locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
List<Location> locationList = locationResult.getLocations();
if (locationList.size() != 0) {
Location location = locationList.get(0);
Log.e("AppLocationService", "Latitude - " +location.getLatitude()+", longitude - " +location.getLongitude() );
}
}
};
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
}
Turns out it was a problem with my manifest. Not only is it necessary to declare the service, but also its type.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
as well as
<service
android:name=".BackgroundService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="location" />
Related
I want to develop a service that will work in background even the app gets killed. In my scenario I am getting location updates using fusedlocationproviderclient and sending it to database. The problem is that when I put app to background using home button location updates stop and approximately in 1 minute service gets killed. Again service gets killed if I kill the app from recent app tray. I have read that there are limitations for using fusedlocationproviderclient in background service. "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." Is it related to this?
Service in manifest:
<service
android:name=".Background_Location_Service"
android:enabled="true"></service>
Here I start the service:
Intent service_intent=new Intent(getApplicationContext(),Background_Location_Service.class);
service_intent.putExtra("username",username);
startService(service_intent);
My service:
public class Background_Location_Service extends Service {
private static Double lat;
private static Double lon;
private FusedLocationProviderClient fusedLocationProviderClient;
private LocationCallback locationCallback;
private LocationRequest locationRequest;
private String username;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
username = intent.getExtras().getString("username");
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
locationRequest = LocationRequest.create();
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(5000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
return;
}
for (Location location : locationResult.getLocations()) {
lat = location.getLatitude();
lon = location.getLongitude();
//Sending latitude, longitude and username to database
Call<ResponseBody> call = Retrofit_Client
.getInstance()
.getAPI()
.send_location(lat, lon, username);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String response_status = response.body().string();
Toast.makeText(Background_Location_Service.this, response_status, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(Background_Location_Service.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
};
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.
}
//requesting updates
fusedLocationProviderClient.requestLocationUpdates(locationRequest,
locationCallback,
Looper.getMainLooper());
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
return Service.START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
fusedLocationProviderClient.removeLocationUpdates(locationCallback);
super.onDestroy();
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Intent restartService = new Intent(getApplicationContext(), this.getClass());
PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartService, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME, 5000, pendingIntent);
}}
I learned, foreground service must be developed including notification to run a service continuously. With this approach can I achieve this? Or is there a better approach?
I read workmanager can be used so it will automatically deicide using jobscheduler or alarmmanager. Also it is able to cope with doze mode. So can I develop workmanager to get last known location periodically (E.g. every 5 minutes) instead of requesting location updates?
My problem is i can get the location in background but once app is killed it stopped updating.
i am storing the latitude and longitude into firebase once it's fetching from getlastlocation method. and foreground and background mode it's updating fine. but when i close like clearing all the background apps then it is stop updating.
public class MyBackgroundLocationService extends Service {
private static final String TAG = MyBackgroundLocationService.class.getSimpleName();
private FusedLocationProviderClient mLocationClient;
private LocationCallback mLocationCallback;
public MyBackgroundLocationService() {
}
#Override
public void onCreate() {
super.onCreate();
mLocationClient = LocationServices.getFusedLocationProviderClient(getApplicationContext());
mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
Log.d(TAG, "onLocationResult: location error");
return;
}
List<Location> locations = locationResult.getLocations();
LocationResultHelper helper = new LocationResultHelper(getApplicationContext(), locations);
helper.showNotification();
helper.saveLocationResults();
Toast.makeText(getApplicationContext(), "Location received: " + locations.size(), Toast.LENGTH_SHORT).show();
}
};
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: called");
startForeground(1001, getNotification());
getLocationUpdates();
return START_STICKY;
}
private Notification getNotification() {
NotificationCompat.Builder notificationBuilder = null;
notificationBuilder = new NotificationCompat.Builder(getApplicationContext(),
App.CHANNEL_ID)
.setContentTitle("Location Notification")
.setContentText("Location service is running in the background.")
.setSmallIcon(R.mipmap.ic_launcher)
.setAutoCancel(true);
return notificationBuilder.build();
}
private void getLocationUpdates() {
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(5000);
locationRequest.setFastestInterval(4000);
locationRequest.setMaxWaitTime(15 * 1000);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
stopSelf();
return;
}
mLocationClient.requestLocationUpdates(locationRequest, mLocationCallback, Looper.myLooper());
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: called");
stopForeground(true);
mLocationClient.removeLocationUpdates(mLocationCallback);
}
}
so i am using foreground service to update location in background and it will show in notification those updates. once app is killed notification also disappeared. please please please anyone help me.
if you need more clear about question i will explain. please someone understand my pain
I am developing offline car tracker android application. It will update location after 5 min and stores it in SQLite.I used FusedLocationAPI but can not get accurate location while travelling in bus without Internet. I am getting accuracy 999m and getting same location after every 5 minutes.
I set alarm manager to 5 minutes.
public static void startAlarmManager(Context context)
{
preferences =context.getSharedPreferences(Constant.SHARED_PREF_NAME, Context.MODE_PRIVATE);
int duration= Integer.parseInt(preferences.getString(Constant.DURATION_SHARED_PREF,Constant.CONSTANT_DURATION_SHARED_PREF));
Log.d("duration",duration+"");
alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
gpsTrackerIntent = new Intent(context, GpsTrackerAlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(context, 0, gpsTrackerIntent, 0);
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
5*60000,
pendingIntent);
}
It will fire broadcast receiver.
public class GpsTrackerAlarmReceiver extends WakefulBroadcastReceiver {
private static final String TAG = "GpsTrackerAlarmReceiver";
#Override
public void onReceive(Context context, Intent intent) {
LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE );
boolean statusOfGPS = manager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if(statusOfGPS) {
context.startService(new Intent(context, LocationService.class));
}
}
}
This is location service. I am getting location with this way.
public class LocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
private static final String TAG = "LocationService";
public static GoogleApiClient googleApiClient;
private LocationRequest locationRequest;
public Context context;
private DatabaseHelper db;
private boolean currentlyProcessingLocation = false;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
if (!currentlyProcessingLocation) {
currentlyProcessingLocation = true;
startTracking();
}
return START_NOT_STICKY;
}
private void startTracking() {
if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS) {
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
if (!googleApiClient.isConnected() || !googleApiClient.isConnecting()) {
googleApiClient.connect();
}
} else {
Log.e(TAG, "unable to connect to google play services.");
}
}
#Override
public void onConnected(#Nullable Bundle bundle) {
Log.d(TAG, "onConnected");
locationRequest = LocationRequest.create();
locationRequest.setInterval(1000); // milliseconds
locationRequest.setFastestInterval(1000); // the fastest rate in milliseconds at which your app can handle location updates
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(
googleApiClient, locationRequest, this);
}
#Override
public void onConnectionSuspended(int i) {
Log.e(TAG, "GoogleApiClient connection has been suspend");
}
#Override
public void onLocationChanged(Location location) {
Log.d(TAG, "onLocationChanged");
startupdate(location);
}
private void startupdate(Location location) {
if (location != null) {
db=new DatabaseHelper(this);
db.insertLocation(location.getLatitude(), location.getLongitude(), "FusedApi Provider", location.getAccuracy());
stopLocationUpdates();
stopSelf();
}
}
public void stopLocationUpdates() {
if (googleApiClient != null && googleApiClient.isConnected()) {
googleApiClient.disconnect();
}
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.e(TAG, "onConnectionFailed");
stopLocationUpdates();
stopSelf();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Please help...if i am wrong. Thanks in advance.
When GPS is not practical you can use LocationManager.NETWORK_PROVIDER, this is location from the phone carrier that is less accurate than GPS but is available everywhere the carrier has a tower. The way I do it is a set a flag isGpsAvailable() to see if that is true I use GPS otherwise I use network provided location. This Google's doc provides detailed solutions including code snippets that you can use and change the way you it fits your needs.
I started Service from main activity like;-
Intent intent = new Intent(this, MyLocationService.class);
startService(intent);
MyLocationService class looks like:-
public class MyLocationService extends Service implements
LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = MyLocationService.class.getSimpleName();
public static Location mCurrentLocation;
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
#Override
public void onCreate() {
Log.e(TAG, "onCreate: ");
initiateGooglePlayService();
}
public void initiateGooglePlayService() {
Log.e(TAG, "initiateGooglePlayService: ");
if (isGooglePlayServicesAvailable()) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(5000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(10.0f);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: ");
return null;
}
private boolean isGooglePlayServicesAvailable() {
Log.e(TAG, "isGooglePlayServicesAvailable: ");
int status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(getApplicationContext());
return ConnectionResult.SUCCESS == status;
}
#Override
public void onConnected(Bundle bundle) {
Log.e(TAG, "onConnected: ");
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
Log.e(TAG, "onConnectionSuspended: ");
}
protected void startLocationUpdates() {
Log.e(TAG, "startLocationUpdates: ");
try {
PendingResult<Status> pendingResult;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
pendingResult = LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
} catch (IllegalStateException ignored) {
}
}
#Override
public void onLocationChanged(Location location) {
Log.e(TAG, "onLocationChanged: " + location.getLongitude());
if (mGoogleApiClient.isConnected()) {
mCurrentLocation = location;
Intent intent = new Intent("GPSLocationUpdates");
intent.putExtra("location", location);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Toast.makeText(this, "location", Toast.LENGTH_LONG).show();
}
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.e(TAG, "onConnectionFailed: ");
}
}
I never stop the service anywhere.
But still I am unable to get the control of onLocationChange().
My motive is that, I need continuous location to do some background operation. It worked in lollipop sometimes. But it is not working in Marshmallow and nougat and even kitkat also. I searched but could not get the proper idea. Please let me know, where i am going wrong. Any suggestion accepted.
I am using following dependency for location;-
compile 'com.google.android.gms:play-services-location:9.6.1'
You can use the HyperTrack SDK to get the location updates in the background. Get more insight about the reliability of SDK here in different conditions.
First Setup the SDK
After setting up the SDK you just need to set the Callback to receive the location updates.
HyperTrack.setCallback(new HyperTrackEventCallback() {
#Override
public void onEvent ( #NonNull final HyperTrackEvent event){
switch (event.getEventType()) {
case HyperTrackEvent.EventType.LOCATION_CHANGED_EVENT:
Log.d(TAG, "onEvent: Location Changed");
HyperTrackLocation hyperTrackLocation = event.getLocation();
LatLng latLng = hyperTrackLocation.getLatLng();
updateCurrentLocationMarker(event.getLocation());
break;
}
}
}
(Disclaimer: I work at HyperTrack.)
/**Check out my Github post i did the same for Taxi clone**/
Link ---> https://github.com/yash786agg/GPS/
Note: Remember to remove or comment the below mentioned code if you want the latitude
and latitude even when the application is in background.
if(networkUtilObj != null)
{
networkUtilObj.disconnectGoogleApiClient();
}
I have following LocationService.java service class.
public class LocationService extends Service
implements LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final long INTERVAL = 1000 * 60;
private static final long FASTEST_INTERVAL = 1000 * 5;
Location mLastLocation;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
String lat, lon;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mGoogleApiClient.connect();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onConnected(Bundle bundle) {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
if (mLastLocation != null) {
lat = String.valueOf(mLastLocation.getLatitude());
lon = String.valueOf(mLastLocation.getLongitude());
Util.WriteSharePrefrence(getApplicationContext(), "track_lat", "" + lat);
Util.WriteSharePrefrence(getApplicationContext(), "track_lng", "" + lon);
}
}
#Override
public void onCreate() {
int permissionCheck = ContextCompat.checkSelfPermission(LocationService.this, Manifest.permission.ACCESS_FINE_LOCATION);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
//Execute location service call if user has explicitly granted ACCESS_FINE_LOCATION..
buildGoogleApiClient();
}
}
#Override
public void onConnectionSuspended(int i) {
}
synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
#Override
public void onLocationChanged(Location location) {
if (location != null) {
Util.WriteSharePrefrence(getApplicationContext(), "track_lat", "" + location.getLatitude());
Util.WriteSharePrefrence(getApplicationContext(), "track_lng", "" + location.getLongitude());
}
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
buildGoogleApiClient();
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
In the above location service what I am doing is.
1. Set fastest interval to refresh the location every 5 sec.
2. Once location availabe onLocationChanged() will be take place. Inside onLocationChanged() method I am writing current latitude and longitude in the shared preference.
3. This service will be running 24 hours in the background to get current location.
I have Mentioned both location condition in my manifest like below:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
My Issue is :
Some times onLocationChanged() method taking wrong latitude longitude
for example : user device in India and it taking USA address. The device version is Xolo - 4.2.1.(India)
Is it device specific issue? Or something I need to change in my code?
What should I do to make my location service better ?
Use Network or GPS providers or Passive if you want
public void Request_LocationUpdate(String type)
{
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(getActivity(),new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION}, 12);
}
else
{
LocationManager locationManager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
switch (type)
{
case "Passive":
locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,10000,10,locationListener);
break;
case "GPS":
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,10000,10,locationListener);
break;
case "Network":
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,10000,10,locationListener);
break;
}
}
}