How to update a notification when a service stopped - Android - android

I have a Service that downloads a database and displays a notification that contains a progress bar indicating download progress. I want to update that notification when the service stops abruptly like when user kills the app, or app gets killed in the background by the OS. The problem is that the service's onDestroy method never called, so I cannot update the notification and remove its progress bar.
I have tried overriding onTaskRemoved method of the Service and added stopWithTask=true in manifest but that didn't helped. I also overrided onDestroy method of the Activity, and put the code that updates the notification, but that also didn't work.
Here's my service:
public class DatabaseDownloadService extends IntentService {
public static final int DOWNLOAD_NOTIFICATION_ID = 1;
private NotificationManagerCompat mNotificationManager;
private NotificationCompat.Builder mNotificationBuilder;
// For updating the UI on download progress.
private ResultReceiver mResultReceiver;
// If the service is stopped before download process is finished,
// updates the notification.
private boolean mCompleted;
public DatabaseDownloadService() {
super("DatabaseDownloadService");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Log.print(getClass().getName(), "onHandleIntent");
if (intent == null || intent.getAction() == null)
return;
mResultReceiver = intent.getParcelableExtra("receiver");
// Some code that downloads the file...
}
private void shutdown() {
if (!mCompleted) {
mNotificationBuilder.setContentText("Download did not complete.")
.setColor(getResources().getColor(R.color.error_color_red))
.setSmallIcon(R.drawable.ic_stat_pause)
.setOngoing(false)
.setProgress(0, 0, false);
mNotificationManager.notify(DOWNLOAD_NOTIFICATION_ID, mNotificationBuilder.build());
}
}
#Override
public void onCreate() {
Log.print(getClass().getName(), "onCreate");
super.onCreate();
startForeground(DOWNLOAD_NOTIFICATION_ID, mNotificationBuilder.build());
}
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onDestroy() {
Log.print(getClass().getName(), "onDestroy");
super.onDestroy();
// This is the code that updates the notification but onDestroy never gets called.
shutdown();
}
}
It should update the notification when the app is killed by Android, or the user. But it doesn't.
How can I solve this issue? I have been searching for a solution for days, and didn't find one, so my only hope is StackOverflow.

Related

Service is killed after a short period of time (1 minute)

I've created a service that its job is to clear the notifications when the app is closed by the user. Everything works perfectly well but sometimes when the application is in the background for more than 1 minute the service is killed (which means that the notifications are not cancelled).
Why is this happening? I thought that the only way that you can stop a service is by using either stopSelf() or stopService().
public class OnClearFromRecentService extends Service {
private static final String TAG = "onClearFromRecentServic";
private NotificationManagerCompat mNotificationManagerCompat;
#Override
public void onCreate() {
super.onCreate();
mNotificationManagerCompat = NotificationManagerCompat.from(getApplicationContext());
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service Started");
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service Destroyed");
}
#Override
public void onTaskRemoved(Intent rootIntent) {
//Put code here which will be executed when app is closed from user.
Log.d(TAG, "onTaskRemoved was executed ");
if (mNotificationManagerCompat != null) {
mNotificationManagerCompat.cancelAll();
} else {
Log.d(TAG, "onTaskRemoved: mNotifManager is null!");
}
stopSelf();
}
}
I start the service from the splash screen Activity like this: startService(new Intent(this, OnClearFromRecentService.class));
Also here are some Log messages:
Try returning START_STICKY form onStartCommand.
Then system will try to recreate.
check this official doc.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service Started");
return START_NOT_STICKY;
}
Also you can try returing START_REDELIVER_INTENT,if you also want Intent to be re-delivered.
START_REDELIVER_INTENT
Constant to return from onStartCommand(Intent, int, int): if this
service's process is killed while it is started (after returning from
onStartCommand(Intent, int, int)), then it will be scheduled for a
restart and the last delivered Intent re-delivered to it again via
onStartCommand(Intent, int, int).
From docs.
I found a solution with the help of #emandt.
I just added these lines of code in onStartCommand() :
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service Started");
Notification notification = new NotificationCompat.Builder(this, CHANNELID)
.setContentTitle("title")
.setContentText("text")
.setSmallIcon(R.drawable.baseline_pause_white_24)
.build();
startForeground(2001,notification);
return START_NOT_STICKY;
}
According to docs the startForeground method :
If your service is started then also make this service run in the foreground, supplying the ongoing notification to be shown to the user while in this state...By default started services are background, meaning that their process won't be given foreground CPU scheduling (unless something else in that process is foreground)
Also,
If your app targets API level 26 or higher, the system imposes restrictions on using or creating background services unless the app itself is in the foreground. If an app needs to create a foreground service, the app should call startForegroundService(). That method creates a background service, but the method signals to the system that the service will promote itself to the foreground. Once the service has been created, the service must call its startForeground() method within five seconds.

How to stop service even after restarting activity?

I want to stop service from activity . It stops when i don't close the app . But when i close the app and run the app again service doesn't stop.
Flow :
--> Click Switch ON
--> Show Notification / Location Listener
--> Kill App
--> Notification still remains (Service is running means)
--> Open app again and
--> Click Switch OFF
--> Service doesn't stop and notification persists
Service Class
public class locationService extends Service {
NotificationCompat.Builder builder;
NotificationManager notificationManager;
public static final String serviceTag = "LocationServiceTag";
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
SharedPreferences sharedPref = getSharedPreferences("AppPref",Context.MODE_PRIVATE);
userCurrentRoute= sharedPref.getString("RouteNo","");
mDatabase = FirebaseDatabase.getInstance().getReference();
notificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE);
Intent repeating_intent =new Intent(this,MainActivity.class);
PendingIntent pendingIntent= PendingIntent.getActivity(this,10,repeating_intent,PendingIntent.FLAG_UPDATE_CURRENT);
builder = new NotificationCompat.Builder(this)
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.bus_small)
.setContentTitle("BusBuzz")
.setContentText("Sharing Location to all")
.setAutoCancel(false)
.setOnlyAlertOnce(true)
.setOngoing(true)
;
notificationManager.notify(10,builder.build());
provider = new LocationGooglePlayServicesProvider();
provider.setCheckLocationSettings(true);
return START_NOT_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
notificationManager.cancel(10);
Toast.makeText(this, "App Closed Location Sharing Stopped", Toast.LENGTH_SHORT).show();
SmartLocation.with(this).location().stop();
super.onDestroy();
}
}
Activity
if (isChecked) {
startService(new Intent(MainActivity.this,locationService.class).addCategory(locationService.serviceTag));
}
else
{
stopService(new Intent(MainActivity.this,locationService.class).addCategory(locationService.serviceTag));
}
You wrote:
--> Click Switch ON
--> Show Notification / Location Listener
--> Kill App
--> Notification still remains (Service is running means)
Just because the notification exists, does not mean that the Service is still running. Actually, it is dead. You killed the App, which kills the Service, and since you return START_NOT_STICKY from onStartCommand(), Android will not restart the Service until your App makes another explicit call to startService(). Since the App was killed, onDestroy() was never called, so the notification was never removed.
You can verify this by using adb shell dumpsys activity services to see if your Service is running after you kill your App.

Android: Unable to stop foreground service

Simply, I want to upload files over the net using a service.
I have created a service with both: binding and startService. I create a foreground service that displays the progress.
The issue is, after the upload is complete, I call stopForeground(true) and then stopSelf but the notification is not removed and (probably) the service is not killed.
Code of the service class:
public class UploaderService extends Service {
// ...
public void finishUpload(File audioFile, File eventsData) {
LogWrapper.d(TAG, "finishUpload");
mUploadHelper.upload(audioFile, eventsData)
// consider this just a callback:
.subscribe(uuid -> {
// Log is printed but service is not stopped
LogWrapper.d(TAG, "finishUpload.subscribe");
stopForeground(true);
stopSelf();
}, LogWrapper::fatalError);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationCompat.Builder builder = NotificationsManager.getDefaultBuilder(this)
.setProgress(0, 0, true)
.setContentText(intent.getStringExtra(EXTRA_TOPIC_NAME))
.setContentTitle("Uploading data to server");
// first creating a notification with uuid,
// after POST /post call, post.uid will be used.
startForeground(DEAFULT_NOTIFICATION_ID, builder.build());
return START_REDELIVER_INTENT;
}
#Override
public IBinder onBind(Intent intent) {
LogWrapper.d(TAG, "onBind " + intent);
return mUploaderBinder;
}
#Override
public boolean onUnbind(Intent intent) {
LogWrapper.d(TAG, "onUnbind " + intent);
intent.putExtra(EXTRA_TOPIC_NAME, mUploadHelper.getTopic().name);
startService(intent);
return false;
}
#Override
public void onCreate() {
super.onCreate();
mUploaderBinder = new UploaderBinder();
}
public class UploaderBinder extends Binder {
public UploaderService getService() {
return UploaderService.this;
}
}
}
Though I could not find it in docs, the startService command should be called from outside the service i.e. from an Activity. I called it from my activity just before binding to it and it worked as expected.
I was probably experiencing an undefined behavior- the service was starting but not stopping. I wish someone could give me a better explanation of why that was happening, and that this indeed is the correct way to do stuff.
Great thanks to #pskink for pointing me in the right direction.

onGoing notification disappears after a few second

I am trying to put a notification in the status bar when a service starts and keep it there until I stop the service but is disappears after a few seconds(about 10). Any suggestions as to what I am missing? This worked before I tried to re write using notification.builder for compatibility with api 15. The log entry shows onDestroy is not called until I stop the service so it is still running.
public class MyService extends Service {
private NotificationManager mNM;
private int NOTIFICATION = R.string.service_started;
public void onCreate() {
super.onCreate();
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
showNotification();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("MyService", "Service Started");
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mNM.cancel(NOTIFICATION);
Log.e("MyService", "Service Ended");
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
private void showNotification() {
Notification.Builder builder = new Notification.Builder(getApplicationContext());
builder.setAutoCancel(false)
.setOngoing(true)
.setSmallIcon(R.drawable.myicon)
.setTicker(getText(R.string.service_label))
.setWhen(System.currentTimeMillis())
.setContentTitle(getText(R.string.service_started))
.setContentText(getText(R.string.service_label));
Notification notification = builder.getNotification();
mNM.notify(NOTIFICATION, notification);
}
I had the same problem with an ongoing notification disappearing in ICS on a new phone. The app and notification had worked perfectly in every version of Android I had tested it on previously, and it even works on an ICS emulator. Needless to say this has been driving me crazy for a couple months now, but I finally found the answer.
http://code.google.com/p/android/issues/detail?id=21635
I am using a BroadcastReceiver to monitor incoming calls on the handset and I programmatically enable the receiver when a button is toggled in addition to setting the notification. So I wrote a small test app with the same BroadcastReceiver hooked up and was able to reproduced the problem. I commented out the setComponentEnabledSetting call and the notification no longer disappears.

How can we prevent a Service from being killed by OS?

I am using Service in my application and it needs to run until my application is uninstalled, but the problem is it gets killed by OS.
How can we prevent it from being killed by OS? Or if it gets killed can we restart that service again through programmatically?
You may run the service in the foreground using startForeground().
A foreground service is a service that's considered to be something
the user is actively aware of and thus not a candidate for the system
to kill when low on memory.
But bear in mind that a foreground service must provide a notification for the status bar (read here), and that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
Note: This still does not absolutely guarantee that the service won't be killed under extremely low memory conditions. It only makes it less likely to be killed.
I've been puzzled by the same issue to yours recently.but now,I've found a good solution.
First of all,you should know that, even your service was killed by OS, the onCreate method of your service would be invoked by OS in a short while.So you can do someting with the onCreate method like this:
#Override
public void onCreate() {
Log.d(LOGTAG, "NotificationService.onCreate()...");
//start this service from another class
ServiceManager.startService();
}
#Override
public void onStart(Intent intent, int startId) {
Log.d(LOGTAG, "onStart()...");
//some code of your service starting,such as establish a connection,create a TimerTask or something else
}
the content of "ServiceManager.startService()" is:
public static void startService() {
Log.i(LOGTAG, "ServiceManager.startSerivce()...");
Intent intent = new Intent(NotificationService.class.getName());
context.startService(intent);
}
However, this solution is just available for the situation of your service being killed by GC.Sometimes our service might be killed by user with Programme Manager.In this situation,your prosses will be killed,and your service will never been re-instantiated.So your service can not be restarted.
But the good news is,when the PM kill your service,it will call your onDestroy method.So we can do something with that method.
#Override
public void onDestroy() {
Intent in = new Intent();
in.setAction("YouWillNeverKillMe");
sendBroadcast(in);
Log.d(LOGTAG, "onDestroy()...");
}
The string of "YouWillNeverKillMe" is a custom action.
The most important thing of this method is,don't add any code before send the broadcast.As system will not wait for completion of onDestroy(),you must send out the broadcast as soon as posible.
Then regist a receiver in manifast.xml:
<receiver android:name=".app.ServiceDestroyReceiver" >
<intent-filter>
<action android:name="YouWillNeverKillMe" >
</action>
</intent-filter>
</receiver>
Finally,create a BroadcastReceiver,and start your service in the onReceive method:
#Override
public void onReceive(Context context, Intent intent) {
Log.d(LOGTAG, "ServeiceDestroy onReceive...");
Log.d(LOGTAG, "action:" + intent.getAction());
Log.d(LOGTAG, "ServeiceDestroy auto start service...");
ServiceManager.startService();
}
Hope this will be helpful to you,and excuse my poor written english.
Override method onStartCommand() in your service class and simply return START_STICKY (as suggested by "Its not blank"). That's all you need. If the process that runs your service gets killed (by a low memory condition for example), the Android system will restart it automatically (usually with some delay, like 5 seconds).
Don't use onStart() anymore as suggested in another answer, it's deprecated.
use
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//**Your code **
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
ref Documentation lifecycle of Service.
Edit added method.
As far i know, onDestroy() will be called only when the service is explicitly stopped(Force Stop). But this method won't get called in case the service gets killed by OS/swiping the Recent Apps list. In those cases another event handler named onTaskRemoved(Intent) gets called. This is due to a defect in Android 4.3-4.4 as per the link here. Try using the below code:-
public void onTaskRemoved(Intent intent){
super.onTaskRemoved(intent);
Intent intent=new Intent(this,this.getClass());
startService(intent);
}
I found another solution of the problem which gurantees that your service will be always alive. In my case, this scheme resloves also the problem with FileObserver, which stops work after some period of time.
Use an activity (StartServicesActivity) to start the service (FileObserverService) as Foreground service.
Use BroadcastReceiver class (in example CommonReceiver) to restart your service in some special situations and in case it was killed.
I used this code in my app "Email Pictures Automatically"
https://play.google.com/store/apps/details?id=com.alexpap.EmailPicturesFree
Here is CommonReceiver class.
public class CommonReceiver extends BroadcastReceiver {
public void onReceive(Context paramContext, Intent paramIntent)
{
paramContext.startService(new Intent(paramContext, FileObserverService.class));
}
}
Here is its definition in AndroidManifest.xml just before application closing tag.
<receiver android:name="com.alexpap.services.CommonReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT"/>
</intent-filter>
</receiver>
Start service in StartServicesActivity activity.
Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
StartServicesActivity.this.startService(iFileObserver);
Here is onStartCommand() method of the service.
public int onStartCommand(Intent intent, int flags, int startId) {
int res = super.onStartCommand(intent, flags, startId);
/*** Put your code here ***/
startServiceForeground(intent, flags, startId);
return Service.START_STICKY;
}
public int startServiceForeground(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, StartServicesActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("File Observer Service")
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(300, notification);
return START_STICKY;
}
I tested this code using Task Killer app, and each time the service was killed, it was restarted again almost immediately (performs onStartCommand()). It is restarted also each time you turn on the phone and after rebooting.
I use this code in my application, which emails every picture you take with your phone to predefinde list of emails. The sending email and list of receiving emails are set in another activity and are stored in Shared Preferences. I took about 100 pictures in several hours and all they were sent properly to receiving emails.
#Override
public void onDestroy() {
super.onDestroy();
startService(new Intent(this, YourService.class));
}
write above code in your service and your service will never stop even user want to destroy it or they want to kill it it will never kill untill your app not get uninstall from your device
You can try to start your service repeatedly, for example every 5 sec.
This way, when your service is running, it will perform onStartCommand() every 5 sec. I tested this scheme and it is very reliable, but unfortunately it increases slightly phone overhead.
Here is the code in your activity where you start the service.
Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
PendingIntent pendingIntentFileObserver = PendingIntent.getService(StartServicesActivity.this, 0, iFileObserver, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
Date now = new Date();
//start every 5 seconds
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, now.getTime(), 5*1000, pendingIntentFileObserver);
And here is onStartCommand() of the service.
//class variable
public static boolean isStarted = false;
public int onStartCommand(Intent intent, int flags, int startId) {
int res = super.onStartCommand(intent, flags, startId);
//check if your service is already started
if (isStarted){ //yes - do nothing
return Service.START_STICKY;
} else { //no
isStarted = true;
}
/**** the rest of your code ***/
return Service.START_STICKY;
}
First create service in another process, and write broadcaster which runs in recursion in time intervals
protected CountDownTimer rebootService = new CountDownTimer(9000, 9000) {
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
sendBroadcast(reboot);
this.start();
Log.d(TAG, "rebootService sending PREVENT AUTOREBOT broadcast");
}
};
After that register broadcast receiver in main process also with timer recursion that is launched after first broadcast from service arrived
protected static class ServiceAutoRebooter extends BroadcastReceiver {
private static ServiceAutoRebooter instance = null;
private RebootTimer rebootTimer = null;
private static ServiceAutoRebooter getInstance() {
if (instance == null) {
instance = new ServiceAutoRebooter();
}
return instance;
}
public class RebootTimer extends CountDownTimer {
private Context _context;
private Intent _service;
public RebootTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
_context.startService(_service);
this.cancel();
Log.d(TAG, "Service AutoRebooted");
}
}
#Override
public void onReceive(Context context, Intent intent) {
if (rebootTimer == null) {
Log.d(TAG, "rebootTimer == null");
rebootTimer = new RebootTimer(10000, 10000);
rebootTimer._context = context;
Intent service = new Intent(context, SomeService.class);
rebootTimer._service = service;
rebootTimer.start();
} else {
rebootTimer.cancel();
rebootTimer.start();
Log.d(TAG, "rebootTimer is restarted");
}
}
}
Service will be auto-rebooted if time at RebootTimer (main process) expires, which means that "PREVENT AUTOREBOT" broadcast from service hasn't arrived
i found a solution .... late answer but i wanted to answer...
we can send a broadcast in the ondestroy of the service and create a receiver that receives the broadcast and starts the service again.... when it is destroyed by any reasons...
pls try following:
final Messenger mMessenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
default:
super.handleMessage(msg);
}
}
}
#Override
public void onCreate() {
super.onCreate();
makeServiceForeground();
}
#Override
public IBinder onBind(Intent arg0) {
return mMessenger.getBinder();
}
private void makeServiceForeground() {
IActivityManager am = ActivityManagerNative.getDefault();
try {
am.setProcessForeground(onBind(null), android.os.Process.myPid(), true);
} catch (RemoteException e) {
Log.e("", "cant set to foreground" + e.toString());
}
}
also need add in manifest.xml
<uses-permission android:name="android.permission.SET_PROCESS_LIMIT"/>

Categories

Resources