I'm developing an android application to track user location in background and keep updating it to firebase database as location changes the app is working fine when the application is opened but when i close it , firebase stop setting new values , it will set the new values directly after I open the application again
this is my android service code :
public class LocationServiceFB extends Service implements LocationListener,GoogleApiClient.ConnectionCallbacks,GoogleApiClient.OnConnectionFailedListener{
private Location mLastLocation;
DatabaseReference instructors;
GeoFire geofire;
String UserId;
SharedPreferences pref;
GoogleApiClient mGoogleApiClient;
LocationRequest mLocationRequest;
private static int UPDATE_INTERVAL = 5000;
private static int DISPLACEMENT = 10;
private static int FATEST_INTERVAL = 3000;
private LocationManager mLocationManager;
#Nullable
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not Yet Implemented");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
instructors = FirebaseDatabase.getInstance().getReference("OnlineInstructors");
geofire = new GeoFire(instructors);
pref = this.getSharedPreferences("LoginTrack", Context.MODE_PRIVATE);
UserId = pref.getString("firebasekey","");
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
instructors.child(UserId).removeValue();
}
#Override
public void onConnected(#Nullable Bundle bundle) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FATEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED){
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,mLocationRequest,this);
}
#Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
#Override
public void onLocationChanged(Location location) {
mLastLocation = location;
geofire.setLocation(UserId, new GeoLocation(location.getLatitude(), location.getLongitude()), new GeoFire.CompletionListener() {
#Override
public void onComplete(String key, DatabaseError error) {
Log.d("ERROR" , "INSTRUCTOR LOCATION SENT TO DATABASE" );
}
});
}
public void uploadLocation(){
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
Log.d("EER","np permissions");
return;
}
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
final double latitude = mLastLocation.getLatitude();
final double longitude = mLastLocation.getLongitude();
geofire.setLocation(UserId, new GeoLocation(latitude, longitude), new GeoFire.CompletionListener() {
#Override
public void onComplete(String key, DatabaseError error) {
Log.d("ERROR" , "INSTRUCTOR LOCATION SENT TO DATABASE" );
}
});
}
}
Android may kill your app's process in favor of more important apps. Android may also stop your process from networking when it's not longer visible in the foreground. This is to prevent poorly behaved apps from consuming too many resources.
If your apps needs to continue networking when the user is no longer using it, you'll have to start a foreground service, which also requires that you show a notification that indicates to the user that your app is still running.
You are using a bound service for upadating your position to the server.
Your service gets killed along with app.
Use an unbound service. Unbound service keeps running in the background even when the app is killed. So, this will work fine even in the background with unbound service.
Related
I am trying to get continuous location updates by running a background service. While I debug the code onConnected(Bundle b) is called and location update request is called. But onLocationChanged(Location location) is never called. Below is my code:
public class LocationUpdateService extends Service implements
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
private GoogleApiClient googleApiClient;
private LocationRequest mLocationRequest;
Location mCurrentLocation;
#Override
public void onLocationChanged(Location location) {
mCurrentLocation = location;
double lat = mCurrentLocation.getLatitude();
double lng = mCurrentLocation.getLongitude();
}
//GoogleApiClient
#Override
public void onConnectionFailed(ConnectionResult bundle) {
}
#Override
public void onConnected(Bundle bundle) {
Log.i("onConnected", "GoogleApiClient");
Toast.makeText(this, "Location service connected", Toast.LENGTH_SHORT).show();
createLocationRequest();
startLocationUpdate();
}
#Override
public void onConnectionSuspended(int i) {
}
//Service
#Override
public void onCreate() {
super.onCreate();
buildGoogleApiClient();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY; // run until explicitly stopped.
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void startLocationUpdate() {
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, mLocationRequest, this);
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
}
void buildGoogleApiClient() {
googleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
googleApiClient.connect();
}
void createLocationRequest() {
mLocationRequest = new LocationRequest().create()
.setInterval(5000)
.setFastestInterval(5000)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
}
I can't understand where I am making mistake, while I have followed android docs. I am testing on real device and app has location permissions.
service in menifest:
<service
android:name=".locations.LocationUpdateService"
android:enabled="true"
android:exported="false"></service>
Call of service in activity:
startService(new Intent(BaseActivity.this, LocationUpdateService.class));
What is your android phone SDK and how are you asking for the permissions on the manifest file?
If your phone SDK is 23 or higher the way you ask for permissions is different.
It was just a stupid debugging issue. I found my device location Access was off in advance settings.
i am making an android application. I want to stop location updates if the device is still and if it moves a distance of 300 or 400m then get it's location again. I don't want to check continuously for location updates and measure distance between previous location and current location because it consumes battery.
My Location service class code:
public class LocationService extends Service implements
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
}
#Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
public void buildGoogleApiClient() {
createLocationRequest();
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(500);
}
public void startLocationUpdates() {
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.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.
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
buildGoogleApiClient();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
// The service is no longer used and is being destroyed
stopLocationUpdates();
}
#Override
public void onLocationChanged(Location location) {
System.out.println("Latitude: " + location.getLatitude());
System.out.println("Longitude: " + location.getLongitude());
}
#Override
public void onConnected(#Nullable Bundle bundle) {
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
I am using locationRequest.setSmallestDisplacement(500) but it continuously receives location updates only the onLocationChanged method is not called if the distance between previous and current location is less than 500m e.g.
Should i use geofencing for this purpose?
What is the best possible way to achieve this using FusedLocationApi with minimum battery consumption?
There are a few things to note here:
When your device isn't moving, Location Services optimizes for battery and does not necessarily do expensive new location lookups. So, you don't really have to try to optimize for this yourself.
Consider passing a smaller value to LocationRequest#setSmallestDisplacement.
To optimize for battery, consider using batched location updates, where location is computed for you in at the interval described in LocationRequest#setInterval, but delivered to your device based on the value in LocationRequest#setMaxWaitTime. This greatly helps with battery.
Not part of your question, but I should note that I would structure the code for requesting and removing location updates a little differently. I would connect GoogleApiClient in onStart(), and disconnect it in onStop(). I would call requestLocationUpdates() in onResume() of in onConnected(), and call removeLocationUpdates() in onPause() or onStop(), but not as late as onDestroy().
So, I have a JobService that is being called on background. However I need it to get the current location and send it to the webservice. The webservice is working great when I send static data. However getting the location is being a problem. The location object is always null. This is what I've been trying.
public class GPSBackgroundJobService extends JobService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private String LOGSERVICE = "GPS";
private InspectorsLogic mInspectorsLogic;
ParsoContext mContext;
private GoogleApiClient mGoogleApiClient;
ParsoLocationListener mLocationListener;
Location mLastLocation;
Double longitude;
Double latitude;
#Override
public void onCreate() {
super.onCreate();
Log.i(LOGSERVICE, "onCreate");
mContext = (ParsoContext) getApplicationContext();
mInspectorsLogic = new InspectorsLogic(mContext);
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
}
#Override
public boolean onStartJob(JobParameters job) {
Log.i(LOGSERVICE, "onStartJob called");
mGoogleApiClient.connect();
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocation!=null) {
latitude = mLastLocation.getLatitude();
longitude =mLastLocation.getLongitude();
sendGPSTask mSendGPSTask = new sendGPSTask();
mSendGPSTask.execute(Double.toString(latitude), Double.toString(longitude));
}else{
Log.e(LOGSERVICE, "Lat and Long are null");
}
return false;
}
#Override
public boolean onStopJob(JobParameters job) {
mGoogleApiClient.disconnect();
return false;
}
#Override
public void onConnected(Bundle connectionHint) {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
}
getLastLocation returns null if it doesn't have a location fix yet- which means unless you're using GPS elsewhere, it almost always returns null. To make sure you get a location use getLocationUpdates and wait for the location to fix (this will not be instant, depending on atmospheric conditions, whether you're inside or out, etc this could take a few seconds to a minute or so- or just never happen).
Apparently the code is fine, to make it work this is what I did:
check the permissions of the app (the location one needs to be activated)
change your GPS settings to (mobile data and wifi one)
making this with the current code posted on the question, made it.
I have a LocationService that gets the user location in the background and sends a broadcast to an activity with the Latitude and Longitude.
It is the code found in the accepted answer to this question Background service with location listener in android
I created the project with the Google Maps Activity provided by Android Studio. In the MapsActivity I get the broadcast extras like this
public class newMessage extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equalsIgnoreCase(LocationService.BROADCAST_ACTION)) {
Bundle extra = intent.getExtras();
latitude = extra.getDouble("Latitude");
longitude = extra.getDouble("Longitude");
System.out.println("Latitude: "+latitude);
System.out.println("Longitude: "+longitude);
LatLng newLocation = new LatLng(latitude,longitude);
}
}
}
I now want to update the map with the new location but I have no idea how to do this. Is it possible with my current setup?
declare Broadcast receiver in activity and show marker to current location
public BroadcastReceiver locationUpdateReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
//show current location marker
mMap.addMarker(new MarkerOptions().position(/*your lat long*/).title("My Location")));
}
};
You need to create a locationService where you can obtain your current location. Check this example:
public class LocationService extends Service implements
LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = LocationService.class.getSimpleName();
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;
protected Location mCurrentLocation;
#Override
public void onCreate() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
buildGoogleApiClient();
mGoogleApiClient.connect();
return START_NOT_STICKY;
}
#Override
public void onConnected(Bundle bundle) {
Log.i("fixedrec", TAG + ">Connected to GoogleApiClient");
if (mCurrentLocation == null) {
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
}
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onDestroy() {
super.onDestroy();
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
Log.d("fixedrec", TAG+ ">StoppingService");
mNM.cancel(NOTIFICATION);
stopForeground(true);
}
#Override
public void onLocationChanged(Location location) {
//plase where you get your locations
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d("fixedrec", TAG + "> Connection failed: ConnectionResult.getErrorCode() = "
+ connectionResult.getErrorCode());
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
protected void startLocationUpdates() {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
Log.i("fixedrec", TAG + "> StartLocationUpdates");
}
protected synchronized void buildGoogleApiClient() {
Log.i("fixedrec", TAG + "> Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
}
To broadcast you location across application you can use BroadCastReceiver or EventBus like Otto.
Then just create a googleMap and add a marker with obtained location to it.
Don't forget yo write locationPermissions inside your manifest file and inside your code if you are dealing with SDK >=23
Also you can study this project. Fixedrec3
Everything you need is there.
I am trying to create a route tracking app. it need to track location even if the app is in background. so i created a service and add code to this service. Following are my code. but there is one problem. I start the service from my main activity.
public void startTracking(View view) {
startService(new Intent(MainActivity.this, LocationIntentService.class));
}
public void stopTracking(View view) {
stopService(new Intent(MainActivity.this, LocationIntentService.class));
}
It start the service and locations are inserted to a local db. But i cant stop these service. When i stop service using above code it still track the location. How can i stop location update.
public class LocationIntentService extends IntentService implements LocationListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = LocationIntentService.class.getSimpleName();
private static final long INTERVAL = 1000 * 10;
private static final long FASTEST_INTERVAL = 1000 * 5;
private static int DISPLACEMENT = 10;
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
DBAdapter dbAdapter;
public LocationIntentService() {
super("LocationIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.e(TAG, " ***** Service on handled");
if (isGooglePlayServicesAvailable()) {
createLocationRequest();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
}
#Override
public void onConnected(Bundle bundle) {
Log.e(TAG, " ***** Service on connected");
startLocationUpdates();
openDB();
}
#Override
public void onConnectionSuspended(int i) {
Log.e(TAG, " ***** Service on suspended");
mGoogleApiClient.connect();
}
#Override
public void onLocationChanged(Location location) {
Log.e(TAG, "Location changed");
mLastLocation = location;
String latitude = String.valueOf(mLastLocation.getLatitude());
String longitude = String.valueOf(mLastLocation.getLongitude());
Log.e(TAG, " ##### Got new location"+ latitude+ longitude);
Time today = new Time(Time.getCurrentTimezone());
today.setToNow();
String timestamp = today.format("%Y-%m-%d %H:%M:%S");
dbAdapter.insertRow(latitude, longitude, timestamp);
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.e(TAG, "Connection failed: ConnectionResult.getErrorCode() = "
+ connectionResult.getErrorCode());
}
#Override
public void onDestroy() {
Log.e(TAG, "Service is Destroying...");
super.onDestroy();
if (mGoogleApiClient.isConnected()) {
stopLocationUpdates();
mGoogleApiClient.disconnect();
}
closeDB();
}
protected void stopLocationUpdates() {
Log.d(TAG, "Location update stoping...");
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
}
protected void startLocationUpdates() {
Log.d(TAG, "Location update starting...");
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
private void openDB() {
dbAdapter = new DBAdapter(this);
dbAdapter.open();
}
private void closeDB() {
dbAdapter = new DBAdapter(this);
dbAdapter.close();
}
protected void createLocationRequest() {
Log.e(TAG, " ***** Creating location request");
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
}
private boolean isGooglePlayServicesAvailable() {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == status) {
return true;
} else {
Log.e(TAG, " ***** Update google play service ");
return false;
}
}
}
The reason that it's not working for you is that you are using an IntentService, so calling stopService() will not cause onDestroy() to be called, presumably because it was already called after onHandleIntent() has completed. There is no need to ever call stopService() on an IntentService see here.
It looks like you should probably just use Service instead of IntentService. That way, when you call stopService(), it would call onDestroy() and unregister for location updates, as you expect.
The only other change you would need to make would be to override onStartCommand() instead of onHandleIntent().
You would have your class extend Service instead of IntentService, and then move your code to register for location updates to onStartCommand:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, " ***** Service on start command");
if (isGooglePlayServicesAvailable()) {
createLocationRequest();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
return Service.START_STICKY;
}
This way you can still call startService() and stopService(), and it should work as you are expecting.
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
call stopLocationUpdates() method in stopService()
When you stop your services. Then called this line in LocationIntentService.class.
locationManager.removeUpdates(this);