计时器任务或处理程序

113

假设我想每隔10秒执行某个操作,而且不一定需要更新视图。

问题是:使用像这里这样的timer和timertask是否更好(我指更高效和有效):

final Handler handler = new Handler();

TimerTask timertask = new TimerTask() {
    @Override
    public void run() {
        handler.post(new Runnable() {
            public void run() {
               <some task>
            }
        });
    }
};
timer = new Timer();
timer.schedule(timertask, 0, 15000);
}

或者只是一个带有postdelayed的处理程序。

final Handler handler = new Handler(); 
final Runnable r = new Runnable()
{
    public void run() 
    {
        <some task>
    }
};
handler.postDelayed(r, 15000);

如果可能的话,请您解释何时使用哪种方法,并且为什么其中一种方法比另一种更有效(如果确实如此)。


2
我阅读了很多关于TimerTasks不稳定行为的帖子。我的建议是避免使用它们,而是使用handler/postDelayed方法。 - Sound Conception
1
我更喜欢使用Handler-postDelay方法 - 这样你可以更好地控制它,并且可以从内部进行调度。 - mihail
1
这里有一个关于 Timer 与 Handler 的绝佳资源。 - CodyF
TimerTask是一个后台任务,因此您无法更新UI。仅此而已... - Yousha Aleayoub
给正在阅读这篇文章的人:@Yousha Aleayoub,当然你可以使用 Handler。 - David
显示剩余2条评论
3个回答

113

HandlerTimerTask更好。

Java中的TimerTask和Android中的Handler都允许您在后台线程上安排延迟和重复任务。然而,文献普遍建议在Android中使用Handler而不是TimerTask(请参见这里这里这里这里这里这里)。

一些关于TimerTask的已知问题包括:

  • 不能更新UI线程
  • 内存泄漏
  • 不可靠(不总是有效)
  • 长时间运行的任务可能会干扰下一个计划事件

示例

我见过的所有类型的Android示例的最佳来源是Codepath。这里是那里的一个使用Handler的示例,用于重复执行任务。

// Create the Handler object (on the main thread by default)
Handler handler = new Handler();
// Define the code block to be executed
private Runnable runnableCode = new Runnable() {
    @Override
    public void run() {
      // Do something here on the main thread
      Log.d("Handlers", "Called on main thread");
      // Repeat this the same runnable code block again another 2 seconds
      handler.postDelayed(runnableCode, 2000);
    }
};
// Start the initial runnable task by posting through the handler
handler.post(runnableCode);

相关


8
@Reek 不,GC应该负责处理它。但是你需要注意已发布以延迟执行的可运行代码块。在上面的示例中,使用的可运行代码块是一个内部类实例,因此具有对包含类(可能是一个activity)的隐式引用。可运行代码块将保留在处理程序关联的looper的消息队列中,直到其下一次执行时间,这可能会在上下文无效后发生,并可能泄漏包含类实例。您可以在适当的时候(例如onStop() for an activity)通过使用 mHandler.removeCallbacks(runnableCode)来清除这些引用。 - bitbybit
11
有史以来最好的参考文献呈现方式!!! (请参见此处,此处,此处,此处,此处和此处)。 - Dunes Buggy
如果我想在ViewModel中使用它怎么办?这不违背了不在Android中使用的理念吗? - desgraci
@desgraci,我从文档看到,ViewModel 不应访问视图层次结构或包含对 Activity 或 Fragment 的引用。但我没有看到禁止使用“Android things”的任何内容。 - Suragch
如果没有仔细编程,Handler在Android上也同样容易产生内存泄漏。我认为没有哪一个比另一个更好,这取决于具体情况。 - David
显示剩余6条评论

19

7
那么对于一次性任务呢?听起来计时器可能更适合,因为你不需要消息队列的开销? - Michael
5
我想我们永远都不会知道。 - Denny

11

已接受答案的 Kotlin 版本:

// execute on the main thread, empty constructor is deprecated
val handler = Handler(Looper.getMainLooper())

val runnableCode = object : Runnable {
    override fun run() {
        Log.d("Handlers", "Called on main thread")
        handler.postDelayed(this, 2000)
    }
}

// or shorter using a lambda function
val runnableCode = Runnable {
    Log.d("Handlers", "Called on main thread")
    handler.postDelayed(this, 2000)
}

handler.post(runnableCode)

4
增加一个在单独线程上运行的 Handler 的示例会很有帮助,特别是对于不需要更新用户界面的任务。 - JCarlosR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接