I am trying to run a background service in an android application. I have tried the standard way of doing it and a lot of other tweaks. The problem is that when the application is closed from recent apps, the service gets killed. This only happens in Chinese Android devices like Oppo, Vivo, Xiomi, etc. It works fine on all other brands.
I have tried the following solutions.
Over-riding the OnStartCommand() of activity to return START_STICKY. This still not re-starts activity after the application is closed as expected.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
Added the application to exceptions of battery saving, so that it doesn't close it to save battery in DOZE mode.
Gave the application permission to Auto-Start from the security settings of phone.
Started the service as a foreground service, including a persistent notification.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
//do heavy work on a background thread
//stopSelf();
return START_NOT_STICKY;
}
Implemented Alarm Manager approach of starting service after it gets killed in onTaskRemoved() and onDestroy() methods of service.
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
android:stopWithTask="false" />
#Override
public void onTaskRemoved(Intent rootIntent){
Log.d("Service:","I am being closed!");
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
I have also tried using a broadcast manager to start my service receiving a broadcast from activity when it is closed.
<receiver
android:name=".SensorRestarterBroadcastReceiver"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped"
/>
public class SensorRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(SensorRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops!");
context.startService(new Intent(context, MyService.class));;
}
}
However, the service is not started again in these chinese devices specifically. While background services of Whatsapp, Facebook and some famous apps return after a few minutes of closing the app. Please suggest the correct way to achieve this.
I have tried solutions provided at Background Service is not restarting after killed in oppo, vivo, mi android version 7.1.2 as also described above. It doesn't solve my problem.
This will happen mostly for optimizing battery and improving phone performance,
As you have tried my solution but still you are getting this issue in some devices,
so here is an alternate solution for killing app on swipe from recent app list,
Ask User to Swipe down the application which will lock application as white list and wont be killed
OR
Add below line for every activity in manifest, which will hide your app from recent app list. hence user cant swipe app from recent apps list.
android:excludeFromRecents="true"
These brands have a white list of apps that can survive normally. If your app not in white list os force stops any app that swiped at recents screen. You must tell user to add your app to whitelist manually.
Related
I have a media player service that gets killed everytime the user clears recent apps. I want the service to continue playing on the background. I have tried
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
return START_STICKY;
}
and
mediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
but it's not working. How do I solve this?
Google did some updates:
Some of those updates included security and it reached the services. This means that we can no longer perform lengthy operations in the background without notifying the user.
Foreground
A foreground service performs some operation that is noticeable to the
user. For example, an audio app would use a foreground service to play
an audio track. Foreground services must display a Notification.
Foreground services continue running even when the user isn't
interacting with the app.
Background
A background service performs an operation that isn't directly noticed
by the user. For example, if an app used a service to compact its
storage, that would usually be a background service. Note: If your app
targets API level 26 or higher, the system imposes restrictions on
running background services when the app itself isn't in the
foreground. In most cases like this, your app should use a scheduled
job instead.
Make sure to call startForeground as soon as possible
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
//do heavy work on a background thread
//stopSelf();
return START_STICKY;
}
This is how you start the foreground service:
public void startService() {
Intent serviceIntent = new Intent(this, ForegroundService.class);
serviceIntent.putExtra("inputExtra", "Foreground Service Example in Android");
ContextCompat.startForegroundService(this, serviceIntent);
}
As specified in Mr.Patel answer,
Many of the manufactures wont allow running of background service when frontend Activity is not running or removed from the recent list.
There is a way of achiving your requirement.
You can run your service in the background by setting a non cancellable notification from your application. Till you close your notification forcefully programatically using a close button, your service will be running in the background.
Hope this will solve your problem.
Well i have searched about 5 hours but no answer seems to do the trick. My app will keep tracking postal objects from a website, and needs to make a refresh every X (user defines it) amount of time to check for updates on the site, so the user will be aware of it and even can turn this feature off if he wants (i will cancel the alarmmanagers manually if so).
What I need to do is something like whatsapp, facebook or gmail, that checks for messages even with the app closed, and send user some notification. But that just seems impossible... I checked with "adb shell dumpsys alarm" and the alarm is there, but only while the app is running. I know this is the exactly expected thing to happen, but how does these other apps always set their AlarmMannagers againd when app is closed?
My current code:
Manifest:
<service android:name="services.HelloService"
android:process=":remote"/>
<receiver android:name="entidade.NotificationPublisher">
<intent-filter>
<action android:name="IWillStartAuto" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
HelloService:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("Hello Service Starting...");
ArrayList<Objeto> list = new ObjetoDAO(this).getAllObjetos(); //Sets the alarms...
for(int i=0; i<list.size(); i++){
if(!list.get(i).isArquivado())
list.get(i).setAlarme(this);
}
return START_STICKY;
}
#Override
public void onDestroy() {
System.out.println("Hello Service Ending...");
sendBroadcast(new Intent("IWillStartAuto"));
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
sendBroadcast(new Intent("IWillStartAuto"));
}
NotificationPublisher:
public class NotificationPublisher extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
ArrayList<Objeto> lista = new ObjetoDAO(context).getAllObjetos(); //list of tracked objects
for(int i=0; i<lista.size(); i++){
if(!lista.get(i).isArquivado()) //sets the AlarmMannager if users want it...
lista.get(i).testeNotificacao(context);
}
}}
UPDATE: Code used to set the alarm...
AlarmManager:
public void setAlarme(Context context) {
if (!isArquivado()) {
int idDoAlarme = getId();
Calendar calendar = Calendar.getInstance();
long interval = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
Intent notificationIntent = new Intent(context, NotificationPublisher.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, idDoAlarme, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), interval, pendingIntent);
}
}
Also noted something: tested the app on my old phone (LG E435f, with Android 4.1.2) and surprise, the Alarm is still set after the app gets closed. So, could it be something with my phone (Lenovo K5 A6020l36, with Android 5.1.1)? But in this case, how are whatsapp and gmail still running their alarms on this phone with the apps closed?
I notice in your question that you want to send notification from server to your user like whats-app, Facebook, etc. even when app is closed.
Have you read about Firebase Cloud Messaging (FCM) or Google Cloud Messaging (GCM), I think this would more suit to your problem. Click Here to know more about it.
Set up your AlarmManager to fire at your predetermined date\time.
When it fires, your code will run so just repeat step 1 to set up the next time you want it to fire and then perform whatever functions you need to in order to check your site (start a service that does this)
Repeat from step 2
Don't forget that alarms do not survive a reboot of the device so you will need to set it up again if the device restarts.
Well, if the user kills your app, than it depends on the manofacturer. Some kill only the process, others kill the process along with services, broadcasts, alarmmangers (etc.) associated with the process.
Maybe the GCM can survive this kill process, as you sayd that whatsapp does get its notifications...
UPDATE: Previously I couldn't find a well defined pattern as to when my foreground service was being killed. After more debugging with the devices (doesn't happen on all) on which this was happening I found.
1.) A lot of times when I open chrome to load a website the foreground service gets killed. Sometimes even when I am using whatsapp this happens.
2.) There are no exceptions and the stacktrace doesn't show anything useful.
Original Question below:
There are many such questions on StackOverflow but the answers so far that I have read mostly say that it is upto Android and we don't have 100% guarantee that a foreground service will not be killed. Some answers suggest START_STICKY but that is not much helpful in my case.
In my case I have a music player app which has a foreground service. This service gets killed on certain devices, mostly some versions of Xiomi (Android version was 5.1.1). Now I understand that android might be short on memory and so my foreground service is being killed, but then why do other music player apps never go through such termination. What is it that they are doing right that I am not?
I made my service foreground service by using startForeground. Also I return START_STICKY in onStartCommand although that doesn't help because the service is restarted after a period of 4-5 sec if killed. To bind my service with my activity I use
bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT );
So what exactly can I improve/change in my app to prevent this from happening, if other apps are working right there must be something that is wrong in my case. Can someone please help. Thanks in advance !!
Edit:
This is how I call startForeground()
public void sendNotification() {
Intent notIntent = new Intent(this, MainActivity.class);
notIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendInt = PendingIntent.getActivity(this, 0,
notIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Bitmap bitmap = null;
if (!notificationShowing || !forwarded) {
Log.i(TAG, "present");
String title = CommonUtils.getSongFromID(songIndex, this);
bigView.setTextViewText(R.id.title, title);
bigView.setImageViewBitmap(R.id.img, bitmap);
smallView.setTextViewText(R.id.title1, title);
smallView.setImageViewBitmap(R.id.img1, bitmap);
if (pauseButton == 1) {
bigView.setImageViewResource(R.id.pause, R.drawable.pause_noti);
smallView.setImageViewResource(R.id.pause1, R.drawable.pause_noti);
} else {
bigView.setImageViewResource(R.id.pause, R.drawable.play_noti);
smallView.setImageViewResource(R.id.pause1, R.drawable.play_noti);
}
musicNotification = builder.setContentIntent(pendInt)
.setSmallIcon(R.drawable.logo1)
.setTicker(songTitle)
.setOngoing(true)
.setContentTitle("Playing")
.setStyle(new Notification.BigTextStyle().bigText("Song App"))
.setContentText(songTitle)
.setPriority(Notification.PRIORITY_MAX)
.build();
musicNotification.contentView = smallView;
musicNotification.bigContentView = bigView;
musicNotification.contentIntent = pendInt;
Intent switchIntent = new Intent("pause");
switchIntent.putExtra("button", "pause");
PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(this, 100, switchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
bigView.setOnClickPendingIntent(R.id.pause, pendingSwitchIntent);
smallView.setOnClickPendingIntent(R.id.pause1, pendingSwitchIntent);
Intent switchIntent1 = new Intent("forward");
switchIntent1.putExtra("button", "forward");
PendingIntent pendingSwitchIntent2 = PendingIntent.getBroadcast(this, 100, switchIntent1, PendingIntent.FLAG_UPDATE_CURRENT);
bigView.setOnClickPendingIntent(R.id.forward, pendingSwitchIntent2);
smallView.setOnClickPendingIntent(R.id.forward1, pendingSwitchIntent2);
Intent switchIntent2 = new Intent("previous");
switchIntent2.putExtra("button", "previous");
PendingIntent pendingSwitchIntent3 = PendingIntent.getBroadcast(this, 100, switchIntent2, PendingIntent.FLAG_UPDATE_CURRENT);
bigView.setOnClickPendingIntent(R.id.previous, pendingSwitchIntent3);
smallView.setOnClickPendingIntent(R.id.previous1, pendingSwitchIntent3);
Intent switchIntent3 = new Intent("end");
switchIntent3.putExtra("button", "end");
PendingIntent pendingSwitchIntent4 = PendingIntent.getBroadcast(this, 100, switchIntent3, PendingIntent.FLAG_UPDATE_CURRENT);
bigView.setOnClickPendingIntent(R.id.end, pendingSwitchIntent4);
smallView.setOnClickPendingIntent(R.id.end1, pendingSwitchIntent4);
startForeground(NOTIFY_ID, musicNotification);
notificationShowing = true;
}
forwarded = false;
}
This happened in Xiomi phone due to below reason.
Solution for MIUI 7.0 => Security => Autostart => select Apps that you want to run in background => Reboot
After reboot your device should able to run your application services in background like other android devices do.
MIUI 4.0 settings
MIUI AutoStart Detailed Description
And if you looking for other phone then check here is service structure.It automatically restart but when you restart phone call BootReceiver.
public class AppService extends Service {
private class LocalBinder extends Binder {
public AppService getServerInstance() {
return AppService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// If we get killed, after returning from here, restart
return Service.START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
}
}
Thanks hope this will help you.
This service gets killed on certain devices, mostly some versions of
Xiomi (Android version was 5.1.1)
Not sure about this, but as per my understanding this might be because of
Bug in os customization from the vendor.
Bugs in Android with respect to prioritizing foreground services, that are triggered by various combinations of service binding flags(i.e. BIND_AUTO_CREATE, BIND_IMPORTANT etc).Read this answer by Robin Davies.
I don't know whether you use startService() or not. But if you don't then as per this documentation:
You can create a service that is both started and bound. That is, the
service can be started by calling startService(), which allows the
service to run indefinitely, and also allow a client to bind to the
service by calling bindService().(This is called Binding to a Started Service)
If you do allow your service to be started and bound, then when the
service has been started, the system does not destroy the service when
all clients unbind. Instead, you must explicitly stop the service, by
calling stopSelf() or stopService().
Although you should usually implement either onBind() or
onStartCommand(), it's sometimes necessary to implement both. For
example, a music player might find it useful to allow its service to
run indefinitely and also provide binding. This way, an activity can
start the service to play some music and the music continues to play
even if the user leaves the application. Then, when the user returns
to the application, the activity can bind to the service to regain
control of playback.
Be sure to read the section about Managing the Lifecycle of a Bound Service, for more information about the service lifecycle when adding binding to a started service.
onStartCommand will be called in case of started service so START_STICKY will work in case of startService() only.
Update on process logs
Proc # 5: prcp F/S/IF trm: 0 22407:com.wave.music.player/u0a2
(fg-service)
In your process log your player service running in foreground with adj setting prcp (visible foreground service) which means it's virtually indestructible. Still your service destroyed by OS than there might be very low memory available to run newly launch app. As per this documentation,
There will only ever be a few foreground processes in the system, and these
will only be killed as a last resort if memory is so low that not even
these processes can continue to run. Generally, at this point, the
device has reached a memory paging state, so this action is required
in order to keep the user interface responsive.
So I think you are doing nothing wrong. I just want to suggest you to read this official Android developer documentation and try to run your service in separate process(Documentation suggests this approach for music player app). Be careful to implement this as it can easily increase—rather than decrease—your RAM footprint if done incorrectly.
I have an app that should update/get data from a server every six hours. To do so I made an AlarmManager the following way:
public class Repository {
public static AlarmManager alarmManager;
public static void initAlarmManager(Context context){
//start the update alarm manager
Intent resultIntent = new Intent(context,AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, 60 * 60 * 1000, pendingIntent);
}
My AlarmReceiver now has to look for updates and if there is new data of a specific condition it has to notify the user via a notification. This is a part of my AlarmReceiver:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(Repository.ddfDb == null){
Repository.initDdfDb(context);
}
if(Repository.alarmManager == null){
Repository.initAlarmManager(context);
}
for(Episode episode : Repository.ddfDb.getListOfNextEpisodes()){
Notification.showNotification(context,episode);
}
}
}
Since my AlarmManager should run all the time I let him start also when boot is completed. To do so I added the following to my manifest file:
<receiver android:name=".AlarmReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
I run the function initAlarmManager() in my onCreate() of the mainActivity. So if the app is started, the AlarmManager starts too and everything works fine. Even if I close my app via the home button or change to another app via the "change between recent apps" button, my AlarmManager still fires and I get the notifications although my app is in the background.
I also run the function initAlarmManager() in my AlarmReceiver. So if I reboot my phone, the receiver gets called, sees that my AlarmManager is null and inits it afterwards. So everything works fine here too.
But here is my problem: If I press the "change between recent apps" button and close my app with a swipe my AlarmManager stops and I won't get any further notifications. This is weird, since after a reboot my app also doesn't appear in the recent apps menu but there it works.
I googled a lot and some people say it is impossible, since if the user really wants to close the app, he has to be able to do so. I understand this, since it provides security against virus apps. But also I see apps like WhattsApp being able to always notify the user.
So is there really no way to accomplish my always running AlarmManager or if there is a way, how do I implement this?
Thank you in advance!
I solved this issue the following way:
I moved all operations I did in the BroadcastReceiver to an IntentService. All the BroadcastReceiver now does is starting this IntentService.
When the app is now closed by swiping it away from the recent apps menu, my notifications still pop up.
Thanks to Muthu for giving me the right hint!
I have a scheduler kind of app that sends a notification to the user at a specified time in the morning and in the evening. I have done this using Alarm Manager. On the emulator the application works just fine as far as this is concerned. However in my device I use "Advanced Task Killer" application to kill running apps and free the device's memory. I noticed that on killing this application the notifications are not shown at the scheduled time. This definitely seems logical but is not at all what I want. I want the notifications to be shown at the scheduled time even though the process is killed. Is there a way to do so?
I figured a little bit of code might be helpful to realize my problem :
I have this in my Main Activity
public void setAlarm(){
Intent intent = new Intent(MainActivity.this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.cancel(pendingIntent);
Cursor tempcursor = db.getAlarmTime();
tempcursor.moveToFirst();
int hour = tempcursor.getInt(tempcursor.getColumnIndex("Hour"));
int minute = tempcursor.getInt(tempcursor.getColumnIndex("Minute"));
tempcursor.close();
db.close();
GregorianCalendar alarmtime = new GregorianCalendar();
alarmtime.set(GregorianCalendar.HOUR_OF_DAY, hour);
alarmtime.set(GregorianCalendar.MINUTE, minute);
alarmtime.set(GregorianCalendar.SECOND, 0);
alarmtime.set(GregorianCalendar.MILLISECOND, 0);
if(alarmtime.before(new GregorianCalendar()))alarmtime.add(GregorianCalendar.DAY_OF_MONTH, 1);
am.set(AlarmManager.RTC_WAKEUP, alarmtime.getTimeInMillis(), pendingIntent);
}
And this in my AlarmReceiver class that extends BroadcastReceiver :
public void onReceive(Context context, Intent intent) {
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent i = new Intent(context, Schedule_Today.class);
PendingIntent pi = PendingIntent.getActivity(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi2 = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AlarmManager.INTERVAL_DAY, pi2); //Setting another notification after 1 day as soon this notification broadcast is received.
CharSequence from = "Scheduler_3";
CharSequence message = "Test Notification";
notification = new Notification(R.drawable.ic_launcher, "Attention",System.currentTimeMillis());
notification.setLatestEventInfo(context, from, message, pi);
notificationManager.notify(1, notification);
}
I want to prevent the application from being killed or at least a module of it running that can broadcast the alarmmanager at the required time so that the user receives notifications
This is not strictly possible, except by making your own version of Android in your own ROM mod.
On Android 2.1 and earlier, third-party task managers, like "Advanced Task Killer", had the ability to "force stop" an application. On Android 2.2 and higher, that ability was reserved for the OS itself, and is available to users via the "Force Stop" button on the app's screen in the list of applications in Settings.
When an app is "force stopped", among other things, all scheduled alarms are removed. In addition, on Android 3.1+, nothing of that app will ever run again, until the user manually launches one of your activities (or something else manually runs one of your components).
You are welcome to write two applications, one that is the main app and the other than ensures that, if the first one appears to have been force-stopped, the alarms are rescheduled. However, there is nothing stopping the user from force-stopping both of those applications.
Also, bear in mind that some devices, like the SONY Xperia Z, block _WAKEUP alarms in general, if the user has activated "STAMINA Mode". See this blog post for more about this.
Hence, I recommend that you redesign your application to take into account that your alarms are not guaranteed to run at all, let alone at the time you expect.