Android的UI线程主要负责处理用户的按键事件、用户触屏事件以及屏幕绘图事件等,耗时操作放在后台进程进行。
那么,UI线程与后台线程之间必然需要进行通信,于是就引入了Handler机制,也就是Android线程间的消息传递机制。
在阅读了Android Handler机制相关的源代码后,做了如下笔记:
一、Handler机制中的主要角色
1.Handler
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.
Each Handler instance is associated with a single thread and that thread's message queue.
When you create a new Handler it is bound to a Looper.
It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.
There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed at some point in the future;
and (2) to enqueue an action to be performed on a different thread than your own.
以上是官方的解释,大概意思如下:
Handler对象和单个线程和这个线程的消息队列相关联,当你创建Handler的时候,他就绑定了一个Looper。(个人理解:就是说Handler,Looper,Thread,MessageQueue这些东西是耦合在一块的)
Handler主要有两个作用:
1.安排messages和runnables在将来某个时间点被执行
2.将一个操作加入队列,在别的线程执行。
官方说的太抽象,可以参考Handler流程图中步骤2和4。
2.Looper
Class used to run a message loop for a thread.
Threads by default do not have a message loop associated with them;
to create one, call prepare in the thread that is to run the loop, and then loop to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler class.
Looper用来为一个线程进行消息循环,默认的线程是没有关联Looper的,你得调用prepare方法,然后再调用loop方法让它处理messages,直到循环被中止。
参考Handler流程图中步骤3。
3.MessageQueue
Low-level class holding the list of messages to be dispatched by a Looper.
Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper.
You can retrieve the MessageQueue for the current thread with Looper.myQueue().
包含由Looper调度的消息列表的低级类。Messages不是直接被添加到MessageQueue的,而是通过关联了Looper的Handler对象来做这个事情。你可以用Looper.myQueue()来获取到当前线程的MessageQueue。
简而言之,MessageQueue就是一个消息队列,先进先出,用来管理message。
4.Message
Defines a message containing a description and arbitrary data object that can be sent to a Handler.
This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.
While the constructor of Message is public,
the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods,
which will pull them from a pool of recycled objects.
Message包含两个额外的int字段和一个你在许多情况下不用做分配的Object字段。
获取Message最好的方法是调用Message.obtain()或者Handler.obtainMessage(),这两个方法将会从回收利用的池子里给你捞出一个message给你用,减少了内存开销。
二、Handler使用实例
获取百度首页的数据,并展示到TextView中
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
TextView tv;
Handler mHandler;
private static final int MSG_TO_MAIN = 111;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.tv);
//创建Handler对象
mHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message message){
super.handleMessage(message);
switch (message.what){
case MSG_TO_MAIN:
tv.setText((String)message.obj);
break;
default:
break;
}
}
};
}
/**
*
* @param view R.id.button
*/
public void requestData(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try{
//使用okHttp进行网络请求
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
Response response = client.newCall(request).execute();
String result = response.body().string();
//将result反馈给主线程
// Message message = Message.obtain();
Message message = mHandler.obtainMessage();
message.what = MSG_TO_MAIN;
message.obj = result;
mHandler.sendMessage(message);
// message.sendToTarget();
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
}
这是一个后台线程向主线程发送消息的例子,值得注意的是,在这个例子里我们没有调用Looper.prepare()和Loop.loop(),这是因为主线程的looper是由Android环境创建的,我们不用自己调用。
三、Handler机制源码分析
以上内容,我们只是搞清楚了Handler机制的大概流程,但是具体实现又是怎么样的呢,下面我们带着问题追踪一下每一个步骤的源码:
Question1: Loop.prepare()是在干什么?
/**
*Initialize the current thread as a looper.
*This gives you a chance to create handlers that then reference this looper, before actually starting the
*loop.
*Be sure to call loop() after calling this method, and end it by calling quit().
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
看到这里,它的大概意思是要将looper绑定到当前线程,也就是说一个线程有且只有一个looper。而且looper是线程隔离的,你有你的looper,我有我的looper,互不干涉。
然而,这只是我们的一个直观感受,它的真实面目是这样的吗,我们还能接着研究这一行代码:
sThreadLocal.set(new Looper(quitAllowed));
要弄清楚这一行代码,就得搞清楚ThreadLocal,ThreadLocalMap和Thread之间的关系。
ThreadLocal是一个泛型类,她负责维护内部的ThradLocalMap,ThreadLocalMap用于存储线程隔离的变量,比如Looper。
下面主要看一下ThradLocal的get和set方法:
//set
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//get
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
至此,我们对Looper.prepare()有了一个大概的了解,但是关于ThreadLocal还有很多问题值得探讨,本文旨在浅析Hanler机制的原理,所以跳过关于ThreadLocal的一些疑问,接着往下看。
Question2: Loooper.loop()发生了什么?
//关键代码
public static void loop() {
final Looper me = myLooper();
...
//死循环,一直处理
for(; ;){
if(!loopOnce(me, ident, thresholdOverride)){
return;
}
}
}
接下来,我们就不得不看看loopOnce这个方法了
//仅展示关键代码
private static booloean loopOnce(final Looper me, final log ident , final int thresholdOverride){
...
msg.target.dispatchMessage(msg);
...
}