49

I manage an ONGOING notification from my application (not from a service).

When I kill application from task manager with "End" button, notification disappear.

When I remove application from multitask pannel, application is killed but notification remains.

My questions are:

  • How to catch this event to clear notification?
  • What happens when application is removed from multitask pannel? Application is destroyed but process staying alive? Is it normal?

As an update:

All my activities extends MyActivity class (which extends Activity) with methods:

@Override protected void onCreate(Bundle state) {
    super.onCreate(state);
    ((MyApplication) getApplication()).onActivityCreate(this, state);
}

@Override protected void onDestroy() {
    super.onDestroy();
    ((MyApplication) getApplication()).onActivityDestroy(this);
}

And my application extends MyApplication class (which extends Application) with methods:

private List<Activity> activities = new ArrayList<Activity>();

protected final void onActivityCreate(Activity activity, Bundle state) {
    if(activities.isEmpty() && state == null) {
        onStart();
    }
    activities.add(activity);
}

protected final void onActivityDestroy(Activity activity) {
    activities.remove(activity);
    if(activities.isEmpty() && activity.isFinishing()) {
        onExit();
    }
}

protected void onStart() {
    // some code
}

protected void onExit() {
    // some code
    notificationManager.cancel(NOTIFICATION_ID);
}

activities is a list of all running activities

It's not simplest mechanism but I need it

Should I use a service instead?


As a new update:

In my onExit() method, if I Log debug message to know what happens like this:

public void onExit() {
    for(int i = 0; i < 100; i++) {
        Log.d(TAG, "onExit");
    }
}

a small amount of log appears once on two, not all (ex: 13/100)

So, I understand that remove application from multitask pannel force to kill application without waiting close methods end to finish properly... But why not process ?!

How can I force to terminate properly ?

alex
  • 5,661
  • 6
  • 33
  • 54
  • Can you show the code as to how your build your Notification? – tolgap Oct 21 '12 at 12:41
  • It is simplest as possible: Notification notification = new Notification(...); notificationManager.notify(NOTIFICATION_ID, notification); – alex Oct 21 '12 at 14:27
  • 2
    I should mention that the problem does not occur when I leave the application directly but when I put it in the background and I empty the list of applications – alex Oct 21 '12 at 16:01

8 Answers8

46

Killing Notifications when main app has been killed.

Since your notification and your app are handled in different threads killing your app via MultitaskManager won't kill your notification. As you already correctly investigated killing your app won't even necesarrily result in an onExit() callback.

So what is the solutions?

You could start a service from your activity. A specialty services have: they restart themselves automatically if app-process have been killed for some reason. So you could reuse the automatic restart by killing the notification on restart.

Step 1 create a service that kills Simple one. It just kills a notification on create and has his special Binder.

public class KillNotificationsService extends Service {

    public class KillBinder extends Binder {
        public final Service service;

        public KillBinder(Service service) {
            this.service = service;
        }

    }

    public static int NOTIFICATION_ID = 666;
    private NotificationManager mNM;
    private final IBinder mBinder = new KillBinder(this);

    @Override
    public IBinder onBind(Intent intent) {
            return mBinder;
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
            return Service.START_STICKY;
    }
    @Override
    public void onCreate() {
            mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            mNM.cancel(NOTIFICATION_ID);
    }
}

Step2: Add it to your manifest: Add it somewhere inbetween your <application> tags.

<service android:name="KillNotificationsService"></service>

Step3: Always create the Service before fireing the notification, and use the static notificationid.

ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className,
            IBinder binder) {
        ((KillBinder) binder).service.startService(new Intent(
                MainActivity.this, KillNotificationsService.class));
        Notification notification = new Notification(
                R.drawable.ic_launcher, "Text",
                System.currentTimeMillis());
        Intent notificationIntent = new Intent(MainActivity.this,
                Place.class);
        PendingIntent contentIntent = PendingIntent.getActivity(
                MainActivity.this, 0, notificationIntent, 0);
        notification.setLatestEventInfo(getApplicationContext(),
                "Text", "Text", contentIntent);
        NotificationManager mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mNM.notify(KillNotificationsService.NOTIFICATION_ID,
                notification);
    }

    public void onServiceDisconnected(ComponentName className) {
    }

};
bindService(new Intent(MainActivity.this,
        KillNotificationsService.class), mConnection,
        Context.BIND_AUTO_CREATE);

It might take a little time until service is restarted (1-5 sec), but it will eventually start and kill the notification.

Samuel
  • 9,883
  • 5
  • 45
  • 57
AndacAydin
  • 1,416
  • 19
  • 26
  • Thank you very much for your interesting answer, I'm looking for "startForeground" service to disable notification on application killing via multitask manager. If it works, it would be cleaner. – alex Jan 23 '13 at 13:47
  • 2
    @alex using `startForeground` will not work either. The ongoing notification will remain if the service is killed. `stopForeground(true)` needs to be called for the notification to be dismissed, which is impossible when the service is killed. – faizal Jul 10 '14 at 08:43
  • @salehsereshki What didnt work, maybe I can help out? – AndacAydin Dec 17 '15 at 13:38
  • It works for me, Thx, but I don't understand why you do. The service is launched when kill the app? Where do you specify that have to be launched when the app is killed? Thx¡ – Terranology Jan 20 '16 at 14:28
  • 2
    @Terranology sorry just saw your comment: The service is started when you show your sticky notification. So at first they both just live along and happily side by side. But then the app gets killed unexpectedly. The service is being killed too, but the notification is still there. Now the service restarts (Android-Service default behaviour) and kills the notification. Thats the workaround. – AndacAydin May 20 '16 at 18:15
  • it's not working now. can anyone provide a better solution? – Silambarasan Nov 26 '19 at 12:21
37

The ability to swipe apps out of the recent apps list is introduced in Ice Cream Sandwich (API-14).

With the same Android version, we received a special method "onTaskRemoved()" in "android.app.Service". It is get invoked when app is removed from recent apps list.

So, just override "onTaskRemoved()" method to achieve your requirements if you are having any service running.

E.g.:

@Override
public void onTaskRemoved(Intent rootIntent) {
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.cancel(NOTIFICATION_ID);
}

Or simply write & start special service to manage the same.

Deepti
  • 934
  • 12
  • 18
  • i want such more details about this.i want to know exactly when ever the swipe can be happen. – KOUSIK daniel Jul 04 '15 at 12:12
  • 1
    Daniel, if you just want to find your app exists in "Recent Apps List" or not. To check whether swipe can be happen on it ever, then you need to use getRecetTasks() method of ActivityManager in Android API smaller than API-21. While for API-21, please refer one of these links: 1) https://developer.android.com/guide/components/recents.html 2)http://stackoverflow.com/questions/24590533/how-to-get-recent-tasks-on-android-l – Deepti Jul 06 '15 at 09:55
  • 1
    Yeah Thank You @Deepti what i exactly want I want to get notification from my app is swipe from or kill from recent app list even i pressed back button – KOUSIK daniel Jul 07 '15 at 05:56
  • Any Method have to find like public void onTaskRemoved(Intent rootIntent) for below api level 14 – KOUSIK daniel Jul 09 '15 at 04:42
  • As I already specified in my answer, there was no option to kill/ swipe any app from recent list, before API 14. But if you still needs to track, app killed by app manager internally then, you can use the concept of STICKY_SERVICE. Please refer the example given by "AndacAydin" (above) for the same. – Deepti Jul 13 '15 at 09:28
  • This methode works if I add break points in on TaskRemoved(). But is does not work without the breakpoints :S – Tom Bevelander Oct 12 '16 at 15:24
12

You shoud create class extends Application and register activity callbacks, whose call when you close application from multitask pannel.

    public class MyApplication extends Application {



@Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {
            }

        @Override
        public void onActivityStarted(Activity activity) {
        }

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
            if (/*check is all your activities onStop state*/) {
                NotificationManager mNM = (NotificationManager)              getSystemService(NOTIFICATION_SERVICE);
                mNM.cancel(R.id.key_notification_id);
            }
        }
    });
}

}

Roman Trokhymets
  • 1,102
  • 10
  • 10
1

I would suggest You to use Deepti solution (https://stackoverflow.com/a/28584405/619673) which was

@Override
public void onTaskRemoved(Intent rootIntent) {
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.cancel(NOTIFICATION_ID);
}

However, he forget mention one thing. If Your connection to service was by call bindService(), onTaskRemoved() won't be trigged. To handle this You must connect with service by call startService() as in example below:

startService(new Intent(context, CustomerService.class));

Once You call it, You will be able catch onTaskRemoved().

Nothing stands in the way to call both methods startService() and somewhere further bindService().


I was basing on solution Service: onTaskRemoved not called if started with bindService

startService(new Intent(context, CustomerService.class)); // this will allow catch onTaskRemoved
// ...
bindService(new Intent(context, CustomerService.class),
                        mConnection, Context.BIND_AUTO_CREATE);
deadfish
  • 11,996
  • 12
  • 87
  • 136
1

I found that AndacAydin's accepted answer of Jan '13 worked well for me. But, here in Jan '21 (API 30) enough has changed that some revision was needed.

"Step 3: Always create the Service before firing ..." needed revision, mostly because the "setLatestEventInfo" method in Notification wasn't merely deprecated but actually removed!

Here's my update on his step 3, which is working fine for me now in R:

ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className,
        IBinder binder) {
        ((KillBinder) binder).service.startService(new Intent(
                MainActivity.this, KillNotificationsService.class));
        Intent notificationIntent = new Intent(getApplicationContext(),
                MainActivity.class);
        PendingIntent contentIntent = PendingIntent.getActivity(
                MainActivity.this, 0, notificationIntent, 0);
        Notification notification = new NotificationCompat.Builder(
                getApplicationContext(), "someChannel")
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setContentIntent(contentIntent)
                    .build();
        NotificationManager mNM =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mNM.notify(KillNotificationsService.NOTIFICATION_ID, notification);
    }

    public void onServiceDisconnected(ComponentName className) {
    }
};

bindService(new Intent(MainActivity.this,
        KillNotificationsService.class), mConnection,
        Context.BIND_AUTO_CREATE);
Ames
  • 91
  • 1
  • 4
0

I have the same problem myself however I managed to fix it

This is what I did

 public class Sample extends Activity {

 private static final String APP_NOTIFICATION_TAG = "sample";
 private static final int NOTIFICATION_ID = 24;
 private NotificationManager notificationManager;
 private Notification appNotification;

 private AppFinishedExecutingListener appFinishedExecutingListener;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // set the layout and other stuff goes here
    appFinishedExecutingListener.execute(this);
    new Thread() {
        @Override
        public void run() {
            try {
                appFinishedExecutingListener.get();
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        destroyActivityComponent();
                    }
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }.start();

    setupNotification();
 }

 /*
 * Setup this app 
 */
private void setupNotification() {

    Intent intent = new Intent(getApplicationContext(), MainActivity.class);
    // make sure when the user click the notification, this will make sure it will resume the app
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

    // define the action should be performed if the user click the notification i.e. resume our app activity
    PendingIntent pIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), intent, 0);

    // setup the look for this app on  a notification panel
    appNotification  = new NotificationCompat.Builder(this)
            .setContentTitle(getString(R.string.app_name))
            .setContentText("Currently listening to kiribati radio")
            .setSmallIcon(R.drawable.ic_notification_logo)
            .setContentIntent(pIntent)
            .setAutoCancel(true)
            .setOngoing(true).build()
            ;

    notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

}

/*
 * Will add app notification when this activity is paused so the user can   
 * quickly access it or resume this activity easily
 */
private void addAppToNotificationP() {
    notificationManager.notify(APP_NOTIFICATION_TAG, NOTIFICATION_ID,   appNotification);
}

/*
 * This will remove the notification if this activity is resumed
 */
private void removeAppFromNotificationP() {
    notificationManager.cancel(APP_NOTIFICATION_TAG,NOTIFICATION_ID);
}

@Override
protected void onPause() {
    super.onPause();
    addAppToNotificationP();
}

@Override
protected void onResume() {
    super.onResume();
    removeAppFromNotificationP();

}

private void destroyActivityCompletely() {
    onResume();
    finish();
}



}

public class AppFinishedExecutingListener extends AsyncTask<MainActivity, Void, Boolean> {

    private MainActivity main_activity;

    @Override
    protected Boolean doInBackground(MainActivity... params) {
        main_activity = params[0];

        while(!main_activity.isFinishing()) {
            try {
                Thread.sleep(100);
                //Log.i(main_activity.TAG,"listening");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //main_activity.finish();


        return true;
    }


}

If you hold the home button for 2-3 seconds then the multipane comes to the foreground while our activity is currently in the pause state. Therefore we need to bring it to the foreground first and then remove the app notification and finally exit the app

destroyActivityCompletely function will remove the app notification and then kill the activity. This will work if the activity is excited from multipane and etc.

0

I wanted to close all my App notifications,remove badge icons on Logout, removed from recent app list . so for

Xamarin.Android

start service at launcher activity(i am creating notification from here) as

Intent intent = new Intent(this, typeof(PushNotificationService));
this.StartService(intent);

where PushNotificationService is my Android service inside which i have

[Service]
    public class PushNotificationService : Service
        {
            public override IBinder OnBind(Intent intent)
            {
                return null;
            }

        public override void OnCreate()
        {
            base.OnCreate();
        }
        public override void OnTaskRemoved(Intent rootIntent)
        {
            try
            {
                var notificationManager = (NotificationManager)GetSystemService(NotificationService);
                notificationManager.Cancel(0);
                ME.Leolin.Shortcutbadger.ShortcutBadger.RemoveCount(Application.Context);
            }
            catch (System.Exception ex)
            {
            }
        }
    }

this line in OnTaskRemoved(Intent rootIntent) did the trick

 var notificationManager = (NotificationManager)GetSystemService(NotificationService);
                notificationManager.Cancel(0);

where 0 in notificationManager.Cancel(0); is the local notifcation id which we put at the time of building notification.

Annu
  • 449
  • 6
  • 18
0

The question is old but it may help others.

As pointed by others, you can use onTaskRemoved() but it is not called on many devices like Xiaomi, Oppo and other Chinese manufacturers', unless the app is added to whitelist.

To support these devices too, service can be started in foreground. This way, notification is attached to the service. So, the notification will be cleared when the service is killed (by swipping in the multitask panel).

rohitverma
  • 767
  • 2
  • 9
  • 14