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发送消息
这种方式比较流畅
- 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;
}
});
- 子线程代码片段
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();
- 关于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方法来实现,但要来自你的新线程。然后,给定的Runnable或Message将Handler的消息队列中调度,并在适当的时候进行处理。