Android - 当屏幕在前台服务时处理程序重复任务停止执行
Android - Handler repeating task stops executing while the screen is on at Foreground Service
首先,这是一个很长的问题,但只针对一个主题,很抱歉我不能分享任何代码,因为它是我们公司的一个项目并且是 class 化的。我们正在使用仅在 100 毫秒内执行任务的前台服务。在我问这个问题之前,我们使用了多种方法在短时间内执行代码,如下:
- 其中包含 "Thread.sleep" 的线程(不是最好的方法,但这是我们的第一次尝试,结果不一致),
- 线程"Object.wait"(与上面相同的结果),
- Timer(也是不一致,一段时间后停止执行),
- ScheduledThreadPoolExecutor也有以上问题
在我的 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 永远不会干扰该过程(希望如此)。
首先,这是一个很长的问题,但只针对一个主题,很抱歉我不能分享任何代码,因为它是我们公司的一个项目并且是 class 化的。我们正在使用仅在 100 毫秒内执行任务的前台服务。在我问这个问题之前,我们使用了多种方法在短时间内执行代码,如下:
- 其中包含 "Thread.sleep" 的线程(不是最好的方法,但这是我们的第一次尝试,结果不一致),
- 线程"Object.wait"(与上面相同的结果),
- Timer(也是不一致,一段时间后停止执行),
- ScheduledThreadPoolExecutor也有以上问题
在我的 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 永远不会干扰该过程(希望如此)。