Android - 当屏幕在前台服务时处理程序重复任务停止执行

Android - Handler repeating task stops executing while the screen is on at Foreground Service

首先,这是一个很长的问题,但只针对一个主题,很抱歉我不能分享任何代码,因为它是我们公司的一个项目并且是 class 化的。我们正在使用仅在 100 毫秒内执行任务的前台服务。在我问这个问题之前,我们使用了多种方法在短时间内执行代码,如下:

在我的 phone (HUAWEI P20 Lite) 上,在尝试了这些方法之后,我们最终决定使用 Handler with a HandlerThread 因为工作不需要与 UI 线程交互。 (并非总是如此,但我们正在为其使用单独的处理程序。)在每种方法之间,这似乎是最一致的方法,但我们不知道它是否取决于 phone、制造商或其他任何东西,我的phone 停止使用 postDelayed 执行循环处理程序,直到我以某种方式与应用 UI 交互,例如:在屏幕打开时触摸通知。是的,我不是在谈论单击通知本身,而是触摸或展开详细信息文本再次启动处理程序。这意味着,我认为我的 phone 正在尝试通过暂停后台执行来节省电量。

现在,我们只想在屏幕打开时 运行 这个方法,因为我们已经通过 "a screen on-off broadcast receiver" 暂停了处理程序本身,它通过删除运行nable,但点亮后,接收到广播,但即使"Handler.post()"执行,也不会运行里面的运行nable,因此它永远不会循环。

为了给您一些上下文,这就是我们遵循的逻辑:

我们用类似下面的代码打开一个前台服务:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
    getApplicationContext().startForegroundService(
        new Intent(getApplicationContext(),
                   MyService.class));
}
else
{
    getApplicationContext().startService(
         new Intent(getApplicationContext(), MyService.class));
}

然后,在服务内部,我们执行如下操作:

public int onStartCommand()
{
    // notification stuff

    HandlerThread thread = new HandlerThread("myThread");
    thread.start();

    Handler handler = new Handler(thread.getLooper());
    handler.post(new Runnable() 
    {
         // do stuff
         // this runnable stops executing after some time. This is where the problem lies.
         handler.postDelayed(this, 100);
    })

    return START_STICKY;
}

onStartCommand 内部的处理程序不稳定,有时不考虑延迟,有时根本不执行。现在,不考虑延迟不是问题,因为我们不必那么一致,因为我们的计算不依赖于经过的时间或其他东西,但如果它停止执行(在这种情况下,它确实如此),那就是问题开始了。

为了处理这个问题,我们将使用 Android Jetpack 提供的全新 class,WorkManager,它非常适合检查处理程序的状态。为了了解线程是否处于活动状态,我们在处理程序中注册了代码的最后执行时间,并将通过 worker class 访问它。现在,如果处理程序挂了,我们想唤醒这些处理程序。如何才能做到这一点?因为请记住,这是在屏幕打开时发生的。

编辑:这里有一些关于线程执行情况的日志。

//2018-12-19 11:35:15.048 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
//2018-12-19 11:35:15.154 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
2018-12-19 11:35:15.262 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
2018-12-19 11:35:45.262 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
//2018-12-19 11:35:45.365 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
//2018-12-19 11:35:45.468 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true

有什么建议吗?有什么解决方法吗?感谢您的每一个帮助和评论,非常感谢。

它会发生在奥利奥之前的设备上吗?

如果您使用 startForegroundService 启动服务,您应该立即在 onStartCommand 上调用 setForeground。否则服务会在短时间内终止。

此外,您的处理程序引用是 onStartCommand 的本地引用,这可能就是它不会持续存在的原因。

找到问题所在,除了我的设备(可能还有其他系统上有 EMUI 的设备)外,其他地方都运行顺利,这显然对电源管理非常严格。

这是日志:

2018-12-19 16:45:52.943 12619-12822/com.example.something D/ThreadHelper: MainThread looping.
2018-12-19 16:45:52.986 1699-2028/? I/ash: com.example.something skips not important notification
2018-12-19 16:45:52.996 1699-2028/? I/ash: com.example.something { doze duration=40061 UptimeDuration=40061 } transition to: hibernation reason:
2018-12-19 16:45:52.996 1699-2028/? I/ash: perform hibernation actions: com.example.something

我的设备自行决定可以静默停止带有不重要通知的进程,因此我通过 NotificationManager.IMPORTANCE_MAX[= 增加了通知的重要性20=]。现在,它根本不会停止工作。

如果您遇到类似情况并想确保发出通知对您来说还不够,那么在华为设备上有一个可以通过设置 --> 电池 --> 启动打开的设置。禁用您的自动应用程序电源管理并将其设为手动,因此 OS 永远不会干扰该过程(希望如此)。