安卓是单线程模型,就是说更新UI的操作只能在主线程中进行,但在开发一款应用时,只用一条主线程是不行的,因为一些耗时操作在主线程中进行时,就会造成线程阻塞,用户体验不好,所以当我们需要做一些耗时操作,比如网络请求、图片加载等,就需要开启一个子线程,在子线程中进行这些耗时操作。既然开启一个子线程,那么这其中就涉及到了线程之间的通信,今天就通过这篇文章记录一下通过handler进行线程之间的通信。
Handler通信机制是通过Handler、Looper、MessageQueue、Message这几个类建立起来的,下面我们通过这几个类来展开对Handler通信机制的学习。
下面是我对这几个类的一个认识理解和概括:
1、Handler,负责线程间消息的发送和接收处理;
2、Looper,负责创建消息队列(MessageQueue),并循环读取消息队列中的消息;
3、MessageQueue,负责存放由Handler发送的消息;
4、Message,负责对需要通信的消息进行封装。
下面我通过一个实现倒计时的demo来加深对上面四个类的认识以及应用。先贴上布局以及其代码:
布局
<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"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="START"
android:id="@+id/start_btn"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="#000000"
android:textSize="32sp"
android:id="@+id/time_text"/>
</RelativeLayout>
布局里面就一个button和一个text view,点击button,text view每一秒刷新一次,实现倒计时的效果。
下面先贴上MainActivity的代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button mStartBtn;
private TextView mTimeText;
private static final int TIME_START_ACTION = 1;
private static int TIME = 60;
//创建一个Handler对象并实例化 重写handleMessage()方法对消息进行处理
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case TIME_START_ACTION:
mTimeText.setText(String.valueOf(msg.obj));
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
initView();
}
private void initView() {
mStartBtn = findViewById(R.id.start_btn);
mTimeText = findViewById(R.id.time_text);
mStartBtn.setOnClickListener(this);
mTimeText.setText(String.valueOf(TIME));
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_btn:
//开启一个子线程,传入一个Runnable对象,重写run方法执行耗时操作
new Thread(new Runnable() {
@Override
public void run() {
//循环发送消息
for (int i = 0;i<60;i++){
//通过Handler.sendMessage()发送消息
mHandler.sendEmptyMessage(TIME_START_ACTION);
try {
//线程睡眠1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
break;
}
}
}
从代码中可以看出,在按钮的点击事件中创建一个子线程,在子线程中让子线程每睡眠1s发送一次消息,一次达到倒计时的效果。发送消息我调用的sendEmptyMessage(int what)方法。这里概括一下发送消息的几种方法:
1、第一类:send方案
sendEmptyMessage(int what)——发送一个空消息,传入一个int类型标识符what,因为消息队列中可以存放多条消息,所以发送消息的时候传入标识符,handler在处理消息的时候靠这个来区分并作出不同的处理;
sendEmptyMessageDelayed(int what, long delayMillis)——发送一个延时空消息,从方法名也能大概猜出这个方法能实现的功能了,其中传入了两个参数,第一个就不说了,和上面的都一样,第二个参数表示延时多少毫秒后发送消息;
sendEmptyMessageAtTime(int what, long uptimeMillis)——在固定的时间发送一个消息,其中第二个参数类型是long型,所以需要将设置的时间转换成long型的毫秒值;
以上是发送一个空消息,消息中不包含Message对象,handler接收到消息后,知道了在某个子线程中发生了这个事件,并随之做出相应的操作。
sendMessage(Message msg)——发送一个消息,传入一个Message对象,将需要传送的信息封装在Message这个类里;
sendMessageDelayed(Message msg, long delayMillis)——发送一个延时消息,和上面发送一个空消息差不多,区别在于这个方法需要传入一个Message对象;
sendMessageAtFrontOfQueue(Message msg)——发送的消息放在队列的最前面;
sendMessageAtTime(Message msg, long uptimeMillis)——在固定的事件发送一个消息;
上面几种方法和发送一个空消息的区别在于,sendMessage()方法需要传入一个Message对象。下面通过一段代码看看这个方法的使用:
Message message = new Message();//实例化一个Message对象
message.obj = i;//填充数据
message.what = TIME_START_ACTION;//设置标识
mHandler.sendMessage(message);//发送消息
在代码中填充数据我是通过message.obj来赋值的,此外还可以通过message.arg来填充一些int类型的数据,也可以用message.setData(Bundle data)方法,传入一个Bundle对象,使用Bundle传递数据就跟fragment之间传递数据时一样的。
此时,有了Message对象,也可以通过message.setTarget(Handler target)方法设置一个目标Handler(消息Message时通过Handler发送和接收的,所以每一个Message总会有一个目标Handler与之对应),绑定了目标,就可以通过方法message.sendToTarget()方法发送消息。
上面提到的send方案中,发送消息时我是直接new了一个Message对象,一般不推荐这样,在平时开发中,多用obtianMessage()方法从消息池中获取一个消息对象,因为直接new的话会为Meaage分配新的内存,性能优化时是不推崇的,所以在开发中一般这样来发送一个消息:
mHandler.obtainMessage(TIME_START_ACTION,i).sendToTarget();
以上就是对send方案的一个介绍,下面我们看看另外一种方案:
2、第二类:post方案
先贴上点击事件下使用post发送一个消息的代码:
new Thread(() -> {
for (int i = 60;i>0;i--){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.post(() -> mTimeText.setText(String.valueOf(--TIME)));
}
}).start();
使用post发送消息,其中又传入了一个Runnable对象,重写了run方法,在run方法中可以直接进行ui的相关的操作,使用lambda表达式,一行代码就搞定,第二种方案相较于第一种非常的简洁。
以上两种方案都可以发送消息,实现线程之间的通信。
下面我们通过Handler源码看看这几个类之间的关系,看看底层消息通信的实现。
消息Message的发送和接收处理都是通过Handler来实现的,我们就从最简单的sendMessage(Message msg)
看看Handler时如何将Message存放到MessageQueue的:
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
注释已经有了简要说明,这个方法是推送一条消息到MessageQueue中所有待定消息的最下面,这也就意味着不出意外最后进来的消息将最后被取出。传进来的消息是通过附属在当前线程的handler调用handlerMessage(Message msg)
方法来接收处理的。
方法下面返回了执行sendMessageDelayed(msg,0)
方法后返回的值,所以sendMessage()
方法也可以看成是延迟时间为0的一个sendMessageDelayed()
方法,再往下走,看看sendMessageDelayed()
方法做了啥操作:
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
在这个方法里面,对传进来的long型时间参数做了个判断,当时间小于0时,重新赋值为0,然后返回了调用sendMessageAtTime()
方法返回的值。
接着往下看sendMessageAtTime()
方法:
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
在这个方法里面,获取了MessageQueue对象,并作了一个非空判断,最后返回调用enqueueMessage()
方法返回的值,将MessageQueue对象、Message对象,以及一个时值传入进去,下面看看enqueueMessage()
中对这三个值做了什么操作:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
因为Message时通过Handler来发送和接收的,所以Message一定会有一个目标Handler,就是在这里通过msg.target = this;
绑定的,最后MessageQueue对象调用了自身的enqueueMessage()
方法,将消息对象传了进去,下面是enqueueMessage()
方法:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
这个方法里面主要就是将Message放到MessageQueue的操作了。这里总结一下Handler发送消息Message到消息队列MessageQueue的过程:
Handler通过sendMessage()方法将Message发送给MessageQueue,然后MessageQueue将Message存放到底部去(若无特别指定)。
以上是Handler发送消息的过程,下面来看看Handler接收个处理消息的过程,首先是Looper.prepare()方法创建Looper对象和Looper.loop()方法循环消息队列:
/** 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
* {@link #loop()} after calling this method, and end it by calling
* {@link #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));
}
通过注释可以知道,prepare()方法是给当前线程初始化一个循环类,并且指出要确保在循环开始前调用方法loop(),结束后调用方法quit()。
代码中,prepare()方法又调用了prepare()方法,并且传入一个boolean类型的参数,从参数名称可以看出这个参数决定是否允许退出,这里给定一个恒值true,具体意义先放一放。
下面这个prepare()方法中首先做了一个非空判断,这里面有一个sThreadLocal,找到它后有一个介绍:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
sThreadLocal.get()在没有调用过prepare()方法时会返回一个null,所以当从sThreadLocal这里面取出的value不为空时,说明prepare()方法已经调用过了,这时就会抛出一个异常Only one Looper may be created per thread(每个线程只能创建一个Looper)
,从这里我们可以得知线程和Looper的关系:每个线程只能有一个Looper,在一个线程中,Looper只能调用一次prepare()方法。
接着往下看,当初次调用prepare()方法进来,执行了sThreadLocal.set()方法,新建了一个Looper对象,并传了进去,下面看看Looper中的构造方法做了啥操作。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在构造方法中可以看到,在这里创建了一个消息队列MessageQueue,这也就是之前概念提到的Looper创建一个MessageQueue,此外将当前线程赋值给了一个全局变量。
最开始调用方法prepare()时就传入的一个boolean类型的参数,一直没有用到,并且在创建消息队列的时候传了进去,点进去可以看到,在MessagQueue的构造方法中,将这个值又赋值给了一个全局变量。
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;//赋值给了一个全局变量
mPtr = nativeInit();
}
这个全局变量在quit()方法中的一个判断语句中用到,当是false的时候抛出异常Main thread not allowed to quit.
,表明主线程不允许执行退出操作,从这儿就能看出,创建子线程时,这个参数为true,表明可以执行退出操作,主线程在创建时,这个参数时false,不能执行退出操作,这也表明,安卓系统最少会有一条主线程。
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
小结:通过查看prepare()方法的源码,我们知道这个方法主要创建了Looper对象,Looper又创建了MessageQueue对象。
下面来看Looper.loop()方法的源码:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
1 public static void loop() {
2 final Looper me = myLooper();
3 if (me == null) {
4 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
5 }
6 final MessageQueue queue = me.mQueue;
7
8 // Make sure the identity of this thread is that of the local process,
9 // and keep track of what that identity token actually is.
10 Binder.clearCallingIdentity();
11 final long ident = Binder.clearCallingIdentity();
12
13 // Allow overriding a threshold with a system prop. e.g.
14 // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
15 final int thresholdOverride =
16 SystemProperties.getInt("log.looper."
17 + Process.myUid() + "."
18 + Thread.currentThread().getName()
19 + ".slow", 0);
20
21 boolean slowDeliveryDetected = false;
22
23 for (;;) {
24 Message msg = queue.next(); // might block
25 if (msg == null) {
26 // No message indicates that the message queue is quitting.
27 return;
28 }
29
30 // This must be in a local variable, in case a UI event sets the logger
31 final Printer logging = me.mLogging;
32 if (logging != null) {
33 logging.println(">>>>> Dispatching to " + msg.target + " " +
34 msg.callback + ": " + msg.what);
35 }
36
37 final long traceTag = me.mTraceTag;
38 long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
39 long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
40 if (thresholdOverride > 0) {
41 slowDispatchThresholdMs = thresholdOverride;
42 slowDeliveryThresholdMs = thresholdOverride;
43 }
44 final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
45 final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
46
47 final boolean needStartTime = logSlowDelivery || logSlowDispatch;
48 final boolean needEndTime = logSlowDispatch;
49
50 if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
51 Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
52 }
53
54 final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
55 final long dispatchEnd;
56 try {
57 msg.target.dispatchMessage(msg);
58 dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
59 } finally {
60 if (traceTag != 0) {
61 Trace.traceEnd(traceTag);
62 }
63 }
64 if (logSlowDelivery) {
65 if (slowDeliveryDetected) {
66 if ((dispatchStart - msg.when) <= 10) {
67 Slog.w(TAG, "Drained");
68 slowDeliveryDetected = false;
69 }
70 } else {
71 if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
72 msg)) {
73 // Once we write a slow delivery log, suppress until the queue drains.
74 slowDeliveryDetected = true;
75 }
76 }
77 }
78 if (logSlowDispatch) {
79 showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
80 }
81
82 if (logging != null) {
83 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
84 }
85
86 // Make sure that during the course of dispatching the
87 // identity of the thread wasn't corrupted.
88 final long newIdent = Binder.clearCallingIdentity();
89 if (ident != newIdent) {
90 Log.wtf(TAG, "Thread identity changed from 0x"
91 + Long.toHexString(ident) + " to 0x"
92 + Long.toHexString(newIdent) + " while dispatching to "
93 + msg.target.getClass().getName() + " "
94 + msg.callback + " what=" + msg.what);
95 }
96
97 msg.recycleUnchecked();
98 }
99 }
在loop()方法上面有段注释,要确保在循环结束调用quit()方法。
首先通过myLooper()方法获取了一个Looper对象,其实myLooper()方法中就是执行了sThreadLocal.get();
这个操作,跟prepare()方法下一样,获取一个Looper对象。
3-5行是一个判断语句,如果获取到的Looper对象为null,会抛出异常,提示没有Looper对象,在Looper调用方法loop()之前要先调用方法prepare()方法。
第6行是获取一个MessageQueue对象,在之前prepare()方法下创建的Looper下,其构造方法中创建了一个MessageQueue对象,并将这个对象赋值给了全局变量mQueue,所以在这里,直接通过me.mQueue获取到这个MessageQueue对象。
7-22行看不大懂,跟我想要了解的业务逻辑貌似关联不大,这里先不去探究。
23-98行是做的一个无限循环,for(;;)
这种循环方式跟while(true)
一样,没有外力干扰的情况下就会一直循环下去,地球不爆炸,它就不放假。
第24行就是读取消息队列中的消息,并且提示有可能造成阻塞,在主线程中也是调用的这个方法进行一个无限循环,读取队列中的消息,也有可能在去除消息的时候造成阻塞,在子线程中影响不大,在主线程中,如果超过5s未响应,就会出现ANR。这就是为什么说耗时操作在子线程中进行。
25-28是一个判断语句,当取出的消息为null时,表明MessageQueue已经退出了,此时就跳出循环。
30-55行也看不大懂,跟业务逻辑关联也不大,这里先不做探究。
第57行我又看到了对取出的消息msg的处理,msg.target.dispatchMessage(msg)
,在这行代码中,首先Message类的对象msg通过msg.target获取到它的目标Handler,记得上面在讲到handler发送消息的send方案中有一种是hander.sendMessage(Message msg),在这个方案中需要传入一个Message对象,在创建Message对象后,可以通过message.setTarget(Handler handler)方法给Message设置目标Handler,在这个方法中就是将传进来的handler赋值给了全局变量target,所以这里就直接通过msg.target获取到它的目标Handler,然后调用方法dispatchMessage()将消息发出去。
中间的代码好像跟业务逻辑关系也不大,这里也不做探究了。
最后,第97行代码,执行了msg.recycleUnchecked()这个操作,当消息从队列中取出后,释放它在队列中所占的资源,以便其他消息能够进入队列复用资源。
小结:Looper.loop()方法主要是循环读取MessageQueue中的Message,并由与Message绑定的Handler去发送消息。
下面我们就看看Handler是如何通过dispatchMessage(msg)
发送消息的:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在这个方法中,只有一个判断语句,不同的条件执行不同的操作。在这里面msg.callback先不去看,后面配合post方案一起讲,else里面,对mCallback这个全局变量进行了一个判断,那我先去看看这个全局变量mCallback是什么东西:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在上面两个Handler构造方法中,对mCallback进行了声明,但我们一般创建Handler对象都没用这两个构造方法,而是用的这个:
为了方便阅读,我截图了这个构造方法,从图中可以看出,我们一般创建Handler对象时啥都没传,那么进入到这个方法,callback就为null,所以此时Handler发送消息就会调用
dispatchMessage(Message msg)
下的handleMessage(msg)
方法。接着,我们来看看handleMessage(msg)方法:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
这个方法并没有具体的方法,其中只包含一个Message对象,但是注释说明了,子类必须实现这个方法才能接收到消息,所以这就有了我们在使用Hander的时候,在主线程中创建Hnadler对象的时候要重写这个方法,然后在在这个方法中对msg进行处理,这个msg就是从子线程传过来的消息。
以上就是对Handler通信的源码学习,在这里做个总结:
1、线程间Message的发送和接收处理是在Handler中完成的;
2、Message存放于MessageQueue中,MessageQueue由Looper调用prepare()方法创建,并通过loop()方法循环读取;
3、一个Thread只能有一个Looper,一个Looper只能由一个MessageQueue,一个MessageQueue中可以存放多条Message。
上面再Handler发送消息的时候说到另外一种更加简洁的方案,就是通过post发送消息,我们就通过源码来看看post方案为什么会更加简洁:
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
注释中说到,因为传入的Runnable对象被添加到了MessageQueue中,所以,Runnable会运行在当前handler附属的线程上,也就是说,当前Handler在哪个线程创建的,那么Runnable中的操作将在那个线程中运行。从代码可以看到post底层的实现方法其实也是sendMessage,不过这里使用getPostMessage(Runnable r)
方法传入了一个Message对象:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
这个方法下面主要是从消息池里面获取一个消息对象,然后通过m.callback = r;
将Runnable对象设置给Message对象,记得在Handler处理消息的时候有一个判断是这样的:
if (msg.callback != null) {
handleCallback(msg);
}
所以此时Message对象里的callback不为空了,那么在处理消息的时候将走handleCallback(msg);
这个方法,并且把Message对象传入了这个方法里面,我们看看这个方法里是怎么对Message做处理的:
private static void handleCallback(Message message) {
message.callback.run();
}
看到这里,我们就已经可以得知,post方案代码更加简洁的原因:
因为post方案处理消息是通过Runnable中的run方法来处理的,而Runnable又是在当前Handler附属的Thread中运行的,所以当我们的Handler在主线程中创建,在子线程中调用post(Runnable r)方法发送消息时,可以直接在run方法下对UI进行操作。
最后,众所周知,如果在子线程中接收消息,则需要通过Looper.prepare()方法来创建Looper对象,然后通过Looper.loop()来对消息队列进行循环读取,但这些操作在主线程中都是不需要的,这是因为,安卓系统是单线程模型,在应用启动时候会创建一条主线程,此时系统会自动完成Looper的相关操作,下面贴上主线创建的源码:
public static void main(String[] args) {//安卓程序运行入口
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();//创建一个Looper对象
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();//开启循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
好了,以上就是本人对Handler的学习和理解,在此记录一下。若读者朋友发现文中有描述不当的地方,还请指正,万分感谢。
网友评论