Android - Saving location updates (using LocationServices API) from a Service - android

I have set up an environment where the app receives location updates, which is handle on the onLocationChanged callback.
// Setup the client.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
// Register the location update.
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
// Interface callback. Called every 5 seconds.
#Override
public void onLocationChanged(Location location) {
// Save the location coordinates to a file.
}
So far so good. Then, for my purposes, I saw the need of triggering the onLocationChanged callback even if the app is not running - that's where BroadcastReceivers and Services come in.
I want a BroadcastReceiver to start a Service, that would save the location coordinates updates do a file. So, in my mind, the architecture would go something like:
// Register the BroadcasReceiver to the activity.
registerReceiver(mBroadcastReceiver, new IntentFilter());
// The BroadcastReceiver
public static class MyBroadcastReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
MyActivity.myContext.startService(new Intent(context, MyService.class));
}
}
// The Service class.
public static class MyService extends Service {
private boolean isRunning = false;
#Override
public void onCreate() {
isRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Creating new thread for my service.
//Always write your long running tasks in a separate thread, to avoid ANR
new Thread(new Runnable() {
#Override
public void run() {
// Save location updates.
}
//Stop service once it finishes its task
stopSelf();
}
}).start();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onDestroy() {
isRunning = false;
}
}
All LocationServices API setup process (the first block of code below) is inside the activity onCreate method.
So, how can I receive location updates from the tread's run() method created by the Service, if the app is not running? The whole design is to be like that:
App not running/destroyed > A specific action trigger the Broadcasreceiver > The BroadcastReceiver trigger the Service > The Service trigger the location updates and save it to a file.

Related

Android: Service is killed and restarted after a while

I know that is a well known subject, but I have tried lot of things. I have an simple application, dedicated to a specific user, application has an mainActivity which is displaying some status on screen and it's starting two services, one is making request from a server (at every 5 minutes) and one which is sending sms and replay to server (at every ten minutes).
The application is running on a Samsung pocket 2 with Android 4.4.2, this device is used only for this application. While the device is connected to ADB the services are working just fine, but if I disconnect the phone and let it running normally, the services are killed repeatable and restarted after a while. The messaged are send with very much delay. I would be thankful for any suggestions.
Here is my code:
Main activity:
public class MainActivity extends Activity {
private TextView _internet;
private TextView _signal;
private TextView _server;
private BroadcastReceiver receiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
IntentFilter intentFilter = new IntentFilter(Constants.SS);
receiverWorker();
registerReceiver(receiver, intentFilter);
startService(new Intent(this, RefreshDBService.class));
startService(new Intent(this, SmsService.class));
}
private void receiverWorker() {
receiver = new BroadcastReceiver() {
public void onReceive(Context arg0, Intent arg1) {
checkState();
}};
}
public void refreshButonClicked(View v) {
checkState();
}`
Here is my first service:
public class RefreshDBService extends Service {
private Thread _backgroundWork;
private ScheduledExecutorService scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
private DataBaseOperations _dataSource;
#Override
public void onCreate() {
super.onCreate();
_dataSource = new DataBaseOperations(this);
_backgroundWork = new Thread(new Runnable() {
#Override
public void run() {
if(Checks.checkInternetConnection(getApplicationContext())){
if(ServerOperations.isServerAvailable(getApplicationContext())){
String inputData = ServerOperations.makeRequest(Constants.GET_DATA_ROUTE, ServerOperations.getMessagesFromServer(getApplicationContext()));
ArrayList<DataSmsObj> dataFromServer=null;
if(inputData!=null && !inputData.isEmpty()){
dataFromServer = ServerOperations.fromJsonToObjects(inputData);
if(dataFromServer.size()>0){
_dataSource.open();
_dataSource.insertDataFromServer(dataFromServer);
_dataSource.close();
}
}
System.out.println("check server for messages in pending status, received -> "+ dataFromServer.size());
}else{
System.out.println("no server");
sentErrorToUI(Constants.NO_SERVER);
}
}else{
System.out.println("no internet");
sentErrorToUI(Constants.NO_INTERNET);
}
}
});
}
public int onStartCommand(Intent intent, int flags, int startId) {
scheduleTaskExecutor.scheduleWithFixedDelay(_backgroundWork, 0, Constants.NEXT_CYCLE/2, TimeUnit.MINUTES);
return START_REDELIVER_INTENT;
}
#Override
public void onDestroy() {
super.onDestroy();
scheduleTaskExecutor.shutdownNow();
}
public IBinder onBind(Intent intent) {
return null;
}
private void sentErrorToUI(String message){
Intent intent = new Intent(Constants.SS);
intent.putExtra(Constants.SS, message);
System.out.println("trimit" +message);
sendBroadcast(intent);
}
}
And this is the second one:
public class SmsService extends Service {
private Thread _backgroundWork;
private ScheduledExecutorService scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
private DataBaseOperations _dataSource;
#Override
public void onCreate() {
super.onCreate();
_dataSource = new DataBaseOperations(this);
_backgroundWork = new Thread(new Runnable() {
#Override
public void run() {
sendFeedbackToServer();
List<DataSmsObj> dataToSent = new ArrayList<DataSmsObj>();
_dataSource.open();
dataToSent = _dataSource.getDataToSent();
_dataSource.close();
System.out.println("messages to sent: "+ dataToSent.size());
for (int i = 0; i < dataToSent.size(); i++) {
//here the messages are send, the code is to long to put it here, but if is need i can do it afterwards
}
}
});
}
public int onStartCommand(Intent intent, int flags, int startId) {
scheduleTaskExecutor.scheduleWithFixedDelay(_backgroundWork, 0, Constants.NEXT_CYCLE, TimeUnit.MINUTES);
return START_REDELIVER_INTENT;
}
#Override
public void onDestroy() {
super.onDestroy();
scheduleTaskExecutor.shutdownNow();
public IBinder onBind(Intent intent) {
return null;
}
If you are using a background Service with a scheduled task, it could be killed by the system. The only way to prevent the killing is a foreground Service. Quoting the documentation:
A foreground service is a service that the user is actively aware of and is not a candidate for the system to kill when low on memory.
You have to call the method startForeground() inside your Service using a Notification to show it. For further information you can check: https://developer.android.com/guide/components/services.html#Foreground
By the way, I recommend you to use the new JobScheduler api above api 21.
https://developer.android.com/reference/android/app/job/JobScheduler.html
Android kills service based on priority stack.
Android: keeping a background service alive (preventing process death)
What is START_STICKY,START_NOT_STICKY and START_REDELIVER_INTENT Service
Above links might help you.
Your devices will sleeps if it is unplugged from computer . So, the solutions :
Use startForeground method to prevent service to be killed and/or use AlarmManager in order to charge event.
It is possible to use start_stiky flag but it just restarts the process if it killed by system.

Android repeated Service - onCreate called once, onStartCommand called many

I followed the basic android documentation to implement a Service, triggered repeatedly by AlarmManager every 40 seconds. Inside the service I register GPS listener, and if I don't get fix within 30 seconds I call stopSelf(), this in order to avoid 2 "concurrent" services running together. However if I do have fix within less then 30 seconds, I perform some logic and after I done I call stopSelf() - Assuming it all will take less then 40 seconds so again I have no issues of "concurrent" services running...
When I log print the order of execution of various Service methods it doesn't make any sense:
onCreate is called only once, while onStartCommand is triggered every 40 seconds.
The GPS is never fixed, maybe the fact that the hosting Activity also registered and do have GPS fix interfere here? (I testing outdoors and the activity does get fix)
This is my implementation - Pretty much straightforward googles android documentation:
public class DirectionService extends Service implements Constants {
private LocationManager mLocationManager;
private LocationListener mLocationListeners;
private Context mContext;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
#Override
public IBinder onBind(Intent arg0) {
return null; //not binding
}
#Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
mContext = getApplicationContext();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//For each start request, send a message to start a job and deliver the start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
return START_STICKY;
}
//Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
/**
* The real work done after we have (first) fixed location and from there we stop the service.
* Therefore we pass the start id.
*/
#Override
public void handleMessage(final Message msg) {
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
mLocationListeners = new LocationListener(msg.arg1);
}
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, GPS_UPDATE_TIME, 0, mLocationListeners);
mLocationManager.addGpsStatusListener(mGPSStatusListener);
} catch (Exception e) {
stopSelf(msg.arg1);
}
//Start timer for GPS to get fix location. Else we might have new concurrent instance of service
new CountDownTimer(30000, 15000) {
public void onTick(long millisUntilFinished) {}
public void onFinish() {
stopSelf(msg.arg1);
}
}.start();
}
}
GpsStatus.Listener mGPSStatusListener = new GpsStatus.Listener() {
public void onGpsStatusChanged(int event) {
switch (event)
{
case GpsStatus.GPS_EVENT_FIRST_FIX:
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) != null) {
isGpsFixed = true;
}
}
break;
default:
break;
}
}
};
private class LocationListener implements android.location.LocationListener {
private int startId;
public LocationListener(int startId) {
this.startId = startId;
}
#Override
public void onLocationChanged(Location location) {
if (isGpsFixed == true && location.getLongitude() != 0.0 && location.getLatitude() != 0.0 && isAlreadySentToCheck == false) {
isAlreadySentToCheck = true;
startLogic(startId);
}
}
#Override
public void onProviderDisabled(String provider) {}
#Override
public void onProviderEnabled(String provider) {}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
}
private void startLogic(final int startId) {
//...
stopSelf(startId);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mLocationManager != null) {
try {
mLocationManager.removeUpdates(mLocationListeners);
} catch (Exception ex) {}
}
}
your service running many time because of start_sticky
if your service is killed by Android due to low memory, and Android clears some memory, then...
STICKY: ...Android will restart your service, because that particular flag is set.
NOT_STICKY: ...Android will not care about starting again, because the flag tells Android it shouldn't bother.
REDELIVER_INTENT: ...Android will restart the service AND redeliver the same intent to onStartCommand() of the service, because, again, of the flag.
suggest to your start_not_sticky

android run application always even if application in background

I want to make the following:
1]- listen to the change of the GPS location of the phone and send it to server to track user location continuously
2]- I have found an example to find the GPS location using LocationListener
3]- I have found a way to open my application when device restart
I need some help to be able to send this data even if user put the application in background
Any help here?
this service should work in the background
The LocationClient is the main entry point for location related APIs, such as location and geofence.
Use the LocationClient to:
Connect and disconnect to Google Location Services.
Request/remove location update callbacks.
Request/remove geofences.
In order to establish a connection, call connect() and wait for the onConnected(android.os.Bundle) callback.
LocationRequest objects are used to request a quality of service for location updates from the LocationClient.
in LocationRequest, you can set parameters there such as the accuracy of the location and time interval between location updates.
onLocationChanged will get called according to time interval you set in LocationRequest and from there you can update your server.
the service does not run in the background so you will need to update the server with AsyncTask or some other way, just make sure the server updates are done on a background thread.
public class LocationUpdatesService extends Service implements GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
private static int LOCATION_UPDATE_INTERVAL = 30000; // how often you will get a location update (this is in milliseconds)
private LocationClient locationClient;
private LocationRequest locationRequest;
private boolean isConnected = false;
#Override
// onCreate is called when the service gets started (from an Activity) than immediately calls onStartCommand
public void onCreate() {
super.onCreate();
if (servicesConnected()) {
startLocationUpdates();
} else {
// isGooglePlayServicesAvailable FAILURE
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private boolean servicesConnected() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == resultCode) {
return true;
} else {
return false;
}
}
public void startLocationUpdates() {
locationRequest = LocationRequest.create();
locationRequest.setInterval(LOCATION_UPDATE_INTERVAL);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setFastestInterval(LOCATION_UPDATE_INTERVAL);
locationClient = new LocationClient(this, this, this);
locationClient.connect();
isConnected = true;
}
#Override
public void onDestroy() {
if (locationClient.isConnected()) {
onDisconnectClient();
} else {
// locationClient is disconnected
}
super.onDestroy();
}
private void onDisconnectClient() {
isConnected = false;
locationClient.removeLocationUpdates(this);
locationClient.disconnect();
locationRequest = null;
locationClient = null;
}
#Override
public void onLocationChanged(Location location) {
// update server from here with AsyncTask (or some other way but in the background)
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
#Override
public void onConnected(Bundle bundle) {
locationClient.requestLocationUpdates(locationRequest, this);
}
#Override
public void onDisconnected() {
}
}
helpful links:
http://developer.android.com/reference/android/app/Service.html
http://developer.android.com/guide/components/services.html
http://developer.android.com/reference/android/location/LocationListener.html
https://developer.android.com/reference/com/google/android/gms/location/LocationRequest.html
https://developer.android.com/reference/com/google/android/gms/location/LocationListener.html

service, that listens for Location Service

So, I was asked to make a Location Tracker.
Location tracker should track even if the app is tuned off...
My idea is to start my own service (lets call it TrackingService) from the activity by calling startService(intent); so the service will run forever (I guess..) and then connect to Location Service from my own created TrackingService. TrackingService should listen to location changes after app was turned off.
I write some code, started TrackingService, and requested location updates in a new thread.
Anyway, location updates stops after I quit app but service is still running.
EDIT:
Ok, so i manage to improve my code a bit, so now when my app is running, i get Log's that my thread (that runs in separate service) is running and that it receives Location Updates.
When i quit y app I still get Log that my thread is running but it does not receives Location Updates...
Anyone can point my a reason why?
P.S. I know that probably there are better ways to get the job done, but I really hoping to fix my code.
Here goes service class
public class TrackingService extends Service {
// DEBUG
public final static String TAG = "TrackingService";
public final static boolean D = true;
// Global constants
private static final long UPDATE_INTERVAL = 10000; // Update frequency in milliseconds
private static final long FASTEST_INTERVAL = 4000; // A fast frequency ceiling in milliseconds
//
int mStartMode; // indicates how to behave if the service is killed
private final IBinder mBinder = new LocalBinder(); // interface for clients that bind
boolean mAllowRebind; // indicates whether onRebind should be used
private int number; // testavimui
LocationThread mLocationThread;
#Override
public void onCreate() {
if (D) {Log.d(TAG, "service - onCreated started");};
mLocationThread = new LocationThread(this);
mLocationThread.start();
// mLocationThread.run();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (D) {Log.d(TAG, "service - onStartCommand started");};
// The service is starting, due to a call to startService()
return mStartMode;
}
#Override
public IBinder onBind(Intent intent) {
if (D) {Log.d(TAG, "service - onBind started");};
// A client is binding to the service with bindService()
return mBinder;
}
#Override
public boolean onUnbind(Intent intent) {
if (D) {Log.d(TAG, "service - onUnBind started");};
// All clients have unbound with unbindService()
return mAllowRebind;
}
#Override
public void onRebind(Intent intent) {
if (D) {Log.d(TAG, "service - onReBind started");};
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
#Override
public void onDestroy() {
if (D) {Log.d(TAG, "service - onDestroy started");};
// The service is no longer used and is being destroyed
mLocationThread.cancel();
}
public class LocalBinder extends Binder {
TrackingService getService() {
// Return this instance of LocalService so clients can call public methods
return TrackingService.this;
}
}
public int number(){
number += 1;
return number;
}
private class LocationThread extends Thread implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener{
private boolean keepOn;
private Context mContext;
private LocationClient mLocationClient;
private LocationRequest mLocationRequest;
public LocationThread (Context context){
mContext = context;
keepOn = true;
}
public void cancel() {
keepOn = false;
if (D){Log.d(TAG, "thread was canceled");};
}
public void run(){
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // Use high accuracy
mLocationRequest.setInterval(UPDATE_INTERVAL); // Set the update interval to 5 seconds
mLocationRequest.setFastestInterval(FASTEST_INTERVAL); // Set the fastest update interval to 1 second
mLocationClient = new LocationClient(mContext, this, this);
mLocationClient.connect();
while (keepOn){
try {
Thread.sleep(10000);
if(D){Log.d(TAG, "thread running");};
} catch (Exception e){
}
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
if(D){Log.d(TAG, "connection failed");};
}
#Override
public void onConnected(Bundle connectionHint) {
if(D){Log.d(TAG, "connected to location service");};
mLocationClient.requestLocationUpdates(mLocationRequest, this);
}
#Override
public void onDisconnected() {
if(D){Log.d(TAG, "disconnected from location service");};
}
#Override
public void onLocationChanged(Location location) {
if(D){Log.d(TAG, "Location changed");};
}
}
}
Although the documentation is not specific about it, I suggest you perform the call to LocationManager.requestLocationUpdates() in the main (UI) thread. You can use a Handler to accomplish that if the call originates from a separate thread.
BTW, if you want your service to run in a separate thread, I suggest you extend IntentService and override the onHandleIntent() method, it is easier that way.
Further advice:
If you want your Service to run even when the phone is in sleep mode, you need a wake lock.
Hint: You don't have to make it run continuosly, that will consume a lot of battery needlessly. Make your Servicecollect a single location, save it to a local database or deliver it to a bound activity and then stop, and then schedule that Service to run from time to time using AlarmManager.
It goes like this: AlarmManager calls a WakefulBroadcastReceiver, which by its turn calls your Service.
I suggest you read the WakefulBroadcastReceiver documentation, it will provide a wake lock for your service automatically (which you have to manually release before the service stops).

Should I create service for GPS tracking APP?

I am writing a location service App that log where the user has been every minute.
Should I create a service for the GPS process? OR just create the LocationManager at the Activity? Which one is better?
Moreover, I have tried to hide the application by pressing hardware home button and turn off GPS at Setting -> Location. I found that the App closed automatically within an hour.
Is it possible to keep the application always alive?
I highly recommend creating the gps at the very least as a thread in the activity, if you want to be slick set it up as a service and broadcast intents from inside an asynctask. Setting it up as a service makes it a bit modular if you want to use it for other applications or in other activities. Thats the way I implemented it.
Its also easier to control the lifetime of your gps readings if you run it from a service instead of your activity, so service doesnt get interrupted if you do switch activities etc.. example of asynctask portion below:
/** Begin async task section ----------------------------------------------------------------------------------------------------*/
private class PollTask extends AsyncTask<Void, Void, Void> { //AsyncTask that listens for locationupdates then broadcasts via "LOCATION_UPDATE"
// Classwide variables
private boolean trueVal = true;
Location locationVal;
//Setup locationListener
LocationListener locationListener = new LocationListener(){ //overridden abstract class LocationListener
#Override
public void onLocationChanged(Location location) {
handleLocationUpdate(location);
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status,
Bundle extras) {
}
};
/** Overriden methods */
#Override
protected Void doInBackground(Void... params) {
//This is where the magic happens, load your stuff into here
while(!isCancelled()){ // trueVal Thread will run until you tell it to stop by changing trueVal to 0 by calling method cancelVal(); Will also remove locationListeners from locationManager
Log.i("service","made it to do in background");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
#Override
protected void onCancelled(){
super.onCancelled();
stopSelf();
}
#Override
protected void onPreExecute(){ // Performed prior to execution, setup location manager
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
if(gpsProvider==true){
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
}
if(networkProvider==true){
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
}
}
#Override
protected void onPostExecute(Void result) { //Performed after execution, stopSelf() kills the thread
stopSelf();
}
#Override
protected void onProgressUpdate(Void... v){ //called when publishProgress() is invoked within asynctask
//On main ui thread, perform desired updates, potentially broadcast the service use notificationmanager
/** NEED TO BROADCAST INTENT VIA sendBroadCast(intent); */
Intent intent = new Intent(LOCATION_UPDATE);
//Put extras here if desired
intent.putExtra(ACCURACY, locationVal.getAccuracy()); // float double double long int
intent.putExtra(LATITUDE, locationVal.getLatitude());
intent.putExtra(LONGITUDE, locationVal.getLongitude());
intent.putExtra(TIMESTAMP, locationVal.getTime());
intent.putExtra(ALTITUDE,locationVal.getAltitude());
intent.putExtra(NUM_SATELLITES,0);/////////////****TEMP
sendBroadcast(intent); //broadcasting update. need to create a broadcast receiver and subscribe to LOCATION_UPDATE
Log.i("service","made it through onprogress update");
}
/** Custom methods */
private void cancelVal(){ //Called from activity by stopService(intent) --(which calls in service)--> onDestroy() --(which calls in asynctask)--> cancelVal()
trueVal = false;
locationManager.removeUpdates(locationListener);
}
private void handleLocationUpdate(Location location){ // Called by locationListener override.
locationVal = location;
publishProgress();
}
}

Categories

Resources