简体   繁体   English

handler.postDelayed与AlarmManager与

[英]handler.postDelayed vs. AlarmManager vs

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. 它使用BroadCastReceiver来检测呼叫何时结束,然后执行一些次要的内务处理任务。 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: 我目前正在为此使用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);
        }
    }

}

} }

Now I have the problem, that this setup works about 90% of the time. 现在,我有一个问题,该设置大约可以在90%的时间内工作。 In about 10% of cases, the notification isn't removed. 在大约10%的情况下,通知不会被删除。 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. 我现在正在考虑postDelayed()替代方法,显然我的选择之一是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 谢谢

I'm currently using handler.postDelayed() for this purpose: 我目前正在为此使用handler.postDelayed():

That's not a good idea, assuming the BroadcastReceiver is being triggered by a filter in the manifest. 假设BroadcastReceiver由清单中的过滤器触发,那不是一个好主意。

Now I have the problem, that this setup works about 90% of the time. 现在,我有一个问题,该设置大约可以在90%的时间内工作。 In about 10% of cases, the notification isn't removed. 在大约10%的情况下,通知不会被删除。 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. 我现在正在考虑postDelayed()的替代方法,显然我的选择之一是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. 另一种可能性是在IntentService延迟工作(通过调用startService()触发startService() ,并使它在其后台线程上休眠几秒钟。

Let's try a new way of doing this. 让我们尝试一种新的方法。 Using RxJava. 使用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. 请记住, 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

    }
} 

The snippet below will just emit the 1 in the onNext() of the subscriber. 下面的代码片段仅在订阅者的onNext()中发出1 Note that this is done on the Computation Threadpool created and managed by the RxJava library. 请注意,这是在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);

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());

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. AlarmManager似乎在10秒钟之类的短时间内效果不佳,并且根据用户报告,该行为在很大程度上取决于固件。

At the end I decided to use Handler and Runnable in my service. 最后,我决定在服务中使用HandlerRunnable

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() 创建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();
        }
    }

}

In addition to the first answer , you might want to consider what the API documentation says for the onReceive method: 除了第一个答案之外 ,您可能还需要考虑API文档对onReceive方法的说明:

[...] 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). 因此,看起来通常不建议在onReceive内启动等待一些时间的东西(即使在您的情况下,它小于10s的限制)。

I had a similar timinig problem with the BroadcastReceiver. 我在BroadcastReceiver中遇到了类似的timinig问题。 I couldn't get my results processed even though I onReceive had been called with exactly what I was exepcting. 即使我以我正在执行的调用onReceive也无法处理我的结果。 It seemed that the thread the BroadastReceiver was running in, got killed before my result processing could finish. 看来BroadastReceiver运行的线程在我的结果处理完成之前被杀死了。 My solutuion was to kick off a new thread to perform all processing. 我的解决方案是启动一个新线程来执行所有处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM