Service of an app stops when phone is not being charged - android

My Activity starts a service by calling startservice(). To simplify my problem lets say the service will be a counter, and the counter will be increased in every 10 sec.
Timer t_counter;
int counter = 0;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
t_counter = new Timer();
t_counter.schedule(new TimerTask() {
#Override
public void run() {
counter++;
Log.d("counter: ",Integer.toString(counter));
}}, 0, 10000);
return Service.START_STICKY;
}
When the phone is being charged, (or in debug mode - since I can see the the Logcat) the service works as expected. In around every 10 sec Logcat shows the debug info, whenever the app is in background or not. But when I have unplugged the phone, the service stops running after a while. Event when the app (Activity which started the service) is active. Note that service not destroyed, just put on hold, or something like this.
Because when I plug in the mobile again, the timer continues and the value of the counter is being increased from the value where I just unplugged the phone. So if the service has been destroyed then value would have been zero again. (also I debugging the lifecycle of the service, and cannot see onStartCOmmand(), onDestroy() would have been called )
I have searched solutions for it, but I think I have not het the right answer for this behavior.
I know that I should use AlarmManager instead of Timer. Or it would also work if I put the service foreground by startForeground(), or maybe separate process would solve this problem. But I would like to know why my solution is working with charging. Also where can I find infos about this "idle" state of a service. (not executing timer schedules, but not destroyed) Thanks!

You need to hold lock if your service has to be running in the background
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl =
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
// when you done
wl.release();
Better way is to use AlarmManager because keep the service running all the time will drain the battery and waste the system resources.

Related

How can I make my app running also in Doze mode without Partial_Wakelock? [duplicate]

One of my peer developer has written an intent service that makes an API call and then sleeps for 2 mins. After waking up, it sends again.
Below is the code:
public class GpsTrackingService extends IntentService {
....
#Override
protected void onHandleIntent(Intent intent) {
do{
try{
//make API call here
//then go to sleep for 2 mins
TimeUnit.SECONDS.sleep(120);
} catch(InterruptedException ex){
ex.printStackTrace();
}
} while (preferences.shouldSendGps()); //till the user can send gps.
}
....
}
Manifest
<service android:name=".commons.GpsTrackingService" />
This is working fine when the phone is active. However, whenever the phone goes into doze mode it fails to wake.
Will using alarm manager with WAKE permission solve this?
I have just got the code base and need to fix this within today. It'll be great if someone can help.
As the documentation says:
In Doze mode, the system attempts to conserve battery by restricting
apps' access to network and CPU-intensive services. It also prevents
apps from accessing the network and defers their jobs, syncs, and
standard alarms.
Periodically, the system exits Doze for a brief time to let apps
complete their deferred activities. During this maintenance window,
the system runs all pending syncs, jobs, and alarms, and lets apps
access the network.
In few words, while in Doze mode the system suspends network accesses, ignores Wake Locks, stops acquiring data from sensors, defers AlarmManager jobs to the next Doze maintenance window (which are progressively less frequently called), also WiFi scans, JobScheduler jobs and Sync adapters do not run.
Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 (?) minutes, per app.
And it seems that the Foreground Services are also involved into this "Doze Drama", at least in MarshMellow (M).
To survive in this situation, tons of applications need to be at least rewiewed. Can you imagine a simple mp3 player which stops playing music when the device enters in Doze Mode?
Doze mode starts automatically, when the device is unplugged from the power supply and left on the table for about 1 hour or so, or even earlier when the user clicks the power button to power down the screen, but I think this could depend by the device manufacturer too.
I tried a lot of countermeasures, some of them really hilarious.
At the end of my tests I reached a possible solution:
One possible (and maybe the only) way to have your app running even when the host device is in Doze mode, is basically to have a ForegroundService (even a fake one, doing no jobs at all) running in another process with an acquired partial WakeLock.
What you need to do is basically the following (you could create a simple project to test it):
1 - In your new project, create a new class which extends Application (myApp), or use the
main activity of the new project.
2 - In myApp onCreate() start a Service (myAntiDozeService)
3 - In myAntiDozeService onStartCommand(), create the Notification
needed to start the service as a foreground service, start the
service with startForeground(id, notification) and acquire the
partial WakeLock.
REMEMBER! This will work, but it is just a starting point, because you have to be careful with the "Side Effects" this approach will generate:
1 - Battery drain: The CPU will work for your app forever if you
don't use some strategy and leave the WakeLock always active.
2 - One notification will be always shown, even in the lockscreen,
and this notification cannot be removed by simply swiping it out, it
will be always there until you'll stop the foreground service.
OK, let's do it.
myApp.java
public class myApp extends Application {
private static final String STARTFOREGROUND_ACTION = "STARTFOREGROUND_ACTION";
private static final String STOPFOREGROUND_ACTION = "STOPFOREGROUND_ACTION";
#Override
public void onCreate() {
super.onCreate();
// start foreground service
startForeService();
}
private void stopForeService() {
Intent service = new Intent(this, myAntiDozeService.class);
service.setAction(STOPFOREGROUND_ACTION);
stopService(service);
}
private void startForeService(){
Intent service = new Intent(this, myAntiDozeService.class);
service.setAction(STARTFOREGROUND_ACTION);
startService(service);
}
#Override
public void onTerminate() {
stopForeService();
super.onTerminate();
}
}
myAntiDozeService.java
public class myAntiDozeService extends Service {
private static final String TAG = myAntiDozeService.class.getName();
private static boolean is_service_running = false;
private Context mContext;
private PowerManager.WakeLock mWakeLock;
private static final int NOTIFICATION_ID = 12345678;
private static final String STARTFOREGROUND_ACTION = "STARTFOREGROUND_ACTION";
private static final String STOPFOREGROUND_ACTION = "STOPFOREGROUND_ACTION";
#Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!is_service_running && STARTFOREGROUND_ACTION.equals(intent.getAction())) {
Log.i(TAG, "Received Start Foreground Intent ");
showNotification();
is_service_running = true;
acquireWakeLock();
} else if (is_service_running && STOPFOREGROUND_ACTION.equals(intent.getAction())) {
Log.i(TAG, "Received Stop Foreground Intent");
is_service_running = false;
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
#Override
public void onDestroy() {
releaseWakeLock();
super.onDestroy();
}
private void showNotification(){
Intent notificationIntent = new Intent(mContext, ActivityMain.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(mContext)
.setContentTitle("myApp")
.setTicker("myApp")
.setContentText("Application is running")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pendingIntent)
.build();
// starts this service as foreground
startForeground(NOTIFICATION_ID, notification);
}
public void acquireWakeLock() {
final PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
releaseWakeLock();
//Acquire new wake lock
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG+"PARTIAL_WAKE_LOCK");
mWakeLock.acquire();
}
public void releaseWakeLock() {
if (mWakeLock != null && mWakeLock.isHeld()) {
mWakeLock.release();
mWakeLock = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
AndroidManifest.xml changes.
In the AndroidManifest.xml add this permission:
<uses-permission android:name="android.permission.WAKE_LOCK" />
Don't forget to add the name of your app in the <application> tag:
<application
....
android:name=".myApp"
....
And finally add your foreground service running into another process:
<service
android:name=".myAntiDozeService"
android:process=":MyAntiDozeProcessName">
</service>
A couple of notes.
In the previous example, the notification created, when clicked,
opens the ActivityMain activity of your test project.
Intent notificationIntent = new Intent(mContext, ActivityMain.class);
but you can use another kind of intent too.
To test it, you have to add some job to be performed into your
ActivityMain.java, for example some repeating alarm (which was
normally stopped when the device falls in Doze Mode), or a ripetitive
network access, or a timed tone played, or.... whatever you want.
Remember that the job performed by the main activity has to run
forever because to test this AntiDoze you need to wait at least 1
hour to be sure the device enters in Doze Mode.
To enter in Doze mode, the device has to be quiet and unplugged, so
you can't test it while you are debugging. Debug your app first,
check that everything is running then stop it, unplug, restart the
app again and leave the device alone and quiet on your desk.
The adb commands suggested by the documentation to simulate Doze
and StandBy modes could and could not give you the right results
(it depends, I suppose, by the device manufacturer, drivers, bla
bla). Please make your tests in the REAL behaviour.
In my first test, I used an AlarmManager and a tone generator to play a tone every 10 minutes just to understand that my app was still active.
And it is still running from about 18 hours, breaking my ears with a loud tone exactly every 10 minutes. :-)
Happy coding!
One of my peer developer has written an intent service that makes an API call and then sleeps for 2 mins. After waking up, it sends again.
Only have a service running while it is actively delivering value to the user. Sitting around for two minutes, watching the clock tick, is not actively delivering value to the user.
Will using alarm manager with WAKE permission solve this?
That depends on what you mean by "solve this". You can use AlarmManager to request to get control every two minutes so that you can do work. While the device is in Doze mode, you will not actually get control every two minutes, but once per maintenance window.

Android, keep CPU on when display is turned off?

I'm at my wits end with this issue. Can't find a good solution for it! In my app I have a service within which a timer gets scheduled over and over. That means after each timerTask is executed, another is started (first, both the timer and timerTask are canceled and then are assigned a new Timer and TimerTask). This is done 24 hours a day! I need this: First launch the app, start the service, then turn off the display and leave the device alone. But trouble is every time I come back to the device, I find my app has finished some tasks then is stuck on a timer although its time interval is passed! I think this is because of CPU sleeping.
For more explanation, suppose I have a list of time intervals in seconds: {60,100,40,120,80}. I start the service and leave the device alone for 5 minutes. Then I come back and see that tasks at 60 and 100 secs are done. But the service is stuck at the task at 40!
Each task includes a very simple network job that takes 5 secs at most.
I tried a wakelock but It's not gonna help. I don't know why!
Here is a code of my service:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(id, new Notification());
pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK,"");
wl.acquire();
doAction();
return START_STICKY;
}
private void doAction(){
if (timer != null)
timer.cancel;
if (timerTask != null)
timerTask.cancel;
timer = new Timer();
timerTask = new TimerTask() {
#Override
public void run() {
doAction();
//Do some network job!
}
};
timer.schedule(timerTask, getNewInterval());
}
You shouldn't keep the wakelock and maybe some power managing system feature kills your service because of this. Read documentation for:
WakefulBroadcastReceiver
and
AlarmManager
and see if first option or second one combined for example with IntentService can't fulfill your requirements. You can reset your alarm when trigerred by AlarmManager to another moment in time, even precisely, using setExact()

File Cleaning Service

As a requirement in Android OS phone, I am developing an app which will clean up a particular folder in a specified interval of time, say every 30 minutes.
I can run a service and clean up the folder every 30 mins. I have few questions over this,
1.Service has onStartCommand, which will be executed when the service starts, can I call a function here which has a Handler which runs every 30 minutes? Example
public int onStartCommand(Intent intent, int flags, int startId){
cleanUpData();
return START_REDELIVER_INTENT;
}
public void cleanUpData()
{
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// call the function again
cleanUpData();
}
}, "30 mins");
}
This code iterates the cleanUpData every 30 mins.
a. Is this correct?
b. Will this hamper the performance?
c. Should I use a separate thread as mentioned in numerous tutorials?
d. Should I use a service after-all? Or is there any other method?
AlarmManager provides the scheduled repeated alarms, but this does not work when the phone is in sleep mode. I do not want to wake up the screen as it does not require any human interaction. Can I ignore AlarmManager? Or does AlarmManager has functionality to run the code even when the phone is in sleep mode and phone wake up is false?
Please suggest. Thanks in advance!
you should use a separate thread for cleaning up the folder.I will not suggest you to use handler inside a service as it runs in the same process as app so a long running operation can make it unresponsive,instead of this use intentservice as it runs on its own thread.
And for the device sleep problem fire an broadcast receiver and acquire custom wake up lock in this broadcast receiver and then invoke your service from here.

Android dropping tcp connection when screen is off

My Android app starts and keeps a TCP connection going within a Foreground service. The activity uses StartService() to start the service. Also the service is started in it's own process.
Here is the services OnStartCommand:
// Code is in C# using Xamarin but regular Java/Android answers are acceptable
public override StartCommandResult OnStartCommand (Intent intent, StartCommandFlags flags, int startId)
{
base.OnStartCommand (intent, flags, startId);
...
var ongoingNotification = new Notification (Resource.Drawable.icon, "Service running");
var pendingIntent = PendingIntent.GetActivity (this, 0, new Intent (this, typeof(MainActivity)), 0);
ongoingNotification.SetLatestEventInfo (this, "Service", "The service is running.", pendingIntent);
StartForeground ((int)NotificationFlags.ForegroundService, ongoingNotification);
return StartCommandResult.RedeliverIntent;
}
The connection is fine when the phone's screen is on, whether or not an activity from my app is open or not. However less than 30 seconds after I turn the screen off I always lose tcp connection. My app reconnects automatically so I get the connection back, but it will continually disconnect when the screen is off. I turn it back on and it's fine, even if an activity from my app isn't open.
It could be connected to the Android Lifecycle but I just don't see how. Based on debug messages I write to a text file on the phone (the issue doesn't happen when a debugger from an IDE is attached), the service seems to be operating as it's supposed to but the connection just isn't being stable.
I've also tested this with and without the "Don't Keep Activities" in Developer options selected to no change. So it shouldn't have anything to do with the Activity Lifecycle at least.
Why is Android dropping my tcp connection, but only when the screen is off?
I solved the problem by acquiring a Partial WakeLock while my service is running.
private PowerManager.WakeLock mWakeLock;
public override void OnCreate ()
{
PowerManager pm = (PowerManager) GetSystemService(Context.PowerService);
mWakeLock = pm.NewWakeLock (WakeLockFlags.Partial, "PartialWakeLockTag");
mWakeLock.Acquire();
}
public override void OnDestroy ()
{
mWakeLock.Release();
}
If I implement something more power efficient in the future, I'll update this answer.

Background Service getting killed in android

We have developed an Android Application which involves a service in the background. To implement this background service we have used IntentService. We want the application to poll the server every 60 seconds. So in the IntentService, the server is polled in a while loop. At the end of the while loop we have used Thread.sleep(60000) so that the next iteration starts only after 60 seconds. But in the Logcat, I see that sometimes it takes the application more than 5 minutes to wake up (come out of that sleep and start the next iteration). It is never 1 minute as we want it to be.
What is the reason for this? Should background Services be implemented in a different way?
Problem2
Android kills this background process (intent service) after sometime. Can't exactly say when. But sometimes its hours and sometimes days before the background service gets killed. I would appreciate it if you would tell me the reason for this. Because Services are not meant to be killed. They are meant to run in background as long as we want it to.
Code :
#Override
protected void onHandleIntent(Intent intent) {
boolean temp=true;
while(temp==true) {
try {
//connect to the server
//get the data and store it in the sqlite data base
}
catch(Exception e) {
Log.v("Exception", "in while loop : "+e.toString());
}
//Sleep for 60 seconds
Log.v("Sleeping", "Sleeping");
Thread.sleep(60000);
Log.v("Woke up", "Woke up");
//After this a value is extracted from a table
final Cursor cur=db.query("run_in_bg", null, null, null, null, null, null);
cur.moveToLast();
String present_value=cur.getString(0);
if(present_value==null) {
//Do nothing, let the while loop continue
}
else if( present_value.equals("false") || present_value.equals("False") ) {
//break out of the while loop
db.close();
temp=false;
Log.v("run_in_bg", "false");
Log.v("run_in_bg", "exiting while loop");
break;
}
}
}
But whenever the service is killed, it happens when the the process is asleep. The last log reads - Sleeping : Sleeping. Why does the service gets killed?
The main problem is that we cannot say
Services are not meant to be killed. They are meant to run in background as long as we want it to.
Basically, that is not true. System still can terminate the service in low memory and possibly other situations.
There are 2 ways to overcome this:
If you are implementing the service, override onStartCommand() and return START_STICKY as the result. It will tell the system that even if it will want to kill your service due to low memory, it should re-create it as soon as memory will be back to normal.
If you are not sure 1st approach will work - you'll have to use AlarmManager http://developer.android.com/reference/android/app/AlarmManager.html . That is a system service, which will execute actions when you'll tell, for example periodically. That will ensure that if your service will be terminated, or even the whole process will die(for example with force close) - it will be 100% restarted by AlarmManager.
You could use ScheduledExecutorService designed specifically for such purpose.
Don't use Timers, as demonstrated in "Java Concurrency in Practice" they can be very inaccurate.
IntentService is not intended to keep running in a while loop. The idea is to react to an Intent, do some processing and stop the service once done.
That does not mean that it's not working and I can't tell you why you see such long delays but the cleaner solution is to use some external source to poke the service periodically. Besides vanilla Java methods you can also have a look at the AlarmManager or a Handler as mentioned in the AlarmManager documentation.
The Handler way would work like this
public class TriggerActivity extends Activity implements Handler.Callback {
// repeat task every 60 seconds
private static final long REPEAT_TIME = 60 * 1000;
// define a message id
private static final int MSG_REPEAT = 42;
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(this);
}
#Override
protected void onStart() {
super.onStart();
// start cycle immediately
mHandler.sendEmptyMessage(MSG_REPEAT);
}
#Override
protected void onStop() {
super.onStop();
// stop cycle
mHandler.removeMessages(MSG_REPEAT);
}
#Override
protected void onDestroy() {
super.onDestroy();
mHandler = null;
}
#Override
public boolean handleMessage(Message msg) {
// enqueue next cycle
mHandler.sendEmptyMessageDelayed(MSG_REPEAT, REPEAT_TIME);
// then trigger something
triggerAction();
return true;
}
private void triggerAction() {
// trigger the service
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.setAction("com.test.intent.OPTIONAL_ACTION");
startService(serviceIntent);
}
}
A simple Activity (which could be extended to have that functionality in all your activities) that sends itself a Message all the time while it is running (here between onStart and onStop)
A better solution would be have an AlarmManager go off every 60 seconds. This AlarmManager then starts the service that polls the server, the service then starts a new AlarmManager, its a recursive solution that works quite well.
This solution will be more reliable as you dont have the threat of the Android OS killing your service, looming over you. As per API: The Alarm Manager is intended for cases where you want to have your application code run at a specific time, even if your application is not currently running.
In your UI/main activity etc, set this timer, to go off in 60 seconds:
long ct = System.currentTimeMillis(); //get current time
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi); //60 seconds is 60000 milliseconds
In yourservice.class you could have this, it checks the connection state, if its good it sets the timer to go off in another 60 seconds:
public class yourservice extends IntentService {
public yourservice() { //needs this constructor
super("server checker");
}
#Override
protected void onHandleIntent(Intent intent) {
WifiManager wificheck = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
if(check for a certain condition your app needs etc){
//could check connection state here and stop if needed etc
stopSelf(); //stop service
}
else{ //poll the server again in 60 seconds
long ct = System.currentTimeMillis();
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi);
stopSelf(); //stop service since its no longer needed and new alarm is set
}
}
}
Services get killed. Like app gets killed. It is Android philosophy that you can get killed at any time.
You should as other wrote not make the assumption that your backgroundservice runs forever.
But you can use a foreground service to drastically reduce the chance of getting killed/restarted. Note that this forces a notification which is always visible. For example music players, vpn applications and sportstracker use this API.
For Problem 1, from vanilla Java, Thread.Sleep() is guaranted to wake the thread after the timer has expired, but not exactly after it has expired, it may be later depending mainly of the statuses of other threads, priority, etc.; so if you sleep your thread one second, then it will sleep at least a second, but it may be 10 depending of a lot of factors, i'm not very versed in Android development, but i'm pretty sure it's the same situation.
For Problem 2, services can be killed when memory get low or manually by the user, so as others have pointed probably using AlarmManager to restart your service after a certain time will help you to have it running all the time.
Sound like you should be using a Service instead of an IntentService but if you want to use an IntentService and have it run every 60 seconds you should use the AlarmManager instead of just telling the Thread to sleep.. IntentServices want to stop, let it and have AlarmManager wake it up when it should run again.
Android is pretty good about killing off long running services. I have found CommonsWare's WakefulIntentService useful in my application: https://github.com/commonsguy/cwac-wakeful
It allows you to specify a time interval as you are trying to do by sleeping.
It could be probably for two reasons..
Either the while loop is creating an issue, it is making the handler to work until temp==true
Adding to it is threads, that is creating long delays upto 6 seconds.
In case, the system is working for a large database, creating long delays between each query will add on the system memory.
When the memory usage for application become so huge that the system memory gets low, system has to terminate the process..
Solution for the Problem..
You could replace above with Alarm Manager to revoke system services after a particular interval of time using Alarm Manager.
Also for getting intent back after the system recovers the application from termination, you should use START_REDELIVER_INTENT. It is to get your last working intent back after the application terminates. for its usage, study https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT
You can try Jobscheduler implementation with JobService running in Background, which is recommended above Android O.

Categories

Resources