runOnUiThread

原型

public final void runOnUiThread (Runnable action)

解释

注:属于Activity的方法。调用runOnUiThread可以在子线程里直接用来更新UI或Toast,需避免耗时操作。

示例

点击按钮后进行循环Toast

直接使用runOnUiThread方法,不推荐

直接在runOnUiThread内进行了和,主界面都被卡顿。属于不正确的用法

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        while (binding.tbnToast.isChecked()) {
            try {
                Log.d(TAG, "toast thread run ...");
                Toast.makeText(MainActivity.this, "toast thread run ...", Toast.LENGTH_SHORT).show();
                Thread.sleep(2000);

            } catch (InterruptedException e) {
                Log.d(TAG, "onCreate thread run e:" + e.toString());
                throw new RuntimeException(e);
            }
        }
    }
});

上述runOnUiThread改进,推荐

runOnUiThread用来Toast。代码片段如下:

new Thread() {
    @Override
    public void run() {
        while (binding.tbtnToastUiThread.isChecked()) {
            long millis = System.currentTimeMillis();
            Log.d(TAG, "ui thread run millis: "+millis);

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "ui thread run millis: " + millis, Toast.LENGTH_SHORT).show();
                }
            });

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                Log.d(TAG, "ui thread run e:" + e.toString());
                throw new RuntimeException(e);
            }
        }
    }
}.start();

线程里用Handler发送消息

这种方式比较流畅

  1. Handler代码片段
private Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message message) {
        if (message.what == 1)
        {
            Bundle bundle = message.getData();
            long millis = bundle.getLong("millis", -1);
            Toast.makeText(MainActivity.this, "handler toast:" + millis, Toast.LENGTH_SHORT).show();
        }

        return true;
    }
});    
  1. 子线程代码片段
new Thread(new Runnable() {
    @Override
    public void run() {
        while (binding.tbtnToastThread.isChecked()) {
            try {

                long millis = System.currentTimeMillis();
                Log.d(TAG, "thread run millis: "+millis);
                Message message = new Message();
                message.what = 1;
                Bundle bundle = new Bundle();
                bundle.putLong("millis", millis);
                message.setData(bundle);
                mHandler.sendMessage(message);
                Thread.sleep(2000);

            } catch (InterruptedException e) {
                Log.d(TAG, "thread run e:" + e.toString());
                throw new RuntimeException(e);
            }
        }
    }
}).start();
  1. 关于Looper,不推荐
    有说在子线程使用Looper,在Looper.prepare()和Looper.loop()之间进行Toast。但程序运行时会阻塞在Looper.loop()处,下面这种也是不正确的使用方式。
    代码片段如下
new Thread() {
    @Override
    public void run() {
        while (binding.tbtnToastUiThread.isChecked()) {
            if (Looper.myLooper() == null) {
                Looper.prepare();
            }
            Toast.makeText(MainActivity.this, "Looper Test", Toast.LENGTH_SHORT).show();
            Looper.loop(); // 此处会阻塞

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}.start();

引申

Toast

Toast是一个包含给用户的快速小信息的视图。Toast可以帮助你创建和显示这些信息。
当视图被显示给用户时,它作为一个浮动视图出现在应用程序上。它将永远不会收到焦点。用户可能正在输入其他东西。我们的想法是尽可能的不引人注目,同时仍然向用户显示你希望他们看到的信息。两个例子是音量控制和简短的信息,说你的设置已被保存。
使用这个类的最简单的方法是调用其中一个静态方法(指makeText),该方法构建了你所需要的一切并返回一个新的Toast对象。
请注意,当应用程序在前台时,Snackbars是首选的简短信息。
请注意,从后台发送的Toast是有速率限制的,所以要避免快速连续发送这种Toast。
从安卓12(API级别31)开始,针对安卓12或更新版本的应用程序的Toast将被限制为两行。

Handler

Handler允许你发送和处理与线程的MessageQueue相关的Message和Runnable对象。每个Handler实例都与一个线程和该线程的消息队列相关联。当你创建一个新的Handler时,它被绑定到一个Looper。它将向该Looper的消息队列传递消息和可运行对象,并在该Looper的线程上执行它们。
Handler有两个主要用途:(1)安排消息和可运行程序在未来的某个时间点执行;(2)排队等待一个动作,在与你自己的线程不同的地方执行。
通过post、postAtTime(java.lang.Runnable,long)、postDelayed、sendEmptyMessage、sendMessage、sendMessageAtTime和sendMessageDelayed方法来完成消息的调度。post版本允许你排队等待Runnable对象,以便在收到这些对象时被消息队列调用;sendMessage版本允许你排队等待一个包含数据包的消息对象,这些数据包将由处理程序的handleMessage方法处理(要求你实现一个Handler的子类)。
将消息发布或发送到Handler时,您可以允许在消息队列准备好处理项目后立即进行处理,也可以指定处理之前的延迟或处理项目的绝对时间。后两者允许您实现超时、时钟周期和其他基于计时的行为。
当为你的应用程序创建一个进程时,它的主线程专门用来运行一个消息队列,负责管理顶层的应用程序对象(活动、广播接收器等)和它们创建的任何窗口。你可以创建你自己的线程,并通过一个Handler与主应用程序线程进行通信。这可以通过调用与之前相同的post或sendMessage方法来实现,但要来自你的新线程。然后,给定的RunnableMessage将Handler的消息队列中调度,并在适当的时候进行处理。

参考

04-15 18:46