取消(并隐藏)Android通知的程序化方法

5

我有一个与通知系统配合使用的服务。当服务停止(和/或应用程序停止)时,通知应该被取消(即不再显示在状态栏上)。

查看提供的Android Dev指南中,我没有找到关闭通知的任何信息。

在SO上,我发现了许多问题。

1.

总结@nipun.birla对如何取消Android通知的答案:

尝试以下步骤来取消通知:

  1. 使用NotifcationManager.cancel(int)notificationID

  2. 使用NotificationManager.cancel(String, int)notificationIDnotificationTag

  3. 作为最后一次尝试,NotificationManager.cancelAll()

然而,并没有提到如果这些方法都不起作用应该怎么做。

2.

应该使用取消所有建议

3.

这个SO线程包含了一个很好的Started Service示例,其中包括了与服务生命周期相关联的通知实现(通知也被杀死)- 有关Started Service详细信息,请参见此处

4.

在这里提出了建议,删除与通知相关联的PendingIntent

5.

还有一些问题和解决方案反映了上述相同的信息: 请参见此处,以及此处,还有更多...

6.

关于编程中以编程方式隐藏状态栏中通知图标的非常有趣的问题和解决方案


我的问题

现在应该很明显了,我的通知不会自动取消。

实施:

请参阅下面的完整实现,虽然我将发布辅助类和核心函数的一般用法

- 创建通知

private Context context;
private NotificationManager notificationManager;
private NotificationChannel notificationChannel;
private NotificationCompat.Builder notificationBuilder;

public NotificationHelper(Context context) {
    this.context = context;

    // Init notification

    // onNotificationCreate()
    {
        // get notification manager system service
        notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // Create notification channel
            createNotificationChannel();

            //Register notification channel with notification manager
            notificationManager.createNotificationChannel(notificationChannel);
        }

    }

    // Init Notification Builder

    // createNotificationChannel() 
    {
        Log.d(TAG, "createNotificationChannel: Creating notification channel");

        // Define notification channel ID, Channel Name and description
        String channelName = BuildConfig.FLAVOR.concat(" Notifier");
        String channelDescription = context.getString(R.string.notification_description);

        // Create notification channel
        notificationChannel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW);
        // Set description of notification channel
        notificationChannel.setDescription(channelDescription);
    }
}

这是通过调用new NotificationHelper(getApplicationContext)来调用的,其中上下文是应用程序上下文(也用作许多其他函数的上下文)。

我的辅助类背后的方法论是简单地使用方法链接,从而实现更加美观的创建、修改和取消通知的方法。

- NotificationHelper的使用:

通过调用setTextContent(String)设置通知contentText

public NotificationHelper setTextContent(String text){
    notificationBuilder.setContentText(text);
    return this;
}

通过`setTitle(String)`设置contentTitle

public NotificationHelper setTitle(String format) {
    notificationBuilder.setContentTitle(format);
    return this;
}

通过调用 setStatusIcon(int) 来设置 smallIcon(状态图标):

public NotificationHelper setStatusIcon(int res_id) {
    notificationBuilder.setSmallIcon(res_id);
    return this;
}

最后,更新通知以显示结果设置的方法如下:
public void update() {
    Log.d(TAG, "update: Updating notification");
    Notification notification = notificationBuilder.build();

    // Set notification flags
    notification.flags |= Notification.FLAG_NO_CLEAR;
    notification.flags |= Notification.FLAG_ONGOING_EVENT;
    notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;

    // Notify update
    notificationManager.notify(TAG, notificationId, notification);
}

- 取消通知

正如预期的那样,取消通知只需调用cancelNotification()即可:

public void cancelNotification() {
    Log.d(TAG, "cancelNotification: Cancelling notification");
    notificationManager.cancel(TAG, notificationId);
}

然而,这对取消通知没有影响。

当通知被取消时,在取消之前发生了以下情况。

完成所有这些操作后,通知仍然存在。

我尝试过以下方法:

  • notificationManger.cancel(int)
  • notificationManger.cancel(String, int)
  • notificationManger.cancelAll

但这并没有起作用,所以我想出了一些创意方法:

此外: 创建一个单独的NotificationManager方法来发布这些更新(即不在此处设置标志,但使用相同的notificationmanager
public void updateCancelable() {
    Log.d(TAG, "update: Updating notification to cancel");
    Notification notification = notificationBuilder
            .setContentIntent(null)
            .setOngoing(false)
            .setAutoCancel(true)
            .build();
    // Notify update
    notificationManager.notify(TAG, notificationId, notification);
}
  • 调用setContentIntent(null)并重新通知以更新,来清除setContentIntent()

  • 我还尝试使用setAutoCancel(true)创建了一个新的可以取消的通知,并使用setOngoing(false)设置了进行状态,然后重新通知以更新

这也没有帮助。我可能漏掉了什么吗?

我还应该提到:在调试我的应用程序时,我注意到当我退出应用程序(停止绑定服务并调用cancelNotification())时,应用程序不应再运行,尽管Android Studio仍保持有一个活动的调试会话,就像应用程序仍在运行一样。不确定这是否与此有关


NotificationHelper类(完整实现)

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.connectedover.BuildConfig;
import com.connectedover.R;
import com.connectedover.listeners.NotificationUpdateListener;

/**
 * Class aimed at providing helper method for creating, maintaining and destroying notifications in conjunction with {@link de.blinkt.openvpn.core.OpenVPNService}
 *
 * @author cybex
 * @since 1.5.1
 */
public class NotificationHelper implements NotificationUpdateListener {

    private static final String TAG = NotificationManager.class.getSimpleName();
    private static final String channelId = BuildConfig.APPLICATION_ID.concat(".").concat(TAG);
    private static final int notificationId = 42;

    private Context context;
    private NotificationManager notificationManager;
    private NotificationChannel notificationChannel;

    private NotificationCompat.Builder notificationBuilder;

    public NotificationHelper(Context context) {
        this.context = context;

        // Init notification
        onNotificationCreate();

        // Init Notification Builder
        createBasicNotification();
    }

    /**
     * Initialize {@link NotificationChannel} and register channel with {@link NotificationManager} service if API is Android Orea (API 26 or higher), else initializes the notification manager
     */
    private void onNotificationCreate() {
        Log.d(TAG, "onNotificationCreate: Initializing notification helper");

        // get notification manager system service
        notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // Create notification channel
            createNotificationChannel();

            //Register notification channel with notification manager
            notificationManager.createNotificationChannel(notificationChannel);
        }
    }

    /**
     * Creates a notification channel required by devices running Android SDK 26 and higher.
     * The notification  channel is set to {@link NotificationManager#IMPORTANCE_LOW} which should have no sound and appear right at the top of the status bar
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    private void createNotificationChannel() {
        Log.d(TAG, "createNotificationChannel: Creating notification channel");

        // Define notification channel ID, Channel Name and description
        String channelName = BuildConfig.FLAVOR.concat(" Notifier");
        String channelDescription = context.getString(R.string.notification_description);

        // Create notification channel
        notificationChannel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW);
        // Set description of notification channel
        notificationChannel.setDescription(channelDescription);
    }

    /**
     * Creates a basic notification using {@link android.support.v4.app.NotificationCompatBuilder} for use throughout the application
     */
    private void createBasicNotification() {
        // Instantiate Notification Builder
        notificationBuilder = new NotificationCompat
                .Builder(context, channelId)
                .setContentTitle(context.getString(R.string.app_name))
                .setSmallIcon(R.drawable.ic_logo_disconnected)
                .setWhen(System.currentTimeMillis())
                .setAutoCancel(false)
                .setOngoing(true);
    }

    /**
     * Set the pending intent of a clickable {@link android.app.Notification} held by {@link NotificationHelper#notificationBuilder}
     * @param pendingIntent Pending intent to connect to activity
     * @return returns an instance of {@link NotificationHelper}
     */
    public NotificationHelper setPendingIntent(PendingIntent pendingIntent){
        Log.d(TAG, "setPendingIntent: Setting notification Pending intent");
        notificationBuilder.setContentIntent(pendingIntent);
        return this;
    }

    /**
     * Updates the notification which is displayed for the user.
     */
    public void update() {
        Log.d(TAG, "update: Updating notification");
        Notification notification = notificationBuilder.build();

        // Set notification flags
        notification.flags |= Notification.FLAG_NO_CLEAR;
        notification.flags |= Notification.FLAG_ONGOING_EVENT;
        notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;

        // Notify update
        notificationManager.notify(TAG, notificationId, notification);
    }

    /**
     * Updates the notification {@link NotificationHelper#notificationBuilder} with new text and displays it to the user
     *
     * @param text new text to display
     * @return returns current {@link NotificationHelper} instance for method chaining.
     */
    public NotificationHelper setTextContent(String text){
        notificationBuilder.setContentText(text);
        return this;
    }

    @Override
    public void onUpdate(String update) {
        Log.d(TAG, "onUpdate: updating notification via callback");
        this.setTextContent(update)
                .update();
    }

    /**
     * Sets a new icon for the notification displayed to the user
     * @param res_id icon resource
     * @return current instance
     */
    public NotificationHelper setLargeIcon(int res_id) {
        notificationBuilder.setLargeIcon(ImageUtils.toBitmap(context, res_id));
        return this;
    }

    /**
     * Sets a new icon for the notification displayed to the user show in the status bar (i.e. the small icon)
     * @param res_id icon resource
     * @return current instance
     */
    public NotificationHelper setStatusIcon(int res_id) {
        notificationBuilder.setSmallIcon(res_id);
        return this;
    }

    public NotificationHelper setTitle(String format) {
        notificationBuilder.setContentTitle(format);
        return this;
    }

    /**
     * Cancels the application notification
     */
    public void cancelNotification() {
        Log.d(TAG, "cancelNotification: Cancelling notification");
        notificationManager.cancelAll();
    }
}

1
我建议去掉Notification.FLAG_FOREGROUND_SERVICE。如果您想要一个前台服务,请在Service上使用startForeground()stopForeground()。我还建议使用NotificationCompat上的方法,而不是直接操作Notification字段(例如flags)。我没有看到很多人在通知中使用标签,所以您可能考虑去掉它。cancel()cancelAll()通常可以工作;您需要确定代码中的错误在哪里。 - CommonsWare
谢谢您的建议,我会尝试并汇报结果。 - CybeX
@CommonsWare 请注意:NotificationCompat 自 API 26 (Android O) 起已被弃用 - https://dev59.com/WlcO5IYBdhLWcg3wpjMz - CybeX
@CommonsWare 您移除 Notification.FLAG_FOREGROUND_SERVICE 位标志的解决方案解决了问题。请问您能否将此作为解决方案发布吗? - CybeX
1个回答

2
如果您想将您的Notification用作前台服务的一部分,而不是直接操作标志,请在您的Service上使用startForeground()stopForeground()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接