ListenableWorker无法移除通知图标。

9

我正在使用ListenableWorker执行后台任务。 同时,我希望操作系统知道我的服务的重要性,因此我调用

 setForegroundAsync(new ForegroundInfo(WorkerConstants.NOTIFICATION_FOREGROUND_ID,
 builder.build()));

正如谷歌文档所建议的那样。

但是,当我的服务停止或取消时,我仍然可以看到前台服务通知,并且我无法将其删除。

此外,我向这个通知添加了取消按钮,但它没有任何作用,我无法关闭它:

PendingIntent intent = WorkManager.getInstance(getApplicationContext())
                            .createCancelPendingIntent(getId());
                    builder.addAction(R.drawable.ic_redesign_exit,"Stop",intent);

有什么建议吗?


1
你解决过这个问题吗?我也遇到了同样的问题,但是无论我做什么都无法解决它。 - n00b
好的。请看我下面的答案。 - Sergei Yendiyarov
5个回答

9
我在Google跟踪器上提交了一个问题,但发现完全是错误的。
官方回应如下:
查看您的样例应用程序,您正在引入一个 ListenableFuture 的完成和主循环之间的竞争条件。 即使没有使用 REPLACE,您其中一个通知更新也是在将 Worker 标记为成功之后发布的。 然而,您可以通过等待 setForegroundAsync() 的调用完成来避免竞争条件(因为它也返回一个 ListenableFuture)。一旦您这样做了 - 就永远不会有一个在 Worker 完成后显示的通知了。 我还将提出改进意见,以便在开发人员犯错时我们可以自动执行此操作。
try {
    // This ensures that you waiting for the Notification update to be done.
    setForegroundAsync(createForegroundInfo(0)).get();
} catch (Throwable throwable) {
    // Handle this exception gracefully
    Log.e("FgLW", "Something bad happened", throwable);
}

1
这对我来说不起作用。无论我做什么,它仍然在返回结果后尝试完成。 - Brill Pappin

2
如果您有一些可以立即结束工作的分支,则需要为通知添加1秒延迟以便其传播。setForeground声称是同步的,但显然不是 - 总有时间需要显示通知。而在未显示通知时调用取消操作无效。这显然是一种竞态条件,谷歌没有处理(我甚至不确定应用程序是否能够处理这种情况)。
扩展解释:
我有一段代码,在某些情况下可能会立即返回。Worker看起来像这样:
setForeground(getForegroundInfo())
loggingRepository.uploadLogs()

那就这样,再简单不过了。但是,日志记录存储库在启动时进行了这个检查:
if (!userIdentity.isLoggedIn) return

如果用户未登录-则不执行任何操作。如果出现这种情况,通知将保持不变。我所需要做的就是添加延迟,就像这样(它是一个CoroutineWorker)。

setForeground(getForegroundInfo())
loggingRepository.uploadLogs()
delay(1000)

0
最新版本的Work Manager库中有一个名为setForeground()的ktx挂起函数,它会在监听器上执行.await()并将其变成同步模式,如果您使用协程的话。

它实际上并没有起作用。我正在这样做,但仍然收到错误。 - Brill Pappin

0

官方文档如下:

ListenableWorker现在支持setForegroundAsync() API,而CoroutineWorker支持暂停的setForeground() API

因此,解决上述问题的方法是在返回Result.success()之前执行繁忙的工作。否则,将抛出此异常。

调用setForegroundAsync()必须在ListenableWorker通过返回Result实例信号完成工作之前完成。

请检查我的代码片段以获取您的解决方案:

    public static void startCloudWorker() {
    OneTimeWorkRequest oneTimeWorkRequest =
            new OneTimeWorkRequest.Builder(CloudWorker.class)
                    .setInitialDelay(..., TimeUnit.MILLISECONDS)
                    ...
                    .build();

    WorkManager workManager = getWorkManager();
    workManager.enqueue(oneTimeWorkRequest);
}


public class CloudWorker extends Worker {
    public CloudWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        final ForegroundInfo foregroundInfo = createForegroundInfo(...)

        setForegroundAsync(foregroundInfo);
        
        // Some busy job here...

        return Result.success();
    }
}

-2
更好地忘记`setForegroundAsync`,采用这里解释的传统方法:https://developer.android.com/training/notify-user/build-notification 以下是我使用的内容:
    Context context = getApplicationContext();
    String title = context.getString(R.string.str_synchronisation);
    String cancel = context.getString(R.string.str_cancel_synchronisation);
    // This PendingIntent can be used to cancel the worker
    PendingIntent intent = WorkManager.getInstance(context)
            .createCancelPendingIntent(getId());
    //notificationManager.cancel(notification_ID);

    NotificationCompat.Builder builder = new NotificationCompat.Builder(context, notif_ch_id)
            .setSmallIcon(R.drawable.ic_sync_black_24dp)
            .setContentTitle(title)
            .setContentText("Use this Content")
            .setOngoing(true)
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
//                // Add the cancel action to the notification which can
//                // be used to cancel the worker
            .addAction(android.R.drawable.ic_delete, cancel, intent);
    notificationManager.notify(notification_ID, builder.build());

不要忘记保留通知管理器的实例:

notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);

请更新您的通知:

builder.setContentText(context.getString(R.string.str_veuiller_patienter)
                + ", fichier " + String.valueOf(i + 1) + totalfiles);
notificationManager.notify(notification_ID, builder.build());

使用以下代码取消您的通知:

notificationManager.cancel(notification_ID);

3
尽管在某些情况下有用,但此答案并未解决原始问题。当让WorkManager工人启动长时间运行的前台服务时,使用 setForegroundAsync 是强制性的。管理自己的通知可摆脱通知,但仍无法正确使用前台服务功能。这似乎是WorkManager库中的一个错误... - necavit

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