在连续循环中实现与 UI 线程的 Handler 交互

implementing Handler interaction with the UI thread on a continuous loop

请耐心等待,我对 android 还是有点陌生​​。

所以我正在制作一个复杂的按钮侦听器。此侦听器应识别短按、以某种方式闪烁(如正常的 onClick 功能),以及识别长按。

短按后,它将通过 increasing/decreasing 值更新(整数)textView。如果用户按住 textView 的按钮,它将持续 increase/decrease 直到按钮被释放。

到目前为止,我发现了 2 个关于此类问题的热门话题:

我对这些线程中的答案的问题是我觉得它们可以进一步分解。我不想创建一个 class 来保存与按钮侦听器有关的所有内容,而是想将处理程序线程上 运行 的物理 "Task" 分离到它自己的 class 中。事实上,我相信这就是 android 文档推荐的做法

https://developer.android.com/training/multiple-threads/communicate-ui#java

现在我有一个工作实现,用于在处理程序上的任务和 UI 线程之间正常传递数据以进行单个更改。但是,一旦我开始添加循环,它就会停止正常工作。如果我做一个 for 循环(即从 0 到 9),它似乎开始忽略 post 的 "delay" 部分,并执行所有增量,并更新 UI 时一切都结束了。如果我尝试 while 循环,它永远不会更新 textView,原因与 for 循环可能失败的原因类似。

这是我实现的 classes,我将在下面解释:

public class AutoUpdatingListener implements View.OnTouchListener, View.OnClickListener, View.OnLongClickListener {

    private TextView textView;
    private int textNumber;
    private String decision;
    private AutoUpdateTask updateTask;
    private static final String MSG_KEY = "textData";

    private final Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            Bundle bundle = msg.getData();
            int data = bundle.getInt(MSG_KEY);

            textView.setText(String.format(Locale.getDefault(), "%d", data));
        }
    };

    public AutoUpdatingListener(TextView textView, String decision) {
        this.decision = decision;
        this.textView = textView;
        this.textNumber = Integer.parseInt(textView.getText().toString());

        updateTask = new AutoUpdateTask(handler, textNumber, decision, MSG_KEY);
    }

    public void onClick(View v) {
        textNumber = (decision.equals("increase")) ? ++textNumber : (decision.equals("decrease")) ? --textNumber : textNumber;
        textView.setText(String.format(Locale.getDefault(),"%d", textNumber));
    }

    public boolean onLongClick(View v) {
        autoUpdate = true;

        handler.postDelayed(updateTask, 500);

        return true;
    }

    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            autoUpdate = false;
            handler.removeCallbacks(updateTask);
        }

        return false;
    }
}

这是任务对象:

public class AutoUpdateTask implements Runnable {
    private Handler handler;
    private int textNumber;
    private final String decision;
    private final String MSG_KEY;

    public AutoUpdateTask(Handler handler, int tempo, String decision, final String MSG_KEY) {
        this.handler = handler;
        this.tempo = tempo;
        this.decision = decision;
        this.MSG_KEY = MSG_KEY;
    }

    @Override
    public void run() {
        // 2nd variation would be to place the loop here
        android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        for (int i = 0; i < 10; i++) {
            textNumber = decision.equals("increase") ? ++tempo : decision.equals("decrease") ? --tempo : tempo;

            Message msg = handler.obtainMessage();
            Bundle bundle = new Bundle();
            bundle.putInt(MSG_KEY, tempo);
            msg.setData(bundle);
            handler.sendMessage(msg);

            SystemClock.sleep(500);
        }
    }
}

这一切都在 onCreate 方法的以下上下文中使用:

AutoIncrementingListener customListener = new AutoIncrementingListener(textView, "increase");
upArrow.setOnClickListener(customListener);
upArrow.setOnLongClickListener(customListener);
upArrow.setOnTouchListener(customListener);

所以本质上,如果用户短按,它会更新字段并完成操作。如果用户按住按钮,它将首先转到 longClick 侦听器,并创建一个由对处理程序的引用、要增加的文本、"increase" 确定和唯一消息键组成的任务。

然后任务被 post 延迟到处理程序线程上,并被执行(数字递增),然后它创建一条消息发送回 UI线程并更新 textView。此过程一直持续到用户释放按钮。

这都是理论上的,正如我上面所解释的,它目前并不能完全按照定义工作。

更新

我已经将循环放入任务的 run() 方法中。在这个循环中,我添加了一个 sleep(500) 调用来阻止增量同时触发。

但是,UI 没有更新的问题仍然存在(尽管已发送消息并实现了 handleMessage() 功能)。 I.E:大约 5 秒后完成循环,它最终更新了 textView。

结论

我放弃了这个实现,对于那些遇到这个问题寻求答案的人感到抱歉。我希望这个线程对您自己的实现有一些见解。

通过上面的更新,我通过调试发现,无论执行时间如何,循环完成后消息都是一起发送的。对于那些寻求这种实施方式的人来说,他们将不得不找出如何绕过这种现象。

你不能这样做。 原因是在 onLongClick 期间你 运行 一个循环 运行 一次全部完成,没有给 UI 刷新时间。

这是什么意思?

好吧,你实际上是在发送 10 条消息,一条接一条地延迟 500 毫秒。这意味着它们会一个接一个地同时到达(它们之间没有延迟),只是在第一个到达之前你会有 500 毫秒的间隔。

你需要做的是增加延迟,这样每条消息都有自己的延迟,比前一条多 500 毫秒。

handler.postDelayed(updateTask, 500*(i+1));

这是第一件事。

第二个问题是您在 AutoUpdatingListener 的 C'Tor 中创建了 updateTask,因此每个更新任务都会发送相同的更新事件编号(如果它只是递增的话)。

问题是即使改成这样的代码:

for (int i = 0; i < 10; i++) {  // example usage of a long press (looping update)
        this.textNumber = Integer.parseInt(textView.getText().toString());
        updateTask = new AutoUpdateTask(handler, textNumber, decision, MSG_KEY);
        handler.postDelayed(updateTask, 500*(i+1));
    }

它不会工作,因为在循环期间任务还没有 运行,所以数字没有机会更新。

太棒了,你可以在循环中作弊并增加数字,但这违背了所有逻辑。

for (int i = 0; i < 10; i++) {  // example usage of a long press (looping update)
            this.textNumber = Integer.parseInt(textView.getText().toString());
            updateTask = new AutoUpdateTask(handler, textNumber+i, decision, MSG_KEY);
            handler.postDelayed(updateTask, 500*(i+1));

        }

结论 您不能在同一线程中拥有发送增量的循环和执行增量的处理程序...