handler.postDelayed 与 AlarmManager 对比

发布于 2024-10-29 14:23:14 字数 2593 浏览 1 评论 0原文

我的一个应用程序有一个小问题。它使用 BroadCastReceiver 来检测呼叫何时完成,然后执行一些次要的内务处理任务。这些必须延迟几秒钟,以允许用户看到一些数据并确保呼叫日志已更新。我目前正在使用 handler.postDelayed() 来实现此目的:

public class CallEndReceiver extends BroadcastReceiver {

@Override
public void onReceive(final Context context, final Intent intent) {
    if (DebugFlags.LOG_OUTGOING)
        Log.v("CallState changed "
                + intent.getStringExtra(TelephonyManager.EXTRA_STATE));
    if (intent.getStringExtra(TelephonyManager.EXTRA_STATE)
            .equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE)) {
        SharedPreferences prefs = Utils.getPreferences(context);
        if (prefs.getBoolean("auto_cancel_notification", true)) {
            if (DebugFlags.LOG_OUTGOING)
                Log.v("Posting Handler to remove Notification ");
            final Handler mHandler = new Handler();
             final Runnable mCancelNotification = new Runnable() {
                   public void run() {
                        NotificationManager notificationMgr = (NotificationManager) context
                        .getSystemService(Service.NOTIFICATION_SERVICE);
                notificationMgr.cancel(12443);
                if (DebugFlags.LOG_OUTGOING)
                    Log.v("Removing Notification ");
                   }
                };
                mHandler.postDelayed(mCancelNotification, 4000);


        }
        final Handler updateHandler = new Handler();
         final Runnable mUpdate = new Runnable() {
               public void run() {
        if (DebugFlags.LOG_OUTGOING)
            Log.v("Starting updateService");
        Intent newBackgroundService = new Intent(context,
                CallLogUpdateService.class);
        context.startService(newBackgroundService);
               }
               };
               updateHandler.postDelayed(mUpdate, 5000);

        if (DebugFlags.TRACE_OUTGOING)
            Debug.stopMethodTracing();
        try
        {
        // Stopping old Service
        Intent backgroundService = new Intent(context,
                NetworkCheckService.class);
        context.stopService(backgroundService);
        context.unregisterReceiver(this);
        }
        catch(Exception e)
        {
            Log.e("Fehler beim Entfernen des Receivers", e);
        }
    }

}

}

现在我遇到了问题,这个设置在大约 90% 的情况下都有效。在大约 10% 的情况下,通知不会被删除。我怀疑,线程在消息队列处理消息/可运行之前就终止了。

我现在正在考虑 postDelayed() 的替代方案,我的选择之一显然是 AlarmManager。但是,我不确定性能影响(或其使用的资源)。

也许有更好的方法来确保在线程死亡之前所有消息都已被处理,或者有另一种方法来延迟这两位代码的执行。

谢谢

I have a minor problem in one of my apps. It uses a BroadCastReceiver to detect when a call finishes and then performs some minor housekeeping tasks. These have to be delayed for a few seconds, to allow the user to see some data and to ensure that the call log has been updated. I'm currently using handler.postDelayed() for this purpose:

public class CallEndReceiver extends BroadcastReceiver {

@Override
public void onReceive(final Context context, final Intent intent) {
    if (DebugFlags.LOG_OUTGOING)
        Log.v("CallState changed "
                + intent.getStringExtra(TelephonyManager.EXTRA_STATE));
    if (intent.getStringExtra(TelephonyManager.EXTRA_STATE)
            .equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE)) {
        SharedPreferences prefs = Utils.getPreferences(context);
        if (prefs.getBoolean("auto_cancel_notification", true)) {
            if (DebugFlags.LOG_OUTGOING)
                Log.v("Posting Handler to remove Notification ");
            final Handler mHandler = new Handler();
             final Runnable mCancelNotification = new Runnable() {
                   public void run() {
                        NotificationManager notificationMgr = (NotificationManager) context
                        .getSystemService(Service.NOTIFICATION_SERVICE);
                notificationMgr.cancel(12443);
                if (DebugFlags.LOG_OUTGOING)
                    Log.v("Removing Notification ");
                   }
                };
                mHandler.postDelayed(mCancelNotification, 4000);


        }
        final Handler updateHandler = new Handler();
         final Runnable mUpdate = new Runnable() {
               public void run() {
        if (DebugFlags.LOG_OUTGOING)
            Log.v("Starting updateService");
        Intent newBackgroundService = new Intent(context,
                CallLogUpdateService.class);
        context.startService(newBackgroundService);
               }
               };
               updateHandler.postDelayed(mUpdate, 5000);

        if (DebugFlags.TRACE_OUTGOING)
            Debug.stopMethodTracing();
        try
        {
        // Stopping old Service
        Intent backgroundService = new Intent(context,
                NetworkCheckService.class);
        context.stopService(backgroundService);
        context.unregisterReceiver(this);
        }
        catch(Exception e)
        {
            Log.e("Fehler beim Entfernen des Receivers", e);
        }
    }

}

}

Now I have the problem, that this setup works about 90% of the time. In about 10% of cases, the notification isn't removed. I suspect, that the thread dies before the message queue processes the message/runnable.

I'm now thinking about alternatives to postDelayed() and one of my choices is obviously the AlarmManager. However, I'm not sure about the performance impact (or the resources it uses).

Maybe there is a better way to ensure that all messages have been processed before a thread dies or another way to delay the execution of those two bits of code.

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一枫情书 2024-11-05 14:23:14

我目前正在使用 handler.postDelayed() 来实现此目的:

这不是一个好主意,假设 BroadcastReceiver 是由清单中的过滤器触发的。

现在我遇到了一个问题,这个设置大约 90% 的时间都有效。在大约 10% 的情况下,通知不会被删除。我怀疑,线程在消息队列处理消息/可运行之前就终止了。

更准确地说,该进程被终止,所有内容都随之终止。

我现在正在考虑 postDelayed() 的替代方案,我的选择之一显然是 AlarmManager。但是,我不确定性能影响(或其使用的资源)。

没那么糟糕。另一种可能性是在 IntentService 中完成延迟的工作——通过调用 startService() 触发——并让它在后台线程上休眠几秒钟。

I'm currently using handler.postDelayed() for this purpose:

That's not a good idea, assuming the BroadcastReceiver is being triggered by a filter in the manifest.

Now I have the problem, that this setup works about 90% of the time. In about 10% of cases, the notification isn't removed. I suspect, that the thread dies before the message queue processes the message/runnable.

More accurately, the process is terminated, taking everything with it.

I'm now thinking about alternatives to postDelayed() and one of my choices is obviously the AlarmManager. However, I'm not sure about the performance impact (or the resources it uses).

It's not that bad. Another possibility is to do your delayed work in an IntentService -- triggered via a call to startService() -- and have it sleep on its background thread for a couple of seconds.

墟烟 2024-11-05 14:23:14

让我们尝试一种新的方法来做到这一点。使用 RxJava。如果您想要同时、顺序地运行数百个此类延迟任务,与异步任务耦合,与同步链式异步调用链接等,那么原型设计会更简单,并且管理大量线程会更容易。

首先,设置订阅者。请记住,Subscriber 上的 new 只能执行一次,以避免内存泄漏。

// Set up a subscriber once
private Subscuber<Long> delaySubscriber = new Subscuber<Long> () {
    @Override
    public void onCompleted() {
        //Wrap up things as onCompleted is called once onNext() is over
    }
    @Override
    public void onError(Throwable e) {
        //Keep an eye open for this. If onCompleted is not called, it means onError has been called. Make sure to override this method
    }
    @Override
    public void onNext(Long aLong) {
        // aLong will be from 0 to 1000
        // Yuor code logic goes here

        // If you want to run this code just once, just add a counter and call onComplete when the counter runs the first time

    }
} 

下面的代码片段将仅在订阅者的 onNext() 中发出 1
请注意,这是在 RxJava 库创建和管理的计算线程池上完成的。

//Now when you want to start running your piece of cade, define an Observable interval that'll emit every second
private Observable<Long> runThisAfterDelay = Observable.just(1).delay(1000, TimeUnit.MILLISECONDS, Schedulers.computation());
// Subscribe to begin the emissions.
runThisAfterDelay.subscribe(delaySubscriber);

例如,如果您想每隔一秒运行一次代码,那么您可以这样做:

private Observable<Long> runThisOnInterval = Observable.interval(1000, TimeUnit.MILLISECONDS, Schedulers.computation());

Let's try a new way of doing this. Using RxJava. It's much simpler to prototype and easier to manage lots of threads if you want to ever run hundreds of such delayed tasks concurrently, sequentially, coupled with async tasks, chained with synchronous chained async calls etc.

Firstly, set up the Subscriber. Remember new on Subscriber should be done only once to avoid memory leaks.

// Set up a subscriber once
private Subscuber<Long> delaySubscriber = new Subscuber<Long> () {
    @Override
    public void onCompleted() {
        //Wrap up things as onCompleted is called once onNext() is over
    }
    @Override
    public void onError(Throwable e) {
        //Keep an eye open for this. If onCompleted is not called, it means onError has been called. Make sure to override this method
    }
    @Override
    public void onNext(Long aLong) {
        // aLong will be from 0 to 1000
        // Yuor code logic goes here

        // If you want to run this code just once, just add a counter and call onComplete when the counter runs the first time

    }
} 

The snippet below will just emit the 1 in the onNext() of the subscriber.
Note that this is done on the Computation Threadpool created and managed by the RxJava library.

//Now when you want to start running your piece of cade, define an Observable interval that'll emit every second
private Observable<Long> runThisAfterDelay = Observable.just(1).delay(1000, TimeUnit.MILLISECONDS, Schedulers.computation());
// Subscribe to begin the emissions.
runThisAfterDelay.subscribe(delaySubscriber);

If you want to run a code after every one second, say, then you can do this:

private Observable<Long> runThisOnInterval = Observable.interval(1000, TimeUnit.MILLISECONDS, Schedulers.computation());
少年亿悲伤 2024-11-05 14:23:14

除了第一个答案之外,您可能还需要考虑 API 文档中关于 onReceive 方法:

[...] 该函数通常在其进程的主线程中调用,因此您永远不应该在其中执行长时间运行的操作 [...]

所以看起来通常它不是一个好主意是在 onReceive 中启动一些等待一段时间的内容(尽管在您的情况下它小于 10 秒的限制)。

我在使用 BroadcastReceiver 时也遇到了类似的计时问题。即使我的 onReceive 已按照我的预期调用,但我仍无法处理结果。似乎 BroadastReceiver 正在运行的线程在我的结果处理完成之前就被终止了。我的解决方案是启动一个新线程来执行所有处理。

In addition to the first answer, you might want to consider what the API documentation says for the onReceive method:

[...] The function is normally called within the main thread of its process, so you should never perform long-running operations in it [...]

So it looks like generally it is not a good idea to start something that waits a couple of time within onReceive (even though, in your case it's less than the 10s limit).

I had a similar timinig problem with the BroadcastReceiver. I couldn't get my results processed even though I onReceive had been called with exactly what I was exepcting. It seemed that the thread the BroadastReceiver was running in, got killed before my result processing could finish. My solutuion was to kick off a new thread to perform all processing.

兮子 2024-11-05 14:23:14

AlarmManager 似乎在 10 秒这样的短时间内工作得不太好,根据用户报告,该行为在很大程度上取决于固件。

最后我决定在我的服务中使用 Handler 和 Runnable 。

创建 Handler 时,请确保在 Service 类中创建它,而不是在 BroadcastReceiver 中,因为在最后一种情况下,您会得到 Can't create Handler inside thread that has not Called Looper .prepare()

public class NLService extends NotificationListenerService {
    private NLServiceReceiver nlservicereciver;
    Handler delayUpdateHandler = new Handler();
    private Runnable runBroadcastUpdate;

    public void triggerViewUpdate() {
        /* Accumulate view updates for faster, resource saving operation.
        Delay the update by some milliseconds.
        And if there was pending update, remove it and plan new update.
         */
        if (runBroadcastUpdate != null) {
            delayUpdateHandler.removeCallbacks(runBroadcastUpdate);
        }
        runBroadcastUpdate = new Runnable() {
            public void run() {
                // Do the work here; execution is delayed
            }
        };
        delayUpdateHandler.postDelayed(runBroadcastUpdate, 300);
    }

    class NLServiceReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            triggerViewUpdate();
        }
    }

}

AlarmManager seems not to work very well for short periods of time like 10 seconds and according to user reports the behaviour heavily depends on the firmware.

At the end I decided to use Handler and Runnable in my service.

When creating the Handler, be sure to create it inside the Service class, not inside the BroadcastReceiver since in the last case you'll get Can't create Handler inside thread that has not called Looper.prepare()

public class NLService extends NotificationListenerService {
    private NLServiceReceiver nlservicereciver;
    Handler delayUpdateHandler = new Handler();
    private Runnable runBroadcastUpdate;

    public void triggerViewUpdate() {
        /* Accumulate view updates for faster, resource saving operation.
        Delay the update by some milliseconds.
        And if there was pending update, remove it and plan new update.
         */
        if (runBroadcastUpdate != null) {
            delayUpdateHandler.removeCallbacks(runBroadcastUpdate);
        }
        runBroadcastUpdate = new Runnable() {
            public void run() {
                // Do the work here; execution is delayed
            }
        };
        delayUpdateHandler.postDelayed(runBroadcastUpdate, 300);
    }

    class NLServiceReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            triggerViewUpdate();
        }
    }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文