49

我管理来自我的应用程序(而不是来自服务)的持续通知。

当我使用“结束”按钮从任务管理器中终止应用程序时,通知消失

当我从多任务面板中删除应用程序时,应用程序被杀死但通知仍然存在

我的问题是:

  • 如何捕获此事件以清除通知?
  • 从多任务面板中删除应用程序时会发生什么?应用程序被销毁但进程仍然存在?正常吗?

作为更新:

我的所有活动都使用以下方法扩展了 MyActivity 类(扩展了 Activity):

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

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

我的应用程序使用方法扩展了 MyApplication 类(扩展了应用程序):

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是所有正在运行的活动的列表

这不是最简单的机制,但我需要它

我应该改用服务吗?


作为新的更新:

在我的 onExit() 方法中,如果我记录调试消息以了解会发生这样的情况:

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

少量日志在两个上出现一次,而不是全部(例如:13/100)

所以,我知道从多任务面板中删除应用程序强制杀死应用程序而不等待关闭方法结束以正确完成......但为什么不处理?!

如何强制正确终止?

4

8 回答 8

46

当主应用程序被杀死时杀死通知。

由于您的通知和您的应用程序在不同的线程中处理,因此通过 MultitaskManager 终止您的应用程序不会终止您的通知。由于您已经正确调查过杀死您的应用程序,因此甚至不一定会导致 onExit() 回调。

那么解决方案是什么?

您可以从您的活动中启动服务。特殊服务有:如果应用进程由于某种原因被杀死,它们会自动重启。因此,您可以通过在重启时终止通知来重用自动重启。

第 1 步创建一个杀死 Simple 的服务。它只是杀死了创建通知并拥有他的特殊活页夹。

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);
    }
}

第 2 步:将其添加到您的清单中: 将其添加到您的 <application> 标记之间的某个位置。

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

第三步:总是在触发通知之前创建服务,并使用静态通知ID。

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);

重新启动服务可能需要一点时间(1-5 秒),但它最终会启动并终止通知。

于 2013-01-17T08:20:40.927 回答
36

Ice Cream Sandwich (API-14) 中引入了将应用程序从最近的应用程序列表中滑出的功能。

使用相同的Android版本,我们在“android.app.Service”中收到了一个特殊的方法“onTaskRemoved()”。当应用程序从最近的应用程序列表中删除时,它会被调用。

因此,如果您正在运行任何服务,只需覆盖“onTaskRemoved()”方法即可满足您的要求。

例如:

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

或者简单地编写并启动特殊服务来管理它。

于 2015-02-18T13:06:48.383 回答
12

您应该创建类扩展应用程序并注册活动回调,当您从多任务面板关闭应用程序时调用该回调。

    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);
            }
        }
    });
}

}

于 2013-01-25T09:12:29.307 回答
1

我建议您使用 Deepti 解决方案(https://stackoverflow.com/a/28584405/619673),它是

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

不过,他忘了提一件事。如果您与服务的连接是通过 call 进行的bindService()onTaskRemoved()则不会被触发。要处理此问题,您必须通过调用与服务连接,startService()如下例所示:

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

一旦你调用它,你将能够 catch onTaskRemoved()

没有什么能阻止调用这两种方法startService()和更远的地方bindService()


我是基于解决方案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);
于 2019-04-07T11:44:45.477 回答
0

我自己也有同样的问题,但是我设法解决了

这就是我所做的

 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;
    }


}

如果您按住主页按钮 2-3 秒,那么当我们的活动当前处于暂停状态时,多窗格将进入前台。因此我们需要先把它带到前台,然后移除应用通知,最后退出应用

destroyActivityCompletely 函数将删除应用程序通知,然后终止活动。如果活动从多窗格等激发,这将起作用。

于 2015-11-26T00:32:08.840 回答
0

我想关闭我所有的应用通知,删除注销时的徽章图标,从最近的应用列表中删除。因此对于

Xamarin.Android

在启动器活动中启动服务(我正在从这里创建通知)为

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

PushNotificationService我的安卓service在哪里

[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)
            {
            }
        }
    }

这条线 OnTaskRemoved(Intent rootIntent)成功了

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

其中 0 innotificationManager.Cancel(0);是我们在构建通知时放置的本地通知 ID。

于 2019-01-31T06:15:22.677 回答
0

这个问题很老,但它可能对其他人有帮助。

正如其他人所指出的,您可以使用 onTaskRemoved() 但它不会在小米,Oppo 和其他中国制造商的许多设备上调用,除非该应用程序被添加到白名单。

为了也支持这些设备,可以在前台启动服务。这样,通知就会附加到服务上。因此,当服务被终止时(通过在多任务面板中滑动),通知将被清除。

于 2019-07-10T22:44:38.400 回答
0

我发现 AndacAydin 接受的 13 年 1 月答案对我来说效果很好。但是,在 21 年 1 月(API 30)已经发生了足够的变化,需要进行一些修订。

“第 3 步:在触发之前始终创建服务……”需要修改,主要是因为 Notification 中的“setLatestEventInfo”方法不仅已被弃用,而且实际上已被删除!

这是我对他的第 3 步的更新,现在在 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);
于 2021-01-11T15:56:35.997 回答