I'm creating a service which should work when the activity is in background as well as when the whole application is destroyed.
I'm calling location coordinates in the service at every 1 minute interval.
However, when I try to do so, the service shuts down automatically just after 12-15 minutes.
I want the service to work endlessly until and unless it is destroyed by the completion of activity on user interaction.
My service class is as follows:
public class SensorService extends Service {
public int counter=0;
public static final int NINTY_SECONDS = 90000;
public static Boolean isRunning = false;
public LocationManager mLocationManager;
public Get_Coordinates mLocationListener;
public GetSharedPreference sharedPreference;
#Override
public void onCreate() {
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
mLocationListener = new Get_Coordinates(getApplicationContext());
sharedPreference = new GetSharedPreference(getApplicationContext());
startTimer();
super.onCreate();
}
public SensorService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public SensorService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i("EXIT", "ondestroy!");
Intent broadcastIntent = new Intent("com.android.startBgService");
broadcastIntent.putExtra("abc","abcd");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, NINTY_SECONDS, NINTY_SECONDS); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
// Log.i("in Etro Sendor", "in timer ++++ "+ (counter++));
if (Check_Internet_Con.isConnectingToInternet(getApplicationContext())) {
if (!isRunning) {
startListening();
}
try {
if (sharedPreference.getActiveUserId() > 0) {
mLocationListener.getLocation();
mLocationListener.insertCoordinatesInSqlite();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
public void startListening() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (mLocationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER))
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, mLocationListener, Looper.getMainLooper());
if (mLocationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER))
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener,Looper.getMainLooper());
}
isRunning = true;
}
}
Here is my manifest
<service
android:name="com.lunetta.etro.e_tro.SensorService"
android:enabled="true"></service>
<service
android:name="com.lunetta.etro.e_tro.SecondService"
android:enabled="true" >
</service>
<receiver
android:name=".SensorRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped">
<intent-filter>
<action android:name="com.android.startBgService" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
And Here is SensorRestarterBroadcastReceiver class
public class SensorRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(SensorRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! Oooooooooooooppppssssss!!!!");
context.startService(new Intent(context, SensorService.class));
}
}
https://developer.android.com/guide/components/bound-services.html
"A bound service typically lives only while it serves another application component and does not run in the background indefinitely."
To make it run indefinitely, you need to bind the service to a UI component that exists indefinitely. You can use an Android Notification. This is a foreground service.
https://developer.android.com/guide/components/services.html#Foreground
Yes it will stop working, check out the new Docs put up by Google, It has changed completely how location works for battery performance, and yes it having a big deal effect on performance i have changed my completely for the very specific task. Also stop using other repos that do claim that they work are wrong
Check the new updated docs by google
https://developer.android.com/training/building-location.html
The best way I implemented your same requirement is and is live in over about 60k devices and working flawlessly is With depending on Version of Android API using JobService with 23 and higher and lower with Background service. Have use LocationProvider API and Location provider Client API accordingly.
with personal experience i will say this The old style of code use to drain the device battery in few hours, now my code is hardly making a dent, its consuming only like 15 percent overnight overall usage. thats a big change in consumption.
Related
I have created an Enterprise mobility managament (EMM)app. When i launched it a few years ago my customer base probably had Android < 8 devices. I've recently had to update my app and the customer base are now using Android 11 devices.
The problem:
I have a BG service that constantly runs. It counts down from 500, then when it hits 0, it fires off a web service that updates states in the EMM portal. The portal is what the customers use to see their devices out in the field.
In the ondestroy method of my BG service, i send a broadcast that uses a receiver to relaunch the destroyed service, thus keeping it alive.
My customers need this service alive as it sends info like data usage and battery level info which could be safety critical in their industry.
What i have tried:
I understand that Android Doze puts app to sleep, i have tried using my solution to send a command to the device that whitelists it from Doze, this does not work.
I've tried using JobIntentService instead but this did not work either.
NB. my app is a device admin app, so does not need to be in the foregroud and actually shouldn't be. Device Admins are also exemp from Doze apparently.
The error:
Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=xxxxx/.ConnectivityService }: app is in background
What is the best way to keep this service running in the background on Android 11?
here is my code:
<service
android:name="xxx.ConnectivityService"
android:enabled="true"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
.
public class ConnectivityServiceRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e(ConnectivityServiceRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! ");
context.startService(new Intent(context, ConnectivityService.class));
//ConnectivityService.enqueueWork(context, new Intent());
}
}
.
public class ConnectivityService extends Service {
private static final String TAG = ConnectivityService.class.getSimpleName();
public int counter=0;
AppObj appobj;
public ConnectivityService(Context applicationContext) {
super();
Log.e(TAG, "inside ConnectivityService!");
}
public ConnectivityService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Log.e(TAG, "inside onStartCommand");
appobj = (AppObj)getApplication();
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "ondestroy!");
Intent broadcastIntent = new Intent("xxx.ActivityRecognition.RestartConnectivityservice");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
Log.e(TAG, "inside startTimer");
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
Log.e(TAG, "inside initializeTimerTask");
timerTask = new TimerTask() {
public void run() {
Log.e(TAG, "in timer ++++ "+ (counter++));
if(counter == 3600){
counter = 0;
appobj.webService.sendPulseToServer();
Intent myIntent = new Intent(getApplicationContext(), TrafficMonitorService.class);
myIntent.setAction("xxx.TrafficMonitorService");
getApplicationContext().startService(myIntent);
}
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
.
<receiver
android:name="xxx.ConnectivityServiceRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="ConnectivityServiceRestartServiceWhenStopped">
<intent-filter>
<action android:name="xxx.ActivityRecognition.RestartConnectivityservice"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
I have created a background service for registering SCREEN_ON and SCREEN_OFF intent to catch an event using broadcast receiver but some time due to unknown reason my service does not pass intent to my main application. For testing I have generated log file using ten second timer that it is my background service working or not, which I have attached below. I got a issue that my background service pause some time and start it automatically.
It is my background service
public class MyBackgroundService extends Service {
BroadcastReceiver receiver;
private static final String LOG_TAG = "MyBackgroundService";
#Override
public void onCreate() {
super.onCreate();
Timber.i("Foreground Service OnCreate");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Timber.i("Start Foreground Service");
receiver = new ScreenStateReceiver();
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
if (intent.getAction().equals(Constants.STARTFOREGROUND_ACTION)) {
Intent notificationIntent = new Intent(this, HomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("text")
.setTicker("text")
.setContentText("text")
.setSmallIcon(R.drawable.logo_icon)
.setContentIntent(pendingIntent)
.setOngoing(true).build();
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
}
startTimer();
return START_STICKY;
}
#Override
public boolean onUnbind(Intent intent) {
Timber.i("Foreground Service onUnbind");
return super.onUnbind(intent);
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Timber.i("Foreground Service onTaskRemoved");
super.onTaskRemoved(rootIntent);
}
#Override
public void onDestroy() {
stopForeground(true);
Timber.i("Foreground Service onDestroy");
stoptimertask();
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// Used only in case of bound services.
return null;
}
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 10000, 10000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Timber.i("Timer");
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
}
It is my broadcast Receiver
public class ScreenStateReceiver extends BroadcastReceiver {
public ScreenStateReceiver(){
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(Intent.ACTION_SCREEN_OFF)){
Timber.i("Screen OFF");
}
else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Timber.i("Screen ON");
}
}
}
Menifest,xml file declaration
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<service android:name=".service.MyBackgroundService"/>
<receiver android:name=".receiver.ScreenStateReceiver" android:enabled="true" android:exported="true">
<intent-filter >
<action android:name="android.intent.action.SCREEN_OFF" />
<action android:name="android.intent.action.SCREEN_ON" />
</intent-filter>
</receiver>
I am starting Service using intent
Intent service = new Intent(getContext().getApplicationContext(), MyBackgroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
service.setAction(Constants.STARTFOREGROUND_ACTION);
getContext().getApplicationContext().startForegroundService(service);
}
else{
service.setAction(Constants.MAIN_ACTION);
getContext().getApplicationContext().startService(service);
}
Here this picture of my log file
LOG FILE
using this log file you can see background service is stop and start automatically.
please help me resolve this issue.
There is a restriction for service in Oreo onward, We can start a service by calling startService() only when the application is in foreground. When you exit from the app (app is in background), then it has a window of several minute, in that app can run the service. At the end of window, app considered to be idle,that time system stop the background service. In order to achieve run a service even in background, either we have to implement foreground service or we have to use job scheduler etc.
How to run long running service in android in all devices greater than api level 16. I have written below code for the service but somehow not working for api 17 - 19. and its working for all devices above 19 api level. Please have a look and suggest me how to do it. I am new android and writing service for the first time.
below is code
public class MyService extends Service {
public int counter = 0;
public ViksitService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i("EXIT", "ondestroy!");
Intent broadcastIntent = new Intent("MyService_RESTART_SERVICE");
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
long oldTime = 0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Log.i("in timer", "in timer ++++ " + (counter++));
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
below is broadcast reciever :
public class ServiceRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(ServiceRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! Oooooooooooooppppssssss!!!!");
context.startService(new Intent(context, MyService.class));;
}
}
below is manifest :
<service
android:name=".services.MyService"
android:enabled="true"
android:exported="true">
</service>
<receiver
android:name=".services.ServiceRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="MyService_RESTART_SERVICE"/>
</intent-filter>
</receiver>
Run a service in background continuously. For example, a service has to be kicked off which will display a toast message 20 seconds once even if the app is closed.
public class AppService extends IntentService {
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
public AppService() {
super("AppService");
}
#Override
protected void onHandleIntent(Intent workIntent) {
Toast.makeText(getApplicationContext(), "hai", Toast.LENGTH_SHORT).show();
SystemClock.sleep(20000);
}
}
Below code works for me...
public class AppService extends Service {
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, " MyService Created ", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, " MyService Started", Toast.LENGTH_LONG).show();
return START_STICKY;
}
}
Accepted answer will not work on from Android 8.0 (API level 26), see the android's background limitations here
Modification in Accepted Answer:
1: You have to invoke the service's startForeground() method within 5 seconds after starting the service. To do this, you can call startForeground() in onCreate() method of service.
public class AppService extends Service {
....
#Override
public void onCreate() {
startForeground(9999, Notification())
}
....
}
2: You must call startForegroundService() instead of startService() by checking API level from where you want to start the service.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
This code work for me..
public class ServiceClass extends Service {
public static final int notify = 300000; //interval between two services(Here Service run every 5 Minute)
private Handler mHandler = new Handler(); //run on another Thread to avoid crash
private Timer mTimer = null; //timer handling
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
if (mTimer != null) // Cancel if already existed
mTimer.cancel();
else
mTimer = new Timer(); //recreate new
mTimer.scheduleAtFixedRate(new TimeDisplay(), 0, notify); //Schedule task
}
#Override
public void onDestroy() {
super.onDestroy();
mTimer.cancel(); //For Cancel Timer
Log.d("service is ","Destroyed");
}
//class TimeDisplay for handling task
class TimeDisplay extends TimerTask {
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable() {
#Override
public void run() {
Log.d("service is ","running");
}
});
}
}
}
In your manifest, where you declare your service, add:
android:process=":processname"
This lets the service run on a separate process and thus it will not be killed with the app.
You can then chose if you want to use foreground. It will show a persistent notification, but reduces the likelihood if the service being killed.
Further, if you want to create a continuously running service, use Service, NOT IntentService. IntentService stops when it is finished doing its action.
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