目录
1.Android中多过程通信的形式有哪些?
a.过程通信你用过哪些?原理是什么?(字节跳动、小米)
2.形容下Binder机制原理?(西方头条)
3.Binder线程池的工作过程是什么样?(西方头条)
4.Handler怎么进行线程通信,原理是什么?(西方头条)
5.Handler如果没有音讯解决是阻塞的还是非阻塞的?(字节跳动、小米)
6.handler.post(Runnable) runnable是如何执行的?(字节跳动、小米)
7.handler的Callback和handlemessage都存在,但callback返回true handleMessage还会执行么?(字节跳动、小米)
8.Handler的sendMessage和postDelay的区别?(字节跳动)
9.IdleHandler是什么?怎么应用,能解决什么问题?
10.为什么Looper.loop不阻塞主线程?
a.Looper有限循环为啥没有ANR(B站)
11.Looper如何在子线程中创立?(字节跳动、小米)
12.Looper、handler、线程间的关系。例如一个线程能够有几个Looper能够对应几个Handler?(字节跳动、小米)
13.如何更新UI,为什么子线程不能更新UI?(美团)
14.ThreadLocal的原理,以及在Looper是如何利用的?(字节跳动、小米)
15.Android 有哪些存储数据的形式?
16.SharedPreference原理,commit与apply的区别是什么?应用时须要有哪些留神?
17.如何判断一个 APP 在前台还是后盾?
18.如何做利用保活?
19.一张图片100×100在内存中的大小?(字节跳动)
20.Intent的原理,作用,能够传递哪些类型的参数?
21.如果须要在Activity间传递大量的数据怎么办?
22.关上多个页面,如何实现一键退出?
23.LiveData的生命周期如何监听的?(B站)
参考解析
1.Android 线程间通信有哪几种形式
跨过程通信要求把办法调用及其数据合成至操作系统能够辨认的水平,并将其从本地过程和地址空间传输至近程过程和地址空间,而后在近程过程中从新组装并执行该调用。
而后,返回值将沿相同方向传输回来。
Android 为咱们提供了以下几种过程通信机制(供开发者应用的过程通信 API)
- 文件
- AIDL (基于 Binder)
- Messenger (基于 Binder)
- ContentProvider (基于 Binder)
- Socket
在上述通信机制的根底上,咱们只需集中精力定义和实现 RPC 编程接口即可。
2.形容下Binder机制原理?(西方头条)
Linux零碎将一个过程分为用户空间和内核空间。对于过程之间来说,用户空间的数据不可共享,内核空间的数据可共享,为了保障安全性和独立性,一个过程不能间接操作或者拜访另一个过程,即Android的过程是互相独立、隔离的,这就须要跨过程之间的数据通信形式。一般的跨过程通信形式个别须要2次内存拷贝,如下图所示:
一次残缺的 Binder IPC 通信过程通常是这样:
- 首先 Binder 驱动在内核空间创立一个数据接管缓存区。
- 接着在内核空间开拓一块内核缓存区,建设内核缓存区和内核中数据接管缓存区之间的映射关系,以及内核中数据接管缓存区和接管过程用户空间地址的映射关系。
- 发送方过程通过零碎调用 copyfromuser() 将数据 copy 到内核中的内核缓存区,因为内核缓存区和接管过程的用户空间存在内存映射,因而也就相当于把数据发送到了接管过程的用户空间,这样便实现了一次过程间的通信。
3.Binder线程池的工作过程是什么样?(西方头条)
Binder设计架构中,只有第一个Binder主线程(也就是Binder_1线程)是由应用程序被动创立,Binder线程池的一般线程都是由Binder驱动依据IPC通信需要创立,Binder线程的创立流程图:
每次由Zygote fork出新过程的过程中,随同着创立binder线程池,调用spawnPooledThread来创立binder主线程。当线程执行binder_thread_read的过程中,发现以后没有闲暇线程,没有申请创立线程,且没有达到下限,则创立新的binder线程。
Binder的transaction有3种类型:
call: 发动过程的线程不肯定是在Binder线程, 大多数情況下,接收者只指向过程,并不确定会有哪个线程来解决,所以不指定线程;
reply: 发起者肯定是binder线程,并且接收者线程便是上次call时的发动线程(该线程不肯定是binder线程,能够是任意线程)。
async: 与call类型差不多,惟一不同的是async是oneway形式不须要回复,发动过程的线程不肯定是在Binder线程, 接收者只指向过程,并不确定会有哪个线程来解决,所以不指定线程。
Binder零碎中可分为3类binder线程:
Binder主线程:过程创立过程会调用startThreadPool()过程中再进入spawnPooledThread(true),来创立Binder主线程。编号从1开始,也就是意味着binder主线程名为binder_1,并且主线程是不会退出的。
Binder一般线程:是由Binder Driver来依据是否有闲暇的binder线程来决定是否创立binder线程,回调spawnPooledThread(false) ,isMain=false,该线程名格局为binder_x。
Binder其余线程:其余线程是指并没有调用spawnPooledThread办法,而是间接调用IPC.joinThreadPool(),将以后线程间接退出binder线程队列。例如: mediaserver和servicemanager的主线程都是binder线程,但system_server的主线程并非binder线程。
4.Handler怎么进行线程通信,原理是什么?(西方头条)
Handler的消息传递机制波及到四个局部:
- Message:线程间传递的对象。
- MessageQueue: 音讯队列,用来寄存Handler公布的Message.
- Handler:负责将Message插入到MessageQueue中以及对MessageQueue中的Message进行解决。
- Looper:负责从MessageQueue中取出Message,并交给Handler.
其中:
- Looper存储在ThreadLocal中,Looper在创立时会同时创立MessageQueue,作为其成员对象.因而Looper和MessageQueue是属于创建者线程的,各线程之间的Looper和MessageQueue互相独立。
- Handler在创立时会从以后线程的ThreadLocal中获得Looper.
- 发送音讯时,在发送线程中调用接管线程中的Handler的sendMessage办法,过程中,Handler会将本身赋予到Message的target中,并将Message插入到Handler对应的MessageQueue中。
- 而接管线程中的Looper在循环过程中会取出这个Message,通过Message.target取出接管线程中的Handler,并将音讯交Handler对象解决。由此实现了跨线程通信。
- 要留神的是:线程与Looper和MessageQueue是一对一的关系,即一个线程只保护一个Looper和一个MessageQueue;而线程与Handler的关系是一对多,即一个线程能够有很多Handler,一个Handler只对应一个线程,这也是为什么Handler在发送音讯时,为什么要将本身赋给Message.target的起因。
5.Handler如果没有音讯解决是阻塞的还是非阻塞的?(字节跳动、小米)
// 上面这个办法有可能会产生阻塞,次要是通过native层的epoll机制,监听文件描述符的写入事件实现的。
// -1:始终阻塞;0:不阻塞;n>0:最多阻塞n秒
nativePollOnce(ptr, nextPollTimeoutMillis);
6.handler.post(Runnable) runnable是如何执行的?(字节跳动、小米)
对于这个handler.post(Runnable r)这个办法,用过很屡次,
看下源码,它到底是怎么解决的。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
看下getPostMessage(r)这个办法的源码,
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
给message设置了回调,
而后,looper进行音讯循环,进行音讯的散发,
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果回调不为空,handleCallback(msg) 找个办法就会执行,
private static void handleCallback(Message message) {
message.callback.run();
}<span style="font-family: Arial, Helvetica, sans-serif;">调用了run办法。</span>
7.handler的Callback和handlemessage都存在,但callback返回true handleMessage还会执行么?(字节跳动、小米)
sendMessageAtTime 应用的uptimeMillis依赖的是零碎以开机工夫的相对工夫;
而sendMessageDelayed应用的delayMillis依赖的是零碎以开机工夫的绝对工夫。
啥意思呢:就是说delayMillis应用的时候要加一个SystemClock.uptimeMillis(),
也就是sendMessageAtTime等于snedMessageDelayed的状况为uptimeMillis == delayMillis – SystemClock.uptimeMillis()[这是一个绝对工夫].
SystemClock.uptimeMillis()是获取零碎从开机启动到当初的工夫,期间不包含休眠的工夫,这里取得到的工夫是一个绝对的工夫,而不是通过获取以后的工夫(相对工夫)。
而之所以应用这种形式来计算工夫,而不是取得以后currenttime来计算,在于handler会受到阻塞,挂起状态,睡眠等,这些时候是不应该执行的;如果应用相对工夫的话,就会抢占资源来执行以后handler的内容,显然这是不应该呈现的状况,所以要防止。
查看源码如下:
/**
* 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);
}
/**
* 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>
* 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)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
8.Handler的sendMessage和postDelay的区别?(字节跳动)
Thread/Hander/Looper是Android在Java线程根底之上提供的线程通信/音讯解决机制,这个家喻户晓,不再细说。Handler提供了两个发送提早解决工作的api:
/**
* 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)
/**
* Causes the Runnable r to be added to the message queue, to be run
* after the specified amount of time elapses.
* The runnable will be run on the thread to which this handler
* is attached.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
*
* @param r The Runnable that will be executed.
* @param delayMillis The delay (in milliseconds) until the Runnable
* 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. Note that a
* result of true does not mean the Runnable 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 postDelayed(Runnable r, long delayMillis)
问题在于,这两个delay的精度到底能有多大?如何了解?很多APP的定时解决机制都是应用这两个api递归抛提早工作来实现的。所以有必要钻研一下框架层的实现,成竹在胸。Android这套音讯循环机制工作在最上层,间隔Linux kernel的工夫治理甚远。本文依然采纳跟踪剖析代码的形式,基于android7.1.1。
postDelayed()实际上封装了sendMessageDelayed(),第一工夫便必由之路:
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
postDelayed()首先通过getPostMessage()将传入的Runnable对象封装成一个Message,调用sendMessageDelayed(),而sendMessageDelayed()减少了一个delay工夫参数的健壮性查看,而后转化成相对工夫,调用sendMessageAtTime()。至此,再多说一句:最简略的sendMessage()和post()实际上也是sendMessageDelayed(0)的封装。所以,Handler形形色色的post/send api们实质上无差别。只是为了让使用者在简略的状况下防止手动封装Message,只需提供一个Runnable即可。Handler调用关系整顿如下:
post()/postDelayed()/sendMessage()->sendMessageDelayed()->sendMessageAtTime()->enqueueMessage()
postAtTime()->sendMessageAtTime()->enqueueMessage()
postAtFrontOfQueue()->sendMessageAtFrontOfQueue()->enqueueMessage()
最初都以enqueueMessage()告终
enqueueMessage()->MessageQueue.enqueueMessage(Message msg, long when)
如前所述,这时候when曾经转化成相对零碎工夫。转入音讯队列类MessageQueue看一下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插入到音讯队列中,插入的新音讯有三种可能成为音讯队列的head:
(1)音讯队列为空;
(2)参数when为0,因为此时when曾经转成相对工夫,所以只有AtFrontOfQueue系列的API才会满足这个条件;
(3)以后的head Message执行工夫在when之后,即音讯队列中无须要在此Message之前执行的Message。
接下来就要看看音讯循环(Looper)如何应用when,这是本文问题的要害。要害的办法,Looper.loop(),启动线程音讯循环:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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.");
}
final MessageQueue queue = me.mQueue;
// 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 = queue.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);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 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();
}
}
从for(;;)能够看到一次循环开始于从音讯队列中去取一个音讯,MessageQueue.next(),如果next()返回null,则loop()会返回,本次音讯循环完结。取出音讯之后,通过Handler.dispatchMessage()解决音讯:
msg.target.dispatchMessage(msg);
也就是说,取下一个音讯的理论执行工夫取决于上一个音讯什么时候解决完。再看MessageQueue.next()做了什么:
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
看到next()实际上也有一个for(;;),而进口只有两个:音讯队列曾经退出,返回null;找到了一个适合的音讯,将其返回。如果没有适合的音讯,或者音讯队列为空,会block或者由IdleHandler解决,不在本文问题领域,暂不开展。次要看找到适合的音讯的逻辑:
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;
}
能够看到,如果在音讯队列中程序找到了一个音讯msg(前文剖析过,音讯队列的插入是由when顺序排列,所以如果以后的音讯没有到执行工夫,其后的也肯定不会到),以后的零碎工夫小于msg.when,那么会计算一个timeout,以便在到执行工夫时wake up;如果以后零碎工夫大于或等于msg.when,那么会返回msg给Looper.loop()。所以这个逻辑只能保障在when之前音讯不被解决,不可能保障肯定在when时被解决。很好了解:
(1)在Loop.loop()中是程序解决音讯,如果前一个音讯解决耗时较长,实现之后曾经超过了when,音讯不可能在when工夫点被解决。
(2)即便when的工夫点没有被解决其余音讯所占用,线程也有可能被调度失去cpu工夫片。
(3)在等待时间点when的过程中有可能入队解决工夫更早的音讯,会被优先解决,又减少了(1)的可能性。
所以由上述三点可知,Handler提供的指定解决工夫的api诸如postDelayed()/postAtTime()/sendMessageDelayed()/sendMessageAtTime(),只能保障在指定工夫之前不被执行,不能保障在指定工夫点被执行。
9.IdleHandler是什么?怎么应用,能解决什么问题?
那么首先咱们就先来通过源码剖析理解一下 IdleHandler 的工作原理吧。
文章出自:IdleHandler原理剖析
IdleHandler的源码剖析
首先,咱们来看下 IdleHandler 的增加和移除。
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
咱们能够通过以后线程的 Looper 获取音讯队列,而后调用 addIdleHandler(IdleHandler)
和 removeIdleHandler(IdleHandler)
增加和移除 IdleHandler。
晓得了如何增加和移除 IdleHandler,咱们再来看下 IdleHandler 的应用。
在 Looper 的 loop()
办法中,会调用 MessageQueue 中的 next()
来取音讯解决,而 IdleHandler 就是在此办法中应用了。
Message next() {
// ......
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// ......
// 1
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// 2
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
// 3
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
// 4
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// 5
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
next()
中取音讯的局部咱们省略了,具体逻辑可参考之前介绍的Android音讯机制。当初咱们只剖析无关 IdleHandler 的这一段代码。
首先 pendingIdleHandlerCount
默认为-1,所以正文1中的 pendingIdleHandlerCount<0
条件是成立的,可是还有一个 mMessages == null || now < mMessages.when
条件,这个条件的意思是以后音讯队列没有音讯了,或者说音讯队列有音讯,然而音讯不是当初解决的(之后某个工夫再解决)。艰深说就是以后没有可解决的音讯的时候,就会进入正文1中计算 IdleHandler 的数量。也就是说,IdleHandler是用在音讯队列空闲的时候的,当音讯队列中有音讯的时候 IdlerHandler 不会起作用,只有在音讯队列解决完音讯的时候才会产生作用。
接着在正文2中,创立了一个数量至多为4的 IdleHandler 数组。并且将音讯队列中的 IdleHandler 全副赋值为数组中的元素。
正文3中,获取 IdleHandler,之后开释数组中的 IdleHandler 援用。
正文4中,调用 IdleHandler的 queueIdle()
函数,并且返回一个 bool 值。
正文5中,利用 queueIdle()
的返回值来判断是否须要移除 IdleHandler, 当 queueIdle()
返回 false 的时候,音讯队列会移除该 IdleHandler, 返回为 true时,则持续保留。
从上述的代码中咱们能够理解到 IdleHandler 的几点个性。
- IdleHandler 是在音讯队列以后无可用音讯的时候产生作用的,如果你想在音讯队列空暇时做一些解决,那么你就能够在以后线程的音讯队列中增加 IdleHandler,并重写它的
queueIdle()
函数。 - IdleHandler 作用次数可为一次或者屡次。这取决于它的
queueIdle()
的返回值,如果queueIdle()
返回 false, 那么音讯队列第一次空暇调用完queueIdle()
之后便会将该 IdleHandler 移除。如果返回 true, 那意味着每次只有音讯队列闲暇,就会调用一次queueIdle()
。
可能有些小伙伴这个时候会有些疑难,如果 queueIdle()
返回 true 的时候,如果音讯队列始终没有音讯处于闲暇状态,是不是就会有限循环调用 queueIdle()
? 答复这个问题之前,咱们须要回想起以前介绍的在 next()
中的函数 nativePollOnce()
。咱们之前介绍过,这个函数在 native 层会将线程阻塞,等有新事件降临的时候再进行唤醒,所以就不会呈现咱们之前猜想的有限调用 queueIdle()
的问题。
IdleHandler 有什么作用呢?
那从源码角度探讨完 IdleHandler ,理解了它的个性和工作原理,接下来咱们就来剖析一下它有什么作用。
其实剖析 IdleHandler 的作用也是须要从它的个性和工作原理来思考的。
首先,IdleHandler 是在音讯队列以后无可用音讯的时候产生作用的,那么咱们就能够猜想 IdleHandler 是不是可用来执行一些优化动作。比方,解决一些不是非常重要的初始化操作。在启动 Activity 的时候,如果将一些不太重要的初始化动作 onCreate()
、onResume()
等函数中可能会造成页面显示迟缓的问题,影响利用启动速度。而如果将这些操作应用 IdleHandler 的 queueIdle()
来进行初始化,那么就能够在线程闲暇的时候来进行这些初始化动作,放慢用户看到界面的速度,从而进步用户体验。但同时须要留神一点, IdleHandler 依赖于音讯队列中的音讯,如果以后始终有音讯进入音讯队列,那么 IdleHandler 可能就始终都无奈有执行的机会。
其次,咱们有时候想获取某个控件的宽高,又或者是想要某个View绘制完之后增加依赖于这个View的View,那么就能够应用 IdleHandler 来进行获取宽高或者增加View。 当然也能够应用 View.post()
来实现,区别是前者是在音讯队列闲暇的时候执行, 后者是作为一个音讯来执行。
LeakCanary 中也有应用到 IdleHandler, LeakCanary 2.0中的源码应用Kotlin编写的,还没有钻研。等之后看了再补上。
IdleHandler 在零碎源码中的使用
IdleHandler 在零碎源码中也有过应用,其中 GcIdler
便实现了 IdleHandler
接口。咱们来看下 GcIdle
是如何工作的。
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}
void doGcIfNeeded() {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
//Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime()
// + "m now=" + now);
if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
//Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");
BinderInternal.forceGc("bg");
}
}
GcIdler
用来实现强制性GC操作的。当当初工夫超过了上一次GC的 MIN_TIME_BETWEEN_GCS == 5000
, 也即离上一次GC超过5秒之后就会再一次强制性触发GC, 并且每个GcIdler 返回 false,只触发一次。而 GcIdler
是怎么增加的呢?咱们能够看下。
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
通过调用 scheduleGcIdler()
来进行触发。而 scheduleGcIdler()
又是通过 ActivityThread 中的 Handler H
发送音讯触发的。再往上追溯,咱们能够晓得是在 AMS 中的这两个办法调用之后触发:
- doLowMemReportIfNeededLocked
- activityIdle
所以Android 会在内存不够的时候应用 IdleHandler 来进行强制性GC 优化。或者可能当 ActivityThread 的 handleResumeActivity
办法被调用时触发的。
10.为什么Looper.loop不阻塞主线程?
咱们平时看IntentService时看到了Thread的run办法如下:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
代码Looper.loop()是一个for死循环,而后忽然想到主线程中也有Looper为什么不卡主线程于是找到了ActivityThread的源码
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
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");
}
main办法的退出就是Looper.loop();的执行结束,所有事件都是Looper的监听,主线程本事就是个阻塞。
Android是事件驱动,Looper外部是一个while死循华,只有程序退出后循环才会进行,如果Looper应用中死掉了,任何事件都不会有反馈了。事件只会阻塞Looper,而Looper不会阻塞事件。
最初
对于如何学习Android Framework开发常识,最近有幸在前阿里技术总监手里扒到这份Android framework高级开发笔记,局部常识章节公布到了在知乎上居然1000+点赞,明天就拿进去分享给大家。
本笔记解说了Framework的次要模块,从环境的部署到技术的利用,再到我的项目实战,让咱们不仅是学习框架技术的应用,而且能够学习到应用架构如何解决理论的问题,由浅入深,具体解析Framework,让你简略高效学完这块常识!
因为篇幅无限,仅展现局部内容,所有的知识点 整顿的具体内容都放在了我的【GitHub】,有须要的敌人自取。
第一章:深刻解析Binder
Binder机制作为过程间通信的一种伎俩,基本上贯通了andorid框架层的全副。所以首先必须要搞懂的Android Binder的根本通信机制。Binder机制作为过程间通信的一种伎俩,基本上贯通了andorid框架层的全副。所以首先必须要搞懂的Android Binder的根本通信机制。
第二章:深刻解析Handler
置信大家都有这样的感触:网上剖析 Handler 机制原理的文章那么多, 为啥还要画龙点睛整顿这份笔记呢?不是说前人们写的文章不好,我就是感觉他们写的不细, 有些点不讲清楚,逻辑很难通顺的,每次我学个什么货色时遇到这种状况都贼好受。
本章先宏观实践剖析与 Message 源码剖析,再到MessageQueue 的源码剖析,Looper 的源码剖析,handler 的源码剖析,Handler 机制实现原理总结。最初还整顿Handler 所有面试题大全解析。
Handler这章内容很长,但思路是循序渐进的,如果你能保持读完我置信必定不会让你悲观。
第三章:Dalvik VM 过程零碎
Andorid系统启动、init 过程、Zygote、SystemServer启动流程、 应用程序的创立应用,Activity的创立、销毁 Handler和Looper。
第四章 深刻解析 WMS
窗口治理框架 零碎动画框架 View的工作原理。
第五章 PackagerManagerService
包治理服务,资源管理相干类。
发表回复