处理可取消超时的典型Java技术是什么?

时间:2014-10-29 01:45:10

标签: java android timeout

在运行Android 2.2的设备上,我想检测用户何时按下屏幕一段时间。想象一下,发送莫尔斯码信息,短按(点)和长按(破折号)。我希望一旦用户抬起她的手指就会对短按钮作出反应,并且在(比方说)500毫秒之后进行更长时间的按压,即使她继续按住她的手指。

我已经查看了FutureTaskScheduledExecutorService,但这些实现看起来有些过分。或者我可能只是冷静地直接处理线程,并查看处理它们所需的所有代码。

这是我在其他语言中完成此类操作的简化伪代码:

public boolean onTouch(MotionEvent event) {
  if (event.getAction() == MotionEvent.ACTION_DOWN) {
    timer = createObjectToCallback(callbackMethod, 500); // milliseconds

  } else if (event.getAction() == MotionEvent.ACTION_UP) {
    if (timer still exists) {
      timer.kill();
      // Do the short press thing
    } else {
      // Do nothing. It already happened when callbackMethod was triggered
    }
  }
}

public void callbackMethod() {
  // Do the long press thing. The timer has already auto-destructed.
}

在Java中有哪些简单的方法?

==编辑以回应@zapl ==

的回答

编写有效的代码是一回事。了解它是如何工作的另一个。

如果我理解正确,更新UI的线程已经在循环中运行。让我们想象一个非常简单的案例。

Main活动创建一个黑色画布,并包含onTouch方法。当它启动时,它会调用setOnTouchListener。主线程现在不断地监听屏幕输入。如果用户触摸屏幕的方式已更改,则会调用onTouch方法,并提供有关更改的信息。

让我们说onTouch方法在触摸点周围绘制一个绿色圆圈。使用属于主线程的循环绘制该圆。绘图完成后,主线程开始从屏幕检查新的更改。如果没有变化,则不会再次调用onTouch,并且绿点不会移动。

当用户抬起手指时,屏幕会向主线程提供更改的信息,onTouch方法中的代码会删除点。

Create interface
Has the screen detected a change? No: loop
Has the screen detected a change? No: loop
...
Has the screen detected a change? Yes: draw a green dot; loop
Has the screen detected a change? No: loop.
...
Has the screen detected a change? Yes: new position => redraw the green dot; loop
...
Has the screen detected a change? Yes: not touching => remove dot; loop
Has the screen detected a change? ...

如果用户的手指移动至少500毫秒,我想让点变成红色。没有移动意味着没有回调onTouch。所以我可以设置一个Handler,它将自己添加到主线程的循环中。主线程现在在其循环中有两个动作。

Create interface
Has the screen detected a change? No: loop
Has the screen detected a change? No: loop
...
Has the screen detected a change? Yes: a touch; draw a green dot; add Handler; loop
Has the screen detected a change? No;
  Is it time for Handler to trigger? No: loop.
...
Has the screen detected a change? No;
  Is it time for Handler to trigger? Yes: change dot color to red; remove Handler; loop.
Has the screen detected a change? No: loop.
...
Has the screen detected a change? Yes: not touching => remove dot; loop
Has the screen detected a change? ...

Handler执行的任何代码都会阻止主线程完成 这是Handler做什么的准确描述吗?

1 个答案:

答案 0 :(得分:1)

当你实际上不需要并行时使用线程确实有点过分,因为它增加了它自己的一组问题。您需要的是安排将来运行的代码,但是在同一个线程上。 Android的Handler可以做到这一点。您可以安排RunnableMessage到达。还有派生的CountDownTimer用于更简单的周期性事件调度。

但在这种情况下可能不需要,因为有GestureDetector

它附带Android,可以区分几种类型的单,长和双击。它的行为也与系统的其他部分一致。您可能想要使用它。

有关http://developer.android.com/training/gestures/detector.html

的更多信息

如果您真的想要实现自己的,或者只是想看一个如何使用Handler的示例,请查看GestureDetector's source。它充满了您发布的代码(mHandler.hasMessagesmHandler.removeMessagesmHandler.sendEmptyMessageDelayed)。

注意:Handler不是标准Java类,因为在同一个线程中调度事件需要一个线程基于消息队列,并且没有标准的解决方案。它也是UI框架依赖于框架的线程安全性。如果您尝试从某个后台线程修改ui,Android会尝试抛出异常。但是,相同的方法应该适用于Swing的事件调度线程(SwingUtilities.invokeLater)。


编辑:尝试解释Ui线程&处理程序:

  

处理程序执行的任何代码都会阻止主线程完成。

正确。 Android的主要线程非常简化,如下所示:

public BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
public void loop() {
    while (true) {
        Runnable currentTask = queue.take(); // blocks until something happens
        currentTask.run();
        // repeat.
    }
}
public void enqueue(Runnable runnable) {
    queue.put(runnable);
}

public static void main(String[] args) {
    startThreadsThatReceiveSystemEvents();
    enqueue(new Runnable() {
        @Override
        public void run() {
            Activity activity = createStartActivity();
            activity.onCreate();
            activity.onResume();
        }
    });
    loop(); // fun fact: an android app will never return from here
            // it's process is simply killed by the system
}

现实世界的等价物经常出现在相当远的堆栈中:

E/AndroidRuntime(20941): at android.os.Looper.loop(Looper.java:130)
E/AndroidRuntime(20941): at android.app.ActivityThread.main(ActivityThread.jav a:3691)

Android在启动/停止活动,绘制屏幕等方面所做的一切都是入队的结果,以便运行循环在某个时刻对其进行评估。 Handler使用完全相同的队列。你排队的所有东西都将被执行,其中包含已经发生的所有其他事情。一个线程不能做与自身并行的事情,所以它都是顺序的。这就是Handler任务阻止其他任务的原因。

您的触摸事件示例基本上是正确的,它只是没有主动查看触摸屏。它通过它与系统的连接得到通知。基本上有另一个线程(Binder线程,如果你曾经看过线程列表)监听来自系统的消息,一旦它们到达,所有这个线程需要做的就是将它们排入主循环。如果等待,这可以自动唤醒循环。

主线程队列实际上不是简单的BlockingQueue,因为它也需要支持预定的事件。它是一个名为MessageQueue的特定于Android的实现,部分在本机代码中实现。 loop方法也在它自己的类中(Looper)。并且队列没有直接使用Runnable,它实际上是Message s的队列 - 可以包含Runnable(在隐藏字段中)和运行循环,当它找到Runnable时在消息中将像上面的示例代码一样执行它。

每个Handler绑定到一个Looper / MessageQueue组合(因此绑定到1个线程)。 Handler.post / sendMessage做了构建正确信息的肮脏工作。排队。 Message有一个返回Handler的链接,因此循环知道Handler的handleMessage方法要调用。

除了使用已经存在的主线程循环/队列之外,您还可以自由地为这些线程创建其他基于队列的线程和处理程序。 https://stackoverflow.com/a/13369215/995891包含了一个很小的例子。