Android中的主线程由 looper 和 Handlers 组成。所以了解创建无阻碍的响应式 UI 很重要。
- MessageQueue 是一个队列,其中包含消息任务。
- Handler 在 MessageQueue 中以任务形式排队,Looper 在任务出现时执行它们 MessageQueue.
- Looper 使线程保持活动状态,循环 MessageQueue 并向相应 Handler 的进程发送消息。
- Thread 通过调用 Looper 的 quit() 方法终止。
Handler 及其组件
- Handler:框架的重要对象,它具有双重责任。它绑定到给定线程的特定 Looper,并提供了将消息发送到相关 MessageQueue 的方法。该处理器还负责在实际执行消息的内容。
- Message:封装有关在特定线程上执行的操作信息。
- Runnable:抽象一个线程可以执行的任何操作的接口。
- MessageQueue:表示线程消耗的消息队列。
- Looper:负责循环的对象,循环检查 MessageQueue 以查看是否有消息要运行并将其发送到特定的 Handler。每个线程只能有一个 Looper。
现在,开始编写代码了。
例子1:
使用 Thread:
创建主线程处理程序
Handler handler = new Handler(Looper.getMainLooper());
-
thread =
new Thread(
new Runnable() {
-
@Override
-
public void run() {
-
handler2 =
new Handler(Looper.getMainLooper());
-
handler2.post(
new Runnable() {
-
@Override
-
public void run() {
-
Toast.makeText(MainActivity.
this,
"hello world",Toast.LENGTH_SHORT).show();
-
}
-
});
-
}
-
});
使用 HandlerThread:
-
handlerThread =
new HandlerThread(
"name");
-
handlerThread.start();
-
handler =
new Handler(handlerThread.getLooper());
-
handler.post(
new Runnable() {
-
@Override
-
public void run() {
-
Toast.makeText(MainActivity.
this,
"世界你好",Toast.LENGTH_SHORT).show();
-
}
-
});
注意:使用完 HandlerThread 后,要调用 quit() 方法退出。
例子2:
进度条
MainActivity.java
-
public
class MainActivity extends AppCompatActivity {
-
-
Handler handler;
-
Thread thread;
-
Button botton;
-
int MAX =
100;
-
TextView textView;
-
HandlerThread handlerThread;
-
ProgressBar progressBar;
-
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
initView();
-
initThread();
-
}
-
private void initView(){
-
botton = (Button)findViewById(R.id.start_progress);
-
progressBar = (ProgressBar)findViewById(R.id.progressBar);
-
textView = (TextView)findViewById(R.id.textView);
-
progressBar.setMax(MAX);
-
}
-
private void initThread(){
-
handler =
new Handler(){
-
@Override
-
public void handleMessage(Message message){
-
super.handleMessage(message);
-
textView.setText(message.what+
"");
-
}
-
};
-
thread =
new Thread(
new Runnable() {
-
@Override
-
public void run() {
-
for (
int i=
0;i<
100;i++){
-
progressBar.setProgress(i);
-
try {
-
Thread.sleep(
100);
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
Message msg =
new Message();
-
msg.what = i;
-
msg.obj =
"obj";
-
handler.sendMessage(msg);
-
}
-
}
-
});
-
}
-
public void onClick(View view){
-
switch (view.getId()){
-
case R.id.start_progress:
-
thread.start();
-
break;
-
default:
-
break;
-
}
-
}
-
public void onDestroy(){
-
super.onDestroy();
-
handlerThread.quit();
-
}
-
}
activity_main.xml
-
<?xml version="1.0" encoding="utf-8"?>
-
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-
xmlns:app=
"http://schemas.android.com/apk/res-auto"
-
xmlns:tools=
"http://schemas.android.com/tools"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"match_parent"
-
tools:context=
".MainActivity">
-
-
<ProgressBar
-
android:id=
"@+id/progressBar"
-
style=
"?android:attr/progressBarStyleHorizontal"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"wrap_content"
-
android:layout_alignParentLeft=
"true"
-
android:layout_alignParentStart=
"true"
-
android:layout_alignParentTop=
"true" />
-
-
<Button
-
android:id=
"@+id/start_progress"
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:layout_alignParentLeft=
"true"
-
android:layout_alignParentStart=
"true"
-
android:layout_below=
"@+id/progressBar"
-
android:layout_marginLeft=
"24dp"
-
android:layout_marginStart=
"24dp"
-
android:layout_marginTop=
"62dp"
-
android:onClick=
"onClick"
-
android:text=
"Start" />
-
-
<TextView
-
android:id=
"@+id/textView"
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:layout_alignBottom=
"@+id/start_progress"
-
android:layout_alignParentEnd=
"true"
-
android:layout_alignParentRight=
"true"
-
android:layout_marginEnd=
"85dp"
-
android:layout_marginRight=
"85dp"
-
android:textSize=
"16sp"
-
android:text=
"Count 1"
-
android:layout_alignTop=
"@+id/start_progress" />
-
-
</RelativeLayout>
例子3:
看到这一点,您可能不太了解,也许会感觉到我在哪里。
接下来,我们使用一个简单的示例来验证和解决我们内心的疑虑。
在这里,我们放置一个按钮,单击后更改TextView的值。
还设置延迟以模拟要处理的大量数据。
-
public
class MainActivity extends AppCompatActivity {
-
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
}
-
-
public void buttonClick(View view)
-
{
-
-
try {
-
Thread.sleep(
6000);
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
TextView myTextView = (TextView)findViewById(R.id.myTextView);
-
myTextView.setText(
"Button Pressed");
-
-
}
-
}
-
<RelativeLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
-
xmlns:tools=
"http://schemas.android.com/tools"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"match_parent"
-
android:paddingLeft=
"@dimen/activity_horizontal_margin"
-
android:paddingRight=
"@dimen/activity_horizontal_margin"
-
android:paddingTop=
"@dimen/activity_vertical_margin"
-
android:paddingBottom=
"@dimen/activity_vertical_margin"
-
tools:context=
".MainActivity">
-
-
<TextView
-
android:text=
"hello world"
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:layout_centerVertical=
"true"
-
android:layout_centerHorizontal=
"true"
-
android:id=
"@+id/myTextView" />
-
-
<Button
-
android:layout_width=
"wrap_content"
-
android:layout_height=
"wrap_content"
-
android:text=
"click me"
-
android:id=
"@+id/button"
-
android:layout_below=
"@+id/myTextView"
-
android:layout_centerHorizontal=
"true"
-
android:layout_marginTop=
"45dp"
-
android:onClick=
"buttonClick" />
-
-
<
/RelativeLayout>
运行后发现,延迟了6秒后才更新视图,这在实际开发中,会严重影响用户体验,造成一种严重卡顿的现象。
要解决这个问题,我们可以使用多线程的方法,修改后如下。
-
public void buttonClick(View view)
-
{
-
Thread thread =
new Thread(
new Runnable() {
-
@Override
-
public void run() {
-
try {
-
Thread.sleep(
6000);
-
Log.d(
"thread:",
"hello world");
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
-
}
-
});
-
thread.start();
-
}
在主线程以外的线程中更新用户界面元素违反了Android开发的关键规则。因此,为了更新用户界面,有必要实现线程的处理程序(Handler)。
-
Handler handler =
new Handler(){
-
@Override
-
public void handleMessage(Message msg){
-
super.handleMessage(msg);
-
TextView myTextView = (TextView)findViewById(R.id.myTextView);
-
myTextView.setText(
"Button click");
-
};
-
};
再次修改 buttonClick() 中的内容
-
public void buttonClick(View view)
-
{
-
Thread thread =
new Thread(
new Runnable() {
-
@Override
-
public void run() {
-
try {
-
Thread.sleep(
6000);
-
Log.d(
"thread:",
"hello world");
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
handler.sendEmptyMessage(
0);
-
}
-
});
-
thread.start();
-
}
拓展内容:handler 几种方法
方法 | 描述 |
---|---|
sendMessage | 将消息推送到消息队列的末尾。 |
sendMessageDelayed | 将消息推送到消息队列的末尾。 |
sendMessageAtTime | 将消息推送到消息队列的末尾。 |
sendEmptyMessage | 发送仅包含单个int代码的消息。 |
sendEmptyMessageDelayed | 在指定的时间过后发送要传递的消息。 |
sendEmptyMessageAtTime | 发送消息以在指定的绝对时间传递。 |
使用 messaga 对象保存数据
-
public void buttonClick(View view)
-
{
-
Thread thread =
new Thread(
new Runnable() {
-
@Override
-
public void run() {
-
try {
-
Thread.sleep(
6000);
-
Log.d(
"thread:",
"hello world");
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
Message msg = handler.obtainMessage();
-
Bundle bundle =
new Bundle();
-
bundle.putString(
"name",
"anny");
-
msg.setData(bundle);
-
handler.sendMessage(msg);
-
}
-
});
-
thread.start();
-
}
obtainmessage() 是从消息池中拿来一个msg 不需要另开辟空间new
取出数据并赋值给 TextView
-
Handler handler =
new Handler(){
-
@Override
-
public void handleMessage(Message msg){
-
super.handleMessage(msg);
-
Bundle bundle = msg.getData();
-
String name = bundle.getString(
"name");
-
TextView myTextView = (TextView)findViewById(R.id.myTextView);
-
myTextView.setText(name);
-
};
-
};
例子4:
为线程创建Looper和MessageQueue:
-
public
class Test extends Thread{
-
public Handler handler;
-
public void run(){
-
Looper.prepare();
-
handler =
new Handler(){
-
@Override
-
public void handleMessage(Message message){
-
super.handleMessage(message);
-
Log.d(
"obj:",
"hello world");
-
}
-
};
-
Looper.loop();
-
}
-
}
仔细看你会发现,我们在这里使用了 Looper.prepare()和 Looper.loop()
为什么前面的示例不起作用?为什么在这里使用它?
1:在默认线程下,主线程系统将自动创建Looper对象并启动消息循环。
2:在主线程中mainHandler = new Handler()等同于new Handler(Looper.myLooper())
将消息发送到 MessageQueue
第一种方式:Message
-
Message msg =
new Message();
-
msg.obj =
"Ali send message";
-
handler.sendMessage(msg);
第二种方式:Runnable
-
new Handler(Looper.getMainLooper()).post(
new Runnable() {
-
@Override
-
public void run() {
-
// this will run in the main thread
-
}
-
});
拓展:
方法 | 描述 |
---|---|
post | 立即让Runnable排队执行。 |
postAtTime | 使Runnable排队以在以毫秒为单位的绝对时间执行。 |
postDelayed | 使Runnable排队以指定的毫秒延迟后执行。 |
postAtFrontOfQueue | 立即将Runnable排队到要执行的前端。 |
Android 提供了 HandlerThread (线程的子类) 来简化过程。在内部,它以健壮的方式执行与我们相同的操作,因此,请始终使用 HandlerThread。
-
public
class Test extends HandlerThread {
-
public Handler handler;
-
public Test(String name) {
-
super(name);
-
}
-
@Override
-
protected void onLooperPrepared(){
-
handler =
new Handler(getLooper()){
-
@Override
-
public void handleMessage(Message message){
-
super.handleMessage(message);
-
//接收消息
-
}
-
};
-
}
-
}
我们在调用 onLooperPrepared 时实例化了 Handler。因此,Handler 可以将其与 Looper 关联。
1:仅在 start() 调用 HandlerThread 之后,即线程运行之后,才准备 Looper。
2:只有在准备好之后,Handler 才能与 HandlerThread Looper 关联。
另一种方式创建 HandlerThread 的方式(之前提过):
-
HandlerThread handlerThread = new HandlerThread("myHandlerThread");
-
handlerThread.start();
-
Looper looper = handlerThread.getLooper();
-
Handler
handler =
new
Handler(looper);
重要说明:请记住,在完成后台线程或活动onDestroy()方法之后调用handlerThread.quit()。
转载:https://blog.csdn.net/qq_41974199/article/details/105699841
查看评论