在 Android 当中,线程分为 UI 线程和工作线程,UI 线程负责界面绘制,事件传递消耗,工作线程负责一些比较好耗时的操作。
当 UI 线程和工作线程需要产生交互时,就需要用到异步了,Android 提供了 Handler 机制来帮助我们完成线程之间的消息传递,在了解 Handler 之前,我们需要先了解几个名词:
上面只是简单的阐述了下 Handler 机制各个组件的功能,下面我们展开来讲述一下:
Android 系统将线程分为 UI 线程和工作线程,看下面代码:
public class MainActivity extends Activity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TTT", "Thread Name = " + Thread.currentThread().getId() + ";Thread Name = " + Thread.currentThread().getName());
new Thread(new Runnable() {
@Override
public void run() {
Log.d("TTT", "Thread Name = " + Thread.currentThread().getId() + ";Thread Name = " + Thread.currentThread().getName());
}
}).start();
}
});
}
}
点击按钮,查看 Log:
08-01 14:41:39.968 9481-9481/com.example.lgb.asynctest D/TTT: Thread Name = 1;Thread Name = main
08-01 14:41:39.976 9481-9604/com.example.lgb.asynctest D/TTT: Thread Name = 161;Thread Name = Thread-161
可以看到,主线程是一个 ID 为 1,Name 为 main 的线程,也就是该线程,负责 UI 的绘制,事件的分发等等工作。
我们创建的线程,也就是工作线程,我们在该线程中不能去操作 UI,但是实际当中我们需要从工作线程中获取数据去更新 UI,这就产生了两个线程之间的交互需求,也就自然而然的引出了异步的概念。
上面说过了,Message 是线程之间传递数据的载体,线程之间传递的信息就是负载在 Message 对象当中。
可以通过以下方法获取 Message 对象:
通过以下方法负载需要传递的信息:
arg1
、arg2
两个字段设置object
字段Bundle
对象,通过调用 Message 的 setData
方法传递出去通过以下方法识别 Message 身份:
what
字段,为该 Message 设置一个身份信息MessageQueue 是线程内部维护的一个消息队列,我们并不会干预其如何运行,我们只需要知道主线程会自动创建一个 MessageQueue(由 Looper 对象创建),非主线程并不会自动创建 MessageQueue,如果需要,需自己创建。
MessageQueue 是无限循环的,当队列中有了新 Message,则由其所在线程拿出来处理,如果没有新 Message,就一直休眠着、等待着。
Handler 负责封装发送和处理 Massage,通俗点儿讲,Handler 在线程 A 当中打包并发送 Message 至 MessageQueue,在线程 B 当中,Handler 将 Message 从 MessageQueue 当中拿出并读取其中�所负载的数据。
创建 Handler 对象:
二者不同之处在于一个 Looper 参数,Looper 会在下面讲到。
发送 Message 的方法:
接收消息:
Looper 是一个比较重要的角色,它是将消息(Message)、消息队列(MessageQueue)、线程(Thread)和 Handler 联接到一起的重要组件。
每个线程都只能有一个 Looper 和 MessageQueue,UI 线程在创建之初就会自动创建一个 MessageQueue 和 Looper,但工作线程却不会自动创建,需要我们自动创建:
Looper 在线程当中创建 MessageQueue 并让其“转”起来,另一个线程中将信息打包成 Message,调用 Handler 对象的 sendMessage 方法将 Message 发送到 MessageQueue 当中,当 MessageQueue 有了新消息,其所在线程就会将消息拿出来处理。
子线程给主线程发送消息
public class MainActivity extends Activity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "来自子线程的消息";
handler.sendMessage(message);
}
}).start();
}
});
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("TTT", "接收到了来自子线程的消息:" + (String) msg.obj);
}
};
}
代码很简单,原理也不难,UI 线程在创建时既有 Looper 和 MessageQueue,我们在子线程当中调用主线程当中的 Handler 对象的 sendMessage 方法将消息发送至主线程的 MessageQueue 当中,主线程从队列中取出消息使用。
主线程给子线程发送消息
如果要在子线程当中创建 Handler 对象,可以这样写:
class ChildThread extends Thread {
private Handler handler = null;
@Override
public void run() {
super.run();
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//......
}
};
Looper.loop();
}
}
这基本可以算模板代码了,但是这样写还不够严谨,因为在其他线程当中这样写
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
childThread = new ChildThread();
childThread.start();
childThread.handler.sendEmptyMessage(200);
Log.d("TTT", "1.ThreadName = " + Thread.currentThread().getName());
}
});
就很容易出现空指针,因为有可能新线程内的 Handler 对象还没有创建成功,这时候调用其 handler 对象的 sendMessage 方法,自然会出现空指针,所以只需要这样修改一下:
class ChildThread extends Thread {
private Handler handler = null;
Object syncObject = new Object();
@Override
public void run() {
super.run();
Looper.prepare();
synchronized (syncObject) {
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("TTT", "2.ThreadName = " + Thread.currentThread().getName());
}
};
syncObject.notifyAll();
}
Looper.loop();
}
public Handler getHandler() {
synchronized (syncObject) {
if (handler == null) {
try {
syncObject.wait();
} catch (InterruptedException e) {
}
}
return handler;
}
}
}
代码很简单,给 Handler 创建的代码加一个锁,确保其只有创建成功才可以被使用即可。