【问题标题】:BroadcastReceiver starting Activity with random delayBroadcastReceiver 以随机延迟启动 Activity
【发布时间】:2018-12-03 10:23:35
【问题描述】:

我正在制作闹钟,但我遇到了一个严重的问题,BroadCastReceiver总是准确地及时调用,我做了一些工作,比如获取额外的意图,不重也不长,然后我调用警报活动。

那里有非常奇怪的行为,很多时候它都可以正常工作,但有时活动调用会延迟,我说的是1-13分钟到目前为止,我的测试对于警报应用程序来说是完全不可接受的。 当设备从打盹模式唤醒时会发生这种情况。

我正在使用setExactAndAllowWhileIdle(),它在调用接收器时确实很有魅力,但从接收器开始的活动却没有。

这是接收方代码的一部分:

@Override
public void onReceive(Context context, Intent intent) {

    Log.d("Ben", "Alarmreceiver HELLO");

[...]

Log.d("Ben", "Alarmreceiver CALLING WAKE SCREEN NOW");

        if (wait) {
            new Thread(new Runnable() {
                @Override
                public void run() {

                    Log.d("Ben", "AlarmReceiver: Alarm still running, wait for finsihing...");

                    try {
                        Thread.sleep(3_000);
                    } catch (Exception e) {
                        context.startActivity(new Intent("package.action.alarm").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
                    }
                    context.startActivity(new Intent("package.action.alarm").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
                }
            }).start();
        }

        else
            context.startActivity(new Intent("package.action.alarm").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));

        Log.d("Ben", "Call done.");
    }
//end of receiver

这是我从 LogCat 得到的:

06-24 20:50:02.623 6527-6527/package D/Ben: Alarmreceiver HELLO
06-24 20:50:02.753 6527-6527/package D/Ben: Alarmreceiver CALLING WAKE SCREEN NOW
06-24 20:50:02.794 6527-6527/package D/Ben: Call done.
06-24 20:55:09.129 6527-6527/package D/Ben: AlarmActivity.onCreate()

如您所见,命令和实际活动开始之间有 5 分钟的延迟!

可以看到“调用完成”Log.d 在不到 200 毫秒后到达。 runnable 应该不是问题,因为在这种情况下它甚至还没有启动。 (如果一个正在运行,它只会等待先前运行的警报结束)

AlarmActivity.onCreate() 的 Log.d 是 AlarmActivity 中的第一条语句,紧接着我获得了唤醒锁。

我试过了:intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);,因为我在另一篇文章中找到了它,但行为完全没有变化。

这真是令人沮丧 - 有谁知道为什么会发生这种情况或我该如何解决?

编辑

现在我用以下代码交换了新线程:

final PendingResult result = goAsync();

    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            context.startActivity(new Intent("package.action.alarm").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));

            Log.d("Ben", "Runnable done.");
            result.finish();
        }
    }, 2_000);

一切似乎工作正常。 我今天进行了 5 次长期测试,每次都设置了 +50 或 60 分钟的警报,以确保系统确实处于打盹模式(我不相信 adb force doze 模式):

goAsync 的前 3 次运行非常及时。

第四次运行,我将代码改回线程而没有明确地异步,再次迟到了 4 分钟以上。

postDelayedgoAsync 的第五次测试再次完美地进行了。

我现在认为这可以可靠地工作,只要它不会再次响起为时已晚。 千感谢乔纳斯!我几个月来一直在与这个问题作斗争,认为一个新的线程就足够异步了;)

编辑 2:

我现在已经对所有这些进行了 1 周的测试,但它仍然不能很好地工作。 在我将 runnable 更改为:

Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                context.startActivity(new Intent("package.action.alarm").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
                Log.d("Ben", "Activity has been called.");

// give an extra 4.5 sec window to start activity and alarm stream...
                Handler h = new Handler();
                h.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        result.finish();
                        Log.d("Ben", "Allowing Rec. finish now...");
                    }
                }, 4_500);
            }
        }, 2_000);

它工作正常,但不是 100%。 (2 秒是完成一个已经在运行的警报,而 4.5 秒我希望给 Activity 和 Service 足够的时间来启动流,然后它会正常工作)

附加信息:

接收器正在启动一个活动,第一个动作是获取唤醒锁(wifi 和电源)并强制屏幕打开。 之后我进行电话处理,然后我启动一个前台服务,它将播放音乐。

我到处都有 Log.d 并且可以看到,有时,当流缓冲时间过长 - 以至于 BR 中的 4.5 秒已经过去时,设备进入睡眠状态并且直到随机播放音乐才开始播放时间又过去了(我猜是维护窗口)即使已经获得了唤醒锁。

这与 Runnable 无关,我现在发现了。我删除了它并在 BR 结束时直接调用了 Activity,导致在 Activity 启动过程中几乎肯定会延迟几分钟。无需异步 - 它会更快地入睡...

当 BR 完成时,我应该如何从广播接收器开始一个没有睡着/打瞌睡的 Activity?

这对我来说似乎是一个错误。或者我错过了一些非常基本的东西。 屏幕一直亮着,但有时会在该延迟(几分钟)之后启动前台流媒体服务。

当我从 BR 启动前台服务时,也会发生同样的事情。前台服务有时会在 onCreate 中间停止,因为 BR 正在完成并且设备似乎处于睡眠状态(打瞌睡)......

我真的很绝望。

当我给 BR 中的第二个可运行文件 7.5 而不是 4.5 秒时,我可以注意到 Google 控制台中的 ANR。 (虽然我相信警报当时工作得很好)。

解决这个问题的正确方法是什么?启动 asyncTask?

编辑 3:

乔纳斯,再次感谢您的回答 - 我整周尝试了很多不同的方法,在我的设备上一切似乎都很好,但用户报告延迟最多 13 分钟...

我尝试了 2 件事:

  • 使用通知和唤醒锁启动 ForegroundService 作为 AlarmReceiver 中的唯一操作,并在服务内完成所有工作

  • 移除接收器并直接启动 AlarmActivity 以尽快完成所有工作 - 获取唤醒锁并强制屏幕作为活动中的第一件事,然后获取 IntentExtras 并启动音乐 StreamService 作为第三件事(所有1-2 秒内)

第二种方法是我目前的状态,我认为它运行良好,直到有人再次报告延迟 13 分钟。

ForegroundService 根本不起作用 - 它经常在音乐播放之前就睡着了......我只允许这个服务在音乐已经开始播放时结束,但这有时会在延迟之后发生 - 我不知道是怎么回事?这不可能是正确的,带有唤醒锁的foregroundService 必须保持设备唤醒!?!?

(我会发布活动,但它由 750 行组成,所以......不。)

无论如何:我还能做些什么来真正保持/强制设备退出打盹模式?

  1. setExactAndAllowWhileIdle 完美运行

  2. 获取唤醒锁工作正常

  3. 获得 IntentExtras 似乎效果不错

之后设备进入睡眠状态,虽然我得到了唤醒锁并且屏幕被强制打开。

  1. 音乐服务已启动,但由于(我猜)再次打盹模式而未完成缓冲

  2. 音乐在 x 分钟后开始播放

这就是我在日志中看到的,现在它在 99% 的情况下都能正常运行,但很少会发生。 我错过了什么?有什么神奇的功能可以让x分钟停止打瞌睡吗?

【问题讨论】:

  • 如果另一个警报仍在运行(该时间用于在再次启动警报服务之前停止警报服务),我想以 3 秒的延迟启动活动。但在这种情况下,线程没有启动。该活动在 ELSE 中调用,应立即启动。似乎系统再次进入睡眠状态,实际上在打盹模式的下一个维护窗口中开始了活动(我相信这是随机延迟时间)。

标签: android broadcastreceiver delay start-activity


【解决方案1】:

现在我认为这可以通过重写启动过程并直接进入foregroundService来解决。

关于延迟: 我最近将 setExactAndAllowWhileIdle 更改为 setAlarmClock,现在我 100% 确定后者在打瞌睡模式下(经常)迟到 - 对于没有其他用例的用户可见警报功能来说做得很好。

当我在闹钟时间前 10 秒 setExactAndAllowWhileIdlesetAlarmClock 到“现在 + 10 秒”时,甚至已经晚了。 (迟到了 1:55 分钟……)

所以现在我回到setExactAndAllowWhileIdle 它总是及时完美地调用 BroadcastReceiver - 总是!

我再次重写了整个启动过程(一直在经常更改它,因为故障非常随机并且很少出现在我的测试设备上)

第一种(旧)方法长期有效:

setExactAndAllowWhileIdle -> BroadcastReceiver -> AlarmActivity -> StreamingService(前台)。

这几个月运行良好,但突然之间我延迟了 1-15 分钟(我猜是维护窗口),日志有时告诉我,甚至前台服务确实启动了,音乐(ExoPlayer)开始缓冲但中断(我猜是打瞌睡)并在随机延迟时间后继续缓冲和播放。

在 ForegroundService 中已经够奇怪了,不是吗?

我的新方法:

setExactAndAllowWhileIdle -> BroadcastReceiver -> StreamService -> startActivity(AlarmActivity) -> (startForeground (the StreamService)) -> 开始播放音乐

这两天在我所有的测试中完美地在 3 种不同的设备上运行,并且在长时间打瞌睡后的早晨也是如此。

BroadcastReceiver(这里删除了所有异步内容)

public class AlarmReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {

    Log.d("Ben", "AlarmReceiver HELLO");

    Bundle b = intent.getExtras();
    int intentId = 1909;

    Intent alarmIntent = new Intent(context, StreamService.class);
    if (b != null) {
        alarmIntent.putExtras(b);
        intentId = b.getInt("id");
        Log.d("Ben", "AlarmReceiver found ID: " + intentId);
    }

// CALC NEXT ALARM and start StreamingService...

    if (Build.VERSION.SDK_INT >= 26) {
        context.startForegroundService(alarmIntent);
        context.startForegroundService(new Intent(context, CalcNextAlarmService.class));
        Log.d("Ben", "SDK >= 26 *** context.startForegroundServices");
    }
    else {
        context.startService(alarmIntent);
        context.startService(new Intent(context, CalcNextAlarmService.class));
        Log.d("Ben", "SDK < 26 *** context.startServices");
    }

附加信息:

在服务中,我在onCreate() 中启动警报屏幕活动,然后在startForeground() 之后启动onStartCommand() 中的音​​乐流。

我现在认为这是我的工作解决方案,直到我注意到其他情况。 感谢您的帮助和建议,乔纳斯·洛克曼!

编辑:警报又迟到了...我将开始一个关于受打瞌睡影响的前台服务的新帖子...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多