1.Android音讯机制概述
1.1. Android音讯机制是什么?
Android音讯机制首要是指Handler的运行机制以及Handler所顺便的MessageQueue和Looper的作业工程,这三者实践上是一个整体,只不过咱们在开发进程中比较多的接触到Handler罢了。
1.2. Handler的首要效果
Handler的首要效果是将一个任务切换到Handler地点的线程中履行。比如咱们常常碰到的一个场景:Android建议不要在主线程中进行耗时操作,否则会导致程序无法呼应(ANR),那么咱们会在子线程中进行耗时的网络请求和I/O操作。当耗时操作完结后需要更新UI,由于Android开发标准的约束,咱们不能在子线程中拜访UI,否则会程序反常,这个时分就需要经过Handler将任务切换到主线程进行UI更新操作。
对于拜访UI只能在主线程中进行,否则程序会抛出反常这个验证作业是由ViewRootImpl的checkThread办法来完结的,如下所示:
void checkThread(){
if (mThread != Thread.currentThread()){
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views."
);
}
}
- 这儿延伸一个问题,体系为什么不允许在子线程中拜访UI?
体系为什么不允许在子线程中拜访UI呢?这是由于Android饿UI控件不是线程安全的,假如在多线程中并非拜访可能会导致UI控件处于不行预期的状况,那为什么体系不对UI控件的拜访加上锁机制呢?缺陷有两个:首要加上锁机制会让UI拜访的逻辑变得复杂;其次锁机制会降低UI拜访的效率,由于锁机制会堵塞某些线程的履行。鉴于这两个缺陷,最简单高效的办法便是选用单线程模型来处理UI操作,对于开发者来说也不是很费事,只是需要经过Handler切换一下UI拜访的履行线程即可。
2. MeesageQueue的作业原理
MessageQueue首要包括两个操作:刺进和读取,分别对应MessageQueue的enqueueMessage和next办法。Handler经过send办法发送一条音讯后,终究会调用MessageQueue的enqueueMessage办法,enqueueMessage办法会将这条音讯刺进到音讯行列中。而next办法的效果是从音讯行列中取出一条音讯并将其从音讯行列中移除。
2.1. enqueueMessage办法剖析
咱们先来剖析下刺进操作的实现,也便是enqueueMessage办法的实现,源码如下:
boolean enqueueMessage(Message msg, long when) {
//...省掉无关代码
synchronized (this) {
//...
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;
}
从enqueueMessage的实现来看,它的首要操作是单链表的刺进操作。链表的刺进操作是依照when的大小进行排序,when值小的message排在前面:
if (p == null || when == 0 || when < p.when):
-
p == null:表明当时链表没有音讯
-
when == 0:表明新Message的when为0
-
when < p.when:表明新Message的when小于链表首结点Message的when
-
假如是上述三种状况,则直接将新的Message刺进到链表首部。
-
假如p == null || when == 0 || when < p.when为false,则会开端遍历链表,获取链表中的下一个Message与新Message的when值比较,假如满意p == null || when < p.when则完毕循环,将新Message刺进。
2.2. next办法剖析
接着咱们看下如何从音讯行列中取出音讯,也便是next办法的实现,源码如下:
Message next() {
//...省掉无关代码
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (; ; ) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//...
}
//...
}
}
能够发现next办法是一个无限循环办法,假如音讯行列中没有音讯,那么next办法会一向堵塞在这儿。当有新音讯到来时,next办法会返回这条音讯并将其从单链表中移除。
3. Looper的作业原理
Looper在Android音讯机制中扮演着音讯循环的角色, 它会不停的从MessageQueue中查看是否有新音讯,假如有新音讯就会立刻处理,否则就一向堵塞在那里。对于Looper咱们需要剖析下它的创立进程以及它是如何轮询音讯的。
3.1. Looper的创立
Handler的作业需要Looper,假如当时线程没有Looper目标线程就会报错,那么如何为当时线程创立Looper呢?其实很简单,经过Looper.prepare()即可为当时线程创立一个Looper目标,接着经过Looper.loop()来敞开音讯循环,如下所示:
new Thread("Thread#1"){
@Override
public void run() {
super.run();
//创立Looper目标
Looper.prepare();
Handler handler = new Handler();
//敞开音讯轮询
Looper.loop();
}
}.start();
咱们先来剖析Looper的prepare办法,看看Looper目标是怎样创立以及存取的,代码如下:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
剖析上面的prepare办法能够知道,Looper目标是经过ThreadLocal实现在线程中存取的,换句话说便是Looper的效果域是线程,每个线程都有各自的Looper目标。并且当时线程创立Looper后,假如再次调用perpare办法,会抛出RuntimeException反常,所以每个线程都只要一个Looper目标。
下面咱们再看下Looper的构造办法,在构造办法中它会创立一个MessageQueue(音讯行列),然后将当时线程的目标保存起来,如下所示:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
3.2. loop办法剖析
Looper最重要的一个办法是loop办法,只要调用了loop后,音讯循环体系才会真正地起效果,它的实现如下所示:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return ;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
msg.target.getTraceName(msg);
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
Looper的loop办法是一个死循环,唯一跳出循环的办法是MessageQueue的next办法返回了null。当Looper的quit办法被调用时,Looper就会调用MessageQueue的quit或者quitSafely办法来告诉音讯行列退出,当音讯行列被标记为退出状况时,它的next办法就会返回null。也便是说,Looper必须退出,否则loop办法就会无限循环下去。loop办法会调用MessageQueue的next办法来获取新音讯,而next是一个堵塞操作,当没有音讯时,next办法会一向堵塞在那里,这也导致loop办法一向堵塞在那里。
假如MessageQueue的next办法返回了新音讯,Looper就会处理这条音讯:msg.target.dispatchMessage(msg),这儿的msg.target是发送这条音讯的Handler目标,这样Handler发送的音讯终究又交给它的dispatchMessage办法来处理了。可是这儿不同的是,Handler的dispatchMessage办法是在创立Handler时所使用的Looper中履行的,这样就功地将代码逻辑切换到指定线程中去履行了。
4. Handler的作业原理
Handler的首要作业是发送和接收音讯。 Handler能够经过post的一系列办法或send的一系列办法来发送音讯,当然终究都是经过sendMessageAtTime办法来发送音讯的,在sendMessageAtTime办法中,经过调用MessaageQueue的enqueueMessage将音讯刺进到音讯行列中。Handler接收音讯是在handleMessage办法中,Looper查询到新音讯后,终究会调用Handler的handleMessage办法,这样音讯终究又回调到了handleMessage放中。
4.1. Handler发送音讯
Handler提供了post的一系列办法或send的一系列办法来发送音讯,如下所示:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postDelayed(Runnable r, int what, long delayMillis) {
return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
}
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
能够看到,Handler提供的post系列办法和send系列办法终究都是调用了sendMessageAtTime办法,而sendMessageAtTime办法终究调用了MessageQueue的enqueueMessage办法向音讯行列中刺进了一条音讯。
至此,Handler发送音讯进程就完结了。
4.2. Handler处理音讯
在剖析Looper的时分,咱们说到Looper的loop办法中,假如有新音讯会交给Handler处理,即Handler的dispatchMessage办法会被调用,dispatchMessage源码如下:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在dispatchMessage办法中,首要查看Message的callback是否为null,不为null就经过handleCallback来处理音讯。Message的callbakc是一个Runnable目标,实践便是Handler的post办法所传递的Runnable参数。
//callback便是post办法所传递的Runnable目标
//public final boolean post(@NonNull Runnable r) {
// return sendMessageDelayed(getPostMessage(r), 0);
//}
private static void handleCallback(Message message) {
message.callback.run();
}
```
- 其次,查看mCallback是否为null,不为null就调用mCall的handleMessage办法来处理音讯,Callback是个接口,它的定义如下:
- ```
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
经过Callback能够选用如下办法来创立Handler目标,Handler handler = new Handler(callback)。那么Callback的意义是什么呢?源码里面的注释现已做了说明:能够用来创立一个Handler的实例但并不需要派生Handler的子类。
最后,调用Handler的handleMessage办法来处理音讯。