两个或更多前台通知带有进度,在更新其进度时相互替换。

3

我有一个服务,可以在前台运行上传任务,同时在通知中显示进度。由于用户可能会使用不同ID请求进行多次上传,因此将运行两个或更多前台服务。一切都很好,但我想用这段代码显示所有任务的通知和进度。

 NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_DEFAULT)
            .setSmallIcon(R.drawable.ic_file_upload_white_24dp)
            .setContentTitle(getString(R.string.app_name))
            .setContentText(caption)
            .setProgress(100, percentComplete, false)
            .setContentInfo(String.valueOf(percentComplete +"%"))
            .setOngoing(true)
            .setAutoCancel(false);

    NotificationManager manager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    manager.notify(code, builder.build());

    startForeground(code,builder.build());

但不幸的是,通知只会被覆盖,但如果我删除startForeground(),那么一切都能正常工作,根据正在进行的任务数量显示多个进度通知,但系统可能会在内存不足时杀死进程,这就是为什么我想让它在前台运行。那么我要如何在前台显示与正在进行的任务数量相等的通知呢?


嗨,manager.notify(code, builder.build()); 这里的code是什么?这个code应该是唯一的。你在两个方法中传递了相同的code。这就是问题所在。 - Grishma Ukani
好的,谢谢。我会尝试生成唯一的ID,但是我还需要在这里使用通知管理器吗?还是只用通知构建器就足够了? - Mihae Kheel
4个回答

2
最后我终于搞清楚了如何做到这一点。首先,我需要在onCreate中稍微延迟一下运行它。
 @Override
public void onCreate (){
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
                startForeground(requestCode, getMyActivityNotification("",completedParts,totalSize));
        }
    },500);

}

然后创建一个通知提供者方法。
//Notification provider
private Notification getMyActivityNotification(String caption, long completedUnits, long totalUnits){

    int percentComplete = 0;
    if (totalUnits > 0) {
        percentComplete = (int) (100 * completedUnits / totalUnits);
    }

    //Return the latest progress of task
    return new NotificationCompat.Builder(this, CHANNEL_ID_DEFAULT)
            .setSmallIcon(R.drawable.ic_file_upload_white_24dp)
            .setContentTitle(getString(R.string.app_name))
            .setContentText(caption)
            .setProgress(100, percentComplete, false)
            .setContentInfo(String.valueOf(percentComplete +"%"))
            .setOngoing(true)
            .setAutoCancel(false)
            .build();

}

那么更新通知进度应该与调用前台分开。

/**
 * Show notification with a progress bar.
 * Updating the progress happens here
 */
protected void showProgressNotification(String caption, long completedUnits, long totalUnits, int code) {

    createDefaultChannel();
    mCaption = caption;
    requestCode = code;
    completedParts = completedUnits;
    totalSize = totalUnits;

    NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (mNotificationManager != null) {
        //Update the notification bar progress
        mNotificationManager.notify(requestCode,  getMyActivityNotification(caption,completedUnits,totalUnits));
    }
}

2
在调用 startForeground 之前,您需要调用 ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_DETACH) 来保留旧的通知。 以下是一个实用程序类,可用于前台服务中管理通知:
class ForegroundNotifications(
    private val context: Context,
    private val foregroundServiceHelper: ForegroundServiceHelper,
    private val notificationService: NotificationService
) {

    var activeNotificationId: Int? = null
        private set

    val notificationIds: Set<Int>
        get() = entries.keys

    val isEmpty: Boolean
        get() = entries.isEmpty()

    private val entries: LinkedHashMap<Int, Notification> = LinkedHashMap()

    fun notifyAndDetachPrevious(notificationId: Int, notification: Notification) {
        synchronized(this) {
            foregroundServiceHelper.startForegroundAndDetachPreviousNotification(
                notificationId,
                notification
            )

            entries[notificationId] = notification
            activeNotificationId = notificationId
        }
    }

    fun cancel(notificationId: Int) {
        synchronized(this) {
            if (notificationId == activeNotificationId) {
                val newActiveNotificationId = entries.keys.findLast { it != activeNotificationId }
                val notification = entries[newActiveNotificationId]

                if (newActiveNotificationId != null && notification != null) {
                    notifyAndDetachPrevious(newActiveNotificationId, notification)
                }
            }

            entries.remove(notificationId)

            if (isEmpty) {
                foregroundServiceHelper.stopForeground()
            } else {
                notificationService.cancel(context, id = notificationId)
            }
        }
    }

}

interface NotificationService {

    fun createDefaultChannel(context: Context)

    fun notify(context: Context, tag: String? = null, id: Int, notification: Notification)

    fun cancel(context: Context, tag: String? = null, id: Int)

}

interface ForegroundServiceHelper {

    fun startForegroundAndDetachPreviousNotification(
        notificationId: Int,
        notification: Notification
    )

    fun stopForeground()

}

class ForegroundServiceHelperImpl(
    private val service: Service
) : ForegroundServiceHelper {

    override fun startForegroundAndDetachPreviousNotification(
        notificationId: Int,
        notification: Notification
    ) {
        ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_DETACH)
        service.startForeground(notificationId, notification)
    }

    override fun stopForeground() {
        ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_REMOVE)
    }

}

2
你没有说如何使用它? - Faisal Khan

1

为不同的通知使用不同的id。在这段代码中,您正在使用相同的默认id,因此新通知正在替换旧通知。


那么在前台下,是否可以显示来自不同任务的多个通知? - Mihae Kheel
是的,你只需要传递不同的ID即可。 - prasid444

0

您应该传递不同的Id以获取不同的通知。

在您的代码中:

使用getId()方法为每个通知获取不同的Id:

private static final AtomicInteger c = new AtomicInteger(0);
 public static int getID() {
        return c.incrementAndGet();
    }

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_DEFAULT)
            .setSmallIcon(R.drawable.ic_file_upload_white_24dp)
            .setContentTitle(getString(R.string.app_name))
            .setContentText(caption)
            .setProgress(100, percentComplete, false)
            .setContentInfo(String.valueOf(percentComplete +"%"))
            .setOngoing(true)
            .setAutoCancel(false);

    NotificationManager manager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    manager.notify(getId(), builder.build());

    startForeground(getId(),builder.build()); // you need to pass different id here. Or i think this method is not there.

对于 Android O 版本,您需要通知渠道:

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications",                      NotificationManager.IMPORTANCE_HIGH);

            // Configure the notification channel.
            notificationChannel.setDescription("Channel description");
            notificationChannel.enableLights(true);
            notificationChannel.setLightColor(Color.RED);
            notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
            notificationChannel.enableVibration(true);
            notificationChannel.setImportance(NotificationManager.IMPORTANCE_HIGH);
            notificationChannel.setSound(notification, Notification.AUDIO_ATTRIBUTES_DEFAULT);
            notificationManager.createNotificationChannel(notificationChannel);
        }

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);

        notificationBuilder.setAutoCancel(true)
                .setDefaults(Notification.DEFAULT_ALL)
                .setWhen(System.currentTimeMillis())
                .setSound(notification)
                .setContentIntent(pendingIntent)
                .setContentText(message)
                .setContentTitle(getResources().getString(R.string.app_name))
                .setSmallIcon(R.drawable.ic_visualogyx_notification)
                .setStyle(new NotificationCompat.BigTextStyle().bigText(message).setBigContentTitle(getResources().getString(R.string.app_name)))
                .setTicker("Hearty365")
                .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher_round));

        notificationManager.notify(getID(),notificationBuilder.build());

这样做比使用这个更合适吗?int requestCode =(“请求上传”+ System.currentTimeMillis())。hashCode(); - Mihae Kheel
通知仍然无法正常工作,同时在两个任务之间切换。 - Mihae Kheel
因为您在manager.notify()方法和startForeground()方法中传递了相同的Id。 - Grishma Ukani
1
是的,你需要为 Android O 创建通知渠道。我会更新我的代码。 - Grishma Ukani
但现在我遇到了一个重复通知的问题,其中一个会在进度改变时不断弹出。 - Mihae Kheel
显示剩余10条评论

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