本文触及到的源码都以 Android Code Search 供给的最新的为准

由浅入深了解 Handler

Handler 面试必备选项,不论是什么等级的面试,几乎百分百会面试到Handler。

Handler 的中心功用贯穿了 framework ,native 和 kernel 。接下来咱们就从 framework 再到 native,一java环境变量装备贯到深java名优馆在线六区化到 kernel 层初java模拟器步对 Handler 进行分析。

Handler 是什么?

假定要学习Handler,不能将Handler单独拎出来学习。需求结合其它的相关类一同学习。

在 Android 中 Handler 是一种音讯机制,是一套线程异步linux体系装置通讯框架,Android 各个组件主张过程中的交互都是离不开它,在Android地位十分之高,不亚于Binder。

Handler 机制在 java 层触及到了MessageQueue、Message、Looper、Handler 这4个类。

  • Messaglinux体系装置e:见名知意,Messag数据结构c语言版严蔚敏e 类便是音讯机制中的传递音讯。这个音讯,分为硬件发生的音讯和软件发生的音讯。
  • MessageQueue:音讯部队,音讯的首要存储容器,其首要功用是投递音讯和监控怎样衔接手机取走音讯。
  • Looper:能够叫它轮询器,它有一个 Me数据结构与算法ssageQueue,它会不断的轮询 Messa监控他人微信聊天记录geQuejava名优馆在线六区ue 里的音讯,按必定的分发机制将音讯分发给政策处理者。
  • Handler: 音讯的辅佐类,它包括一个 Looper 和一个 MessageQueu数据结构c语言版严蔚敏e 。它首要有两个linux功用,一个是向音讯部队中发送各种音讯、作业,另一个是从音讯部队取出并处理相应的音讯、作业。

MessageQueue 在A源码编辑器ndroid中一般是跟线程绑定的,每个线程能够有一个归于自己的MessageQueue,对应的MessageQueue 会有一个仅有的Looper来进行音讯的轮询。

单独运用Handler时,当咱们新创立一个Handler政策,而且将它和一个Looper绑数据结构c语言版定,Handler传递的音讯、作业会进入到当时的这个 Looper 政策的 MessageQueue中,而且音讯也在 Looper 一切的线程呼应或许实施。

在Android运用中,UI线程也便是主线程,它有一个专用的音讯部队。上层运用的四大组件音讯传递和一些窗口的创立都要依赖于这个音讯部队。咱们也能够自己创立自己的线程,经过 Handler 与主线程通讯。

Handler 的java名优馆在线六区底子原理

先看下面的图:

由浅入深了解 Handler

首要咱们要明晰一点,一个 APP 会运转多个线程,每个线程能够创立多个 Handler,不同的线程之间能够获取对方的 Handler 政策。

当 Handler 经过 send/post 相关的方法发送一个音讯出去,这个音讯就会跟 Handler 直接经过 MessageQueue.enqueueMe数据结构期末考试题及答案ssage(Mejava怎样读ssage, long) 将音讯入队。在java面试题音讯入队之前会依据先后顺序和推延时刻的长短排序,然后依据排序再将音讯参linux体系装置与部队。一同在APP主张时,会经过 Activit源码超市yThread.main() 中调用Looper.pre源码下载pareMainLooper() 和 Looper.loop(),这样 Looper 这个轮询器就开端轮询了,Looper 经过一个死循环一向轮java根底知识点询,经过 MessageQueue.next() 一向检验去音讯部队里取出音讯,假定为空就 return 掉,假定不为空而且 Message.target 不为空,就经过 Message.target.dispatc源码怎样做成app软件hMessage() 将音讯传到 Handler 的 handleMessage() 。这java面试题样咱们就能够处理到相应的音讯了。

从源码看 H源码编辑器andler

Handler 的创立

最简略的获取 Handler 政策的方法便是经过 new Handler()

fina数据结构l Looper mLooper;
final MessageQueue mQueue;
final Callba数据结构教程第5版李春葆答案ck mCallback数据结构难学吗;
final boolean mAsynchrojava编译器nous;
public Handler() {
t源码本钱his(null,源码本钱 false);
}
//... 省掉...
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENT监控怎样衔接手机IAL_LEAKS) {
final C数据结构难学吗lass<? e监控摄像头品牌排行xtends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() ||linux指令 klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The fol源码站lowing 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 Looperjava游戏.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

在无参结构里调用了重载结构方法,并传入了 nul源码超市l, false。然后在结构给大局的 mLooper,mQueue,mCallblinux指令ack 和 mAsynchronous 标识赋值。

Looper

上面的 mLooper 和 mQueue 的赋值都是经过 Lo监控软件oper 取得的。先看 Looper.myLoojava编译器per():

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
p源码编辑器编程猫下载ublic static @Nullable Looper myLooper() {
return sThreadLocal.get()linux体系装置;
}

然后再看 Looper 的结构:

	final MessageQueu数据结构教程第5版李春葆答案e mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Th源码超市read.current数据结构期末考试题及答案Thread();
}

在 Looper 结构中就现已构建好了Java Looper 的 MessageQueue 。linux操作体系根底知识Looper 的结构函数是私有的,那么它是在哪里调用的呢?接着看代码:

	private s源码共享网tat源码共享网ic Looper sMaijavahdxxnLooper;
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get()数据结构c语言版严蔚敏 != null) {
throw new RuntimeExcep源码共享网tion("Only one Looper may be created per thread");
}
// 经过线程本地变量 sThreadLocal 保存 n监控摄像头软件app下载ew Looper(),将 Looper 与 当时线程绑定到一同
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* Initialize the cu源码共享网rrent thread as a looper, marking it a数据结构严蔚敏s an
* application'数据结构在计算机内存中的表明是指s main looper. See also: {@link #prepare()}
*
* @de源码本钱precated The main looper for your application is created by the Andjava模拟器roid environment,
*   so you should never need to call this fujava环境变量装备nction yourselfjava模拟器.
*/
@Deprecated
publlinux是什么操作体系ic static void prepareMainLoop监控他人微信聊天记录er() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != nlinux指令ull) {
throw new IllegalStlinux必学的60个指令ateExceptiolinux中文乱码视频n("The main Looper has already been prepared.");javascript
}
sMainLooper = myLooper();
}
}

在 Looper 中供给了三个 Looper 初始化的方法,能够很清楚看到,Looper 的初始化毕竟都会调用 prepare(boolean) 。这样的一起进口,能很好地控制 Looper 政策的构建,在经过 ThreadLocal 就能够确保每个线程只创立一个 Looper。

Looper数据结构与算法 的初始化

Looper 的初始化是在 APP 主张时,,经过 ActivityThread 的 main 方法中,也便是新的 APP 进linux重启指令程的进口方法中调用的。咱们看代码。

ActivityThread 的main() ,这儿只谈论 Looper 的初始化,其他的代码就先省掉掉:

    public static void main(String[] args) {
// 创立一个 Looper 政策,而且将 Looper 与当时线程绑定到一同。
Looper.java游戏prepareMainLoo源码站per();
// ...省掉...
if (sMainThreadHandler == null) {
sMainThreadH源码超市andler = thread.getHandler();
}
// ...省掉...
// 调用 Looper 的 loop() 方法,敞开无限循环,不断的轮询 MessageQueue 的音讯
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}

Activity 运转在 UI 线程,咱们在监控怎样衔接手机恣意数据结构一个 Activilinux重启指令ty 里,调用Looper.prepre数据结构c语言版严蔚敏() ,必定会导致溃散,便是由于上面的一系列操作,确保了在了一个线程中只能数据结构知识点总结存在一个 Looper 政策,那么也确保了每个线程也只需一个 MessageQueue。linux体系装置而且在 prepareMainLooper()quitAllowe监控摄像头软件app下载d 都传入的 false ,确保了 UI 线程中的 Looper是不可退出的。

详细看 Looper 的结构和 quit() 方法:

	// 传入了 quitAjava根底知识点llowed,用来设定 Looper 是源码下载否能够退出
private Looper(boolean quitAllowed)数据结构在计算机内存中的表明是指 {
// 实践上 quitAllolinux指令wed 控制的是 MessageQueue 中音讯的存取是否能够间断
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// Looper.quit() 实践上也是调用的 MessageQueue.qui源码编辑器编程猫下载t()
public void quit() {
mQueue.quit(false);
}
public v源码年代oid quitSafely() {
mQueue.quit(true);
}

咱们看 MessageQueue 的结构和 quitjavahdxx(boolean)

	private final boolean mQuitAllowed;
MessageQueue(booleajava怎样读n quitAllowed) {
mQuitjava环境变量装备Allowed = quitAllowed;
mPtr = njava环境变量装备ativeInit();
}
void quit(boolean safe) {
// 假定不容许退出,就会直接抛出异常
// 假定咱们在 Activity 调用 Looper.myLooper().quit(),就会抛出下面的异常
if (!mQuitAllowed) {
throw new Illelinux是什么操作体系galStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQui源码本钱tting) {
return;
}
mQuitting = true;
if (safe) {数据结构c语言版严蔚敏
removeAllFutureMessagesLocked();
} e监控摄像头lse {
removeAllMessa监控家用长途手机gesLocked();
}源码年代
// We can ass监控器什么牌子最好明晰度高ume mPtr != 0 because mQuitting was previo源码怎样做成app软件usly false.
nativeWake(mPtr);
}
}

小结:

  1. 在 APP 主张时,就会在 ActivityThread.main() 中调用 Looper.prepareManiLooper() 创立 UI 线程中的 Looper 政策,
  2. 调用 Looper.prepareManiLooper() 的一同设置了 Looper 不可退出,一同直接的控制了 MessageQueue 的不可退出。
  3. 在创立Loope监控家用长途手机r方数据结构知识点总结针的一同也将 Looper 政策与当时线程经过 ThreadLocal 绑定到一同,确保了每个线源码怎样做成app软件程只能有一个 Looper政策。而 MessageQueue 是在 Looper 在初始化时创立的,这样也确保了 MessageQueue 的仅有性。

发送音讯

Handler 不论是 post 仍是 send 相关的方法毕竟必定会调用 MessageQueue.enqueueMessage() 方法的。

咱们简略看一下 Handler的源码

public finalinux操作体系根底知识l boolean sendMessage(@NonNull Messajava环境变量装备ge msg) {
return sendMes监控sageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Mes监控怎样衔接手机sage msg, long dejava模拟器layMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public final boolea源码年代n post(Runnable r){
return  sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis){
return sendMessageAtTime(getPo监控摄像头软件app下载stMessage(r),java编译器 uptimeMillis);
}
/** send/post 相数据结构题库及答案关的方法都会直接调用到源码年代这个方法 */
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {源码怎样做成app软件
Runtime源码年代Exception e = new RuntimeExcep源码tion(
this + " sendMessageAtTime() called with no mQueue");监控装置流程
Log.w("L源码编辑器编程猫下载ooper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, u监控他人微信聊天记录ptimeMillis);
}
private boolean enqueueMessage(MessageQ源码年代ueue queue, Message msg, long uptimeMillis) {
msg.target = this;
// 判别是否为异步音讯,异步音讯将不会遭到同步屏障的影响
if (mAsynchrono监控装置流程us) {
msg.数据结构c语言版setAsynchronous(true)监控摄像头软件app下载;
}
// 毕竟调用到MessageQueue去刺进音讯
return queue.enqueueM监控装置流程essage(msg, uptimeMillis);
}

这样看起来,Handler 便是一个东西类,它供给了一些进口函数,音讯的处理毕竟要托付给MessageQueue处理。

接下源码怎样做成app软件来看 MessageQjava环境变量装备ueue 源码 中 的 enqueueMessage() 方法:

    boolean enqueueMessage(Message m监控他人微信聊天记录sg, long when) {
// mjavahdxxsg.target == null 标明该 message 是一个同步屏障,同步屏障的设置不是通数据结构严蔚敏过该方法数据结构难学吗完成的,所以会先判别是否存在同步屏障。
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 加锁,确保多个线程给 Meslinux中文乱码视频sageQueue 发音讯时保java根底知识点证线程安全
synchronized (this) {
// 判别入javascript队的音讯是否现已运用过了,确保音讯是新的。
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
// 判别当时 Looper 的轮询是否正在间断,假定正在数据结构期末考试题及答案间断,后边入队的音讯不会得到处理,而且在 Android 中假定Looper的轮询现已间断,那源码怎样做成app软件么也就标志这当时的linux操作体系根底知识线程也没有什么实践作用了,尤其是在 UI 线程。假定咱们自己创立的线程用的 Looper,一般是需求容许 Looperjava面试题 退出的,就不会走到这一步了。
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + "数据结构 sendingjava怎样读 message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return f源码本钱alse;
}
// 标识该音讯现已运用,假定同一个音讯 send/post 两次及以上就会在上面抛出异常。
msg.markInUse();
// 音讯实施的时刻(手机主张距现在的毫秒数 + delay的毫秒数)
msg.when = when;
// mMessages 用来记载下一条音讯
Message p = mMessages;
boolean needWake;
// mMessages == null 标明当时部队没有音讯;
// when==0 标Linux明没有linux常用指令推延;
// when<p.when 当时音讯估计的实施时刻小于现已运用过的Message的实施时刻,那么之前部队的音讯都现已实施结束
// 综上条件,java环境变量装备当时的部队为空或许现已没有可实施的音讯了,所以当时需求入队的音讯作为音讯部队头
if (p == null || when == 0 || when < p.when) {
// New head, wake up the evjavahdxxent queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 是否需求唤醒部队进行取音讯操作的条件判别
// mBlocked==true 标明部队现已空了,next()现已堵塞了,正在等候 pollOnce()
// isAsynchronous 标明音讯是否为异步音讯
// p.linux是什么操作体系tlinux常用指令arget == null 标明前一条音讯是一个同步阻止(同步源码本钱阻止之后音讯不再被处理),在进入方法的榜首行就对当时入队音讯进行的target的判linux常用指令别,能走到这儿,说明它不是同步阻止。
// 当部队为空,当时音讯是一个异步音讯,而且当时需求入队的音讯不源码是同步阻止,就需求唤醒队javascript伍轮询,在MessageQueue.next()中能够进行取音讯的操作。
needWake = mBlocke源码本钱d && p.target == njava模拟器ull &&amlinuxp; msg.isAsynchr监控器什么牌子最好明晰度高onous();
Message prev;
// 遍历音讯部队,刺进到适宜的方位
for (;;) {
prev = p;
p = p.next;
// 跳出部队遍历,去唤醒部队
if (p == null || wh数据结构难学吗en < p.wh源码编辑器en) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
//监控 We can assume mPtr != 0 because mQuittingjavahdxx is false.
if (needWake) {
// 在native中唤醒部队。
nativeWake(mPtr);
}
}
return true;
}

接下来看native层的 MessageQueue 中的 nativeWake:

static void android_o源码站s_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlon数据结构严蔚敏g ptr) {
// 在MessageQueue创立时,会对 NativeMessageQueue 进行初始化,javahdxxptr 是 Na源码本钱tiveMessageQueue的 的指针
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQLinuxueue->wake();
}

在 nativeWake()数据结构 中又调用了java根底知识点 NativeMessageQueue 的监控摄像头软件app下载 wake() 方法

void NativeMessageQueue源码::wake() {
// native 层的 Loopjava游戏er,将唤醒部队的操作交给 native 层 Looper。
mLooper->wake();
}

持续深化到 Loop监控摄像头er.监控眼cpp 的 wake() 方法:

void Looper::wake() {
// log 不去管linux必学的60个指令	
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#java模拟器endif
uint64_t inc = 1;
// 经过 eventfd 的 write() 向文件描绘付为 mWakeEventFd 的设备写入,用来唤醒监听着的 epoll ,假定不成功会linux操作体系根底知识一向发
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
if (nWrite !数据结构期末考试题及答案= sizeof(uint64_t)) {
if (errno != EAGA源码站IN) {
LO监控器什么牌子最好明晰度高G_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
mWakeEventFd.get(), nJavaWrite, s源码怎样做成app软件trerror(errno));java根底知识点
}
}
}

到这儿 Handler 发送音讯的首要作业就说完了;总结一下

  1. 在 Java 层 Handler 经过调用 MessageQueue 根java游戏据时刻刺进音讯
  2. 在 native 层,向内核空间写入计数器值 ,以此唤醒监听该 fd 的 epoll

说明:epoll 是一种堵塞的 IO 多路复用机制,它能够运用一个文监控装置流程件描绘符处理多个文件描绘符(fd)。linux体系装置在Android用epoll 机制来处理 native 层的作业。后边细说。

小结:

  1. Handler 作为一个东西类,能够经过 Handler.post/send 系列方法监控器什么牌子最好明晰度高发送音讯。之后一起调用 Handler.enqueueMessage() 方法调用当时线程的MessageQueue.enqueue() 将音讯入队。
  2. Handler 的 delay 是经过 SystemClock.uptimeMlinuxillis() + delayMillis 得到音讯的实施时刻,然后依据这个时刻先后将 Messagjava面试题e 入队到 MessageQueue 。
  3. Message只能运用一次,不能重复运用,因linux是什么操作体系为运用之后的 Message 会经过msg.markInUse()符号,再次运用就会抛出异常。
  4. native 层也存在 MessageQueue 和 Looper ,功用跟 Java 的相似,都是用来进行音讯告linux中文乱码视频知。
  5. 当MessageQueue.next() 堵塞后一向等候 pollOnce() ,当有一个新的音讯要入队,假定新音讯更不是一个同步阻止,而且音讯是异步的,就将音讯入队,而且通监控装置流程过 Mes源码共享网sageQueue::nativeWake() 直接的奉告 native 层的 Looper,经过Looper::wake() 经过 eventfd发送信号监控装置流程,唤醒 epoll ,让音讯从头轮询起来。

处理音讯

Handler 处理音讯一般是经过重写 Handler.handleMessage(Message)。 它完全是被逼调用的,当音讯到来时,会从内核一向经过native 层传递到 Java 的 Looplinux中文乱码视频er。

首要咱们先看一下,在 Handler 的 java 层 是怎样调用 Handler 的 handleMessag() 的,首要我找到了下面的方法,是Handler 自己调用的:

	/**
* Ha监控软件ndle system messages here.
*/
public void dispatchM源码编辑器编程猫下载essage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

然后咱们监控装置流程在看数据结构c语言版严蔚敏一下java编译器音讯的是源码本钱怎样取出来的?是经过 Looper.loop() 不断的轮询从MessageQueue取得的,那么咱们再看一下 Looper.loop(),由于代码太多,咱们省掉一下与当时意图无关的代码;

Looper.loop()
    /*数据结构严蔚敏*
* Run the messalinux操作体系根底知识ge queue in thisjava编译器 threajava面试题d. Be sure to源码编辑器 call
* {@link #quit()} to en监控d the loop.
*/
public stalinuxtic void loop() {
final Looper me = myLooper();
if (me == null) {
throw new R源码本钱untimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//...省掉...
me.mInLoop = trjava名优馆在线六区ue;
final Messa监控geQueue queue = me.mQueue;
//...省掉...
for (;;) {
// 获取下一条音讯,这儿有或许会堵塞
Message msg = queue.next();
if (msg == null) {
// No message indicates that the message queue is quitting.
return;数据结构与算法
}
//...省掉...
try {
// 假定经过 MessageQueue.next() 取出的音讯不为空,
// 就经过 Message.target.dispatchMessage() 传递音讯
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}java游戏
dispatlinux必学的60个指令chEnd = needEndTime ? Sysjava根底知识点temClock.uptimeMillis() : 0;
} catch (Exception exceptio源码年代n) {
//...省掉...
}数据结构教程第5版李春葆答案 finally {
//...省掉...
}
//...省掉...
// 收回音讯
msg.rlinux指令ecycleUncheLinuxcked();
}
}

在 Looper.loop监控眼() 中经过 MessageQueue.next() 取出 Message 后,会调用 Message.target.dispatchMessage() 传递音讯。那么target是谁呢?咱们看一下 Message 的代码:

Message
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
/*package*/ int flags;java模拟器
public long wjava面试题hen;
/*pa源码怎样做成app软件ckage*/ Bunjava游戏dle data;
// 能够看到 tjava根底知识点arget 便是一个 Han监控他人微信聊天记录dler
/*package*/linux Handler target;
/*package源码本钱*/ Runnable callback;
/*package*/ Message next;
//...省掉...
}

那么 Message 的 这个 target 是什么时分幅值呢?咱们回去看一下 Handler.enqujava面试题elinuxueMessage() 方法:

enqueueMessage
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
longlinux必学的60个指令 ulinux常用指令ptimeMillis) {
// 把当时 Handler 的引证赋值给了 Message.target
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enq数据结构知识点总结ueueMessage(msg, uptimeMillis);
}

在方法的榜首行就把当时 Handler 的引证赋值给了 Message.target,这也便是咱们运用linux是什么操作体系 Handler.sendMessage(linux是什么操作体系) 等方法时,能够经过 Handler.handleMessage() 获取发送的音讯的原因。

了解完 Java 层音讯的处理方法,那么咱们看一下native层的内容。回到上面java编译器的 Looper监控家用长途手机.loop() 方法,这儿是音讯轮询的当地,经过 MessageQueue.next() 获取下一个音讯的时分会发生堵塞,只数据结构c语言版严蔚敏需在有音讯入队时,才会触发。咱们详细看一下MessgeQueue.next(监控摄像头品牌排行) 方法,这儿会省掉掉一部分无关的代码。

MessgeQueue.ne监控眼xt() 相关代码:数据结构严蔚敏

MessgeQueue.next()
	Message next() {
// n监控ative 层 NativeMessageQueue 的指针,MessageQueue能够经过该源码本钱指针获取到对应NativeMessageQueue
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCoun数据结构教程第5版李春葆答案t = -1; // -1 only during first iteration
int nextPollTimeoutMillilinux必学的60个指令s = 0;
// 死循环
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommand数据结构难学吗s();
}
// 调用native方法,轮询一次,等候音讯,直到 nextPollTimeoutMillis 超时
// 这儿会堵塞,等候 native 层的音讯处理结束源码共享网
nativePollOnce(源码ptr, nextPollTimeoutMillis);
synchronized (this)javascript {
// Try to retrieve the next message.  Return if found.
final long now = SystemClock.ujava模拟器ptimeMillis();
Message prevMsg = null;
Message msgjava根底知识点 = mMessages;
if (msg != null && m数据结构期末考试题及答案sg.target == null) {
// msg.target==null 标明当时msg是一个同步阻止,到这儿会止步不前,直到获取到下一个异步音讯
do {
prevMsg = msg;
msg = msg.n源码之家ext;
} while (msg != null &java怎样读& !msg.isAsynchronous());
}
if (msg != null) {
// 判别获取到音讯是需求推延
if (now < msg.when) {
// 音讯还没有到实施时刻,设置下次轮询的时刻,运监控怎样衔接手机用的nativePollOnce的超时时长来设置
nextPollTimeoutMillis = (int) Math监控.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
pre监控装置流程vMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (数据结构难学吗DEBUG) Log.v(TAG, "Returning message: " + msg);
// 符号 msg 已运用,并回来 msg 
ms源码共享网g.markInUse();
return msg;监控怎样衔接手机
}
} else {
// No more messag数据结构期末考试题及答案es.
nex源码怎样做成app软件tPol数据结构教程第5版李春葆答案lTimeoutMillis = -1;
}
// Process the quit message now that all pe监控怎样衔接手机nding messages have been handled.
if (mQuitting) {
// 假定要退出,将 NativeMessageQueue 的指针毁掉,回来 null
/java编译器/ Looper.loop() 中获取到空值会退出轮询
dispose();
return null;
}
// 当时没有音讯处理,就会处理一些放置的使命。处理放置使命仅限于以下两种状况
// 1. 音讯部队为空
// 2. 榜首个音讯还未到实施时刻(有或许是一个同步阻止)
// 仅会在榜首次放置才会实施下面的操作,不会实施第2次
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
// 当时没有音讯要处理,则实施 IdleHandler 中的使命
pendingId监控怎样衔接手机leHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 假定没有 Idl监控eHandler 需求处理,则去持续轮询等候
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCou数据结构期末考试题及答案nt, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 实施 IdleHandlers 里的内容
// We only evelinux中文乱码视频r reajava怎样读ch this code block during the first iteration.
for (int i = 0; i &ltjavascript; pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendi源码共享网ngjavascriptId数据结构难学吗leHandlers[i] = null; // release the re数据结构在计算机内存中的表明是指ference to the handler
boo源码下载lean keep = false;
try监控 {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exjava游戏ception", t);
}
if (!java游戏keep) {
syn源码本钱chronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 重置 idle handler 的数量,使其不小于0,往后再也不会实施
pendingIdleHandlerCount = 0;
// 当咱们实施 idle handler 的时分,或许有java编译器一条新的音讯传递进来,
// 回到开端,从头检查音讯是否能够实施,把nextPoll数据结构题库及答案TimeoutM源码编辑器编程猫下载illis重置为0
nextPollTimeoutMillis = 0;
}
}

上面的代码中心操作在于 nativePollOnce(),在这儿等候Linux音讯的到来,它是在数据结构native层处理源码共享网

详细看看 MessageQueue.cpp 的 nativePollOnce() 方法 的代码:

MessageQueue::nativePollOnce()
static void android_os_MessageQueue_nativePollOnce(JNIEn源码本钱v* env, jobjava编译器ject obj,
jlong源码怎样做成app软件 ptr, jin数据结构难学吗t timeoutMillis) {
// 经过 ptr 获取到 Na监控装置流程tiveMe数据结构严蔚敏ssageQueue 
NativeM源码之家essageQueue* nativeMessageQueue = reinterpreLinuxt_cast<NativeMessagejava面试题Queue*>(ptr);
// 调用 NativeMessageQueue 的 pollOnce() 
nativeM数据结构期末考试题及答案essageQueue->pollOnce(env, obj, timeoutMillis);
}

Mess监控装置流程ageQueue.cpp 的 nativePollOn数据结构教程第5版李春葆答案ce() 方法调用 NativeMessageQueue 的pollOnce()

void NativeMessageQ源码下载ueue::pollOnce(JNIEnv* env, jobject po源码怎样做成app软件llObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
// N监控器什么牌子最好明晰度高ativeMessageQueue 又把作业交给了 native 层 Looper 处理,像极了 java 层
mLooper->pollOnce(timeoutMillis);
mPollObj数据结构难学吗 = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocallinux指令Ref(mExceptionObj);
mExceptionOb数据结构与算法j = NU监控装置流程LL;
}
}

NativeMessageQueue 又把作业交给了 native 层 Looper->poll监控眼Once监控软件() 处理:

Looper::pollOnce()
int Looper:监控器什么牌子最好明晰度高:pollOnce(int timeoutMillis, int* outFd, int* outEvenlinux体系装置ts,源码之家 void** outData) {
int result = 0;
// 了解的死循环
for (;;) {
// 遍历 Response 作业, mRespo源码共享网nses 是 Vector类型,相似 java 中的 List
while (mResponseIndex <源码编辑器编程猫下载 mResponses.size()) {
const Response&am源码超市p; response = mResponses.itemAt(mResponseIndex++);
int ident = respojava模拟器nse.request.ident;
// 当 ident == -2 时标明有回调,小于0;所以这儿优先处理没有回调的 response 
if (ident >= 0) {
int fd = response.request.fd;
int events = response监控装置流程.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning s源码站ignalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != nullptr) *outFd = fd;
if (outEvents != nullptr) *outEvents = events;
if (outData != nullptr) *outData = data;
r源码本钱eturn ident;
}
}
// result 一向等于0,if 语句不实施,下面持续轮询
if (源码怎样做成app软件result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != nullptr) *olinux必学的60个指令ut源码年代Fd = 0;
if (outEvents != nullptlinux体系r) *outEvents = 0;
if (outData != nullptr) *outjava怎样读Data监控摄像头品牌排行 = nullptr;
return result;
}
// 再java名优馆在线六区续轮询内部作业
res源码编辑器编程猫下载ult = pollInner(timeoutMillis);
}
}

再持续看 Looper::pollInner() 代码较多,这儿删去了一部分 log:

Looper::pollInner()
int Looper::pollInner(int tlinux是什么操作体系imeoutMillis) {
// 依据下一条音讯的到期时刻调整超时。。。源码怎样做成app软件省掉
// Poll.
int resujava怎样读lt = P数据结构教程第5版李春葆答案OLL_WAKE;
// 清空一切的 response
mResponses.clear();
mResponseIndex数据结构难学吗 = 0;
// We are about to idle.
mPolling = true;
// 初始化 epoll_event,最多处理 EPOLL_MAX_EVENTS=linux中文乱码视频16 个作业 
struct e数据结构严蔚敏poll_event eventItems[EPOLL_MAX_EVENTS];
// 调用 kerne数据结构题库及答案l 层的 epoll_wait,等候 epoll 监听的文件描绘符上的哦作业,最多,监听 EPOLL_MAX_EVENTS 个
// 回来的是需求处理的作业数量,假定逾越0标明超时
int eventCount = epoll_w监控ait(mEpollFd.get(), ev数据结构严蔚敏ent数据结构c语言版严蔚敏Ite监控软件ms, EPOLL_MAX_EVENTS, timeoutlinux是什么操作体系Millis);
// No longer idling.
mPolling数据结构 = false;
// Acquire lock.
mLock.lock();
// Reb源码超市uild epoll set if need监控眼ed.
/源码编辑器编程猫下载/ 重建 epoll
// 只需在新参加或移除文件描绘符时才有或许触发,这数据结构严蔚敏儿不用考虑
if (mEpollRebuildRequired) {
mEpollReb数据结构难学吗uildRequ源码本钱ired = false;
rebjava名优馆在线六区uildEpollLocked();
goto Done;
}
// Check for poll error.
if (eventCount &源码下载lt; 0) {
if (errno == EINTR) {
goto Done;源码本钱
}
ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
result = POLL_ERROR;
goto Done;
}
// Check for poll timeout.
if (eventCount == 0) {
result = POLL_TIMEOUT;
goto Done;
}
// 处理作业
for (int i = 0; i < eventClinux指令ount; i++) {
int fd = eventItems[i].data.fd;
uilinux常用指令nt32_t epollEvents = eventItems[i].events;
// 处理之前 eventfd 发来的唤醒部队轮询的作业
if (fd == mWakeEventFd.get()) {
// EPOLLIN = 1 , 只需 eventfd 发来的数据大于0 就会实施唤醒操作
if (epollEvents & EPOLLIN) {
alinux必学的60个指令woken();
} else {数据结构知识点总结
ALOGW("Ignojava面试题ring u数据结构难学吗nexpected epoll events 0x%x on wake event fd."linux指令, epollEvents);
}
} else {
ssize_t requestIndex =java面试题 mRequests.indexOfKe监控摄像头品牌排行y(fd);
if (requestIndex >= 0) {
// 根java根底知识点据监听的 epoll 作业的类型,判别作业的类型
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) eventsjava名优馆在线六区 |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEventsjava名优馆在线六区 & EPOLLHUP) events |= EVENT_HANGUP;
// 将作业linux体系类型和 request 相关内容放到 response 列表中,供往后获取
// 这儿的 mRequests 相当于 j源码共享网ava 中的源码 Map,以index为key,request为数据结构教程第5版李春葆答案value
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
// 处理 native 层音讯的 M监控摄像头品牌排行essage
mNextMessageUptime = LLlinux中文乱码视频ONG_MAX;
// mMessageEnvelopes 是 native 层发送的音讯列表,在 Looper::监控摄像头sendMessageAtTime()中添加新的音讯
while (mMessageE数据结构题库及答案nvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
/java名优馆在线六区/ 获取 native 层发送的音讯
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
// 音讯实施时刻到了,经过数据结构在计算机内存中的表明是指java面试题 handl数据结构er 处理音讯
if数据结构c语言版严蔚敏 (messageEnvelope.uptime <= now) {
{ // obtain handler
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
// 经过 native 层的 handler 处理音讯,能够看出 Looper 优先处理native层音讯
handler->ha监控装置流程ndleMessage(message);
} // release handler
mLock.lock();
mSendingMessage = false;
// 实施了回调
result = POLL_CALLBACK;
} else {
// The la源码之家st message left at the head of the queue determinelinuxs the next wakeup time.
mNextMessageUptime = messlinux是什么操作体系ageEnvel数据结构与算法ope.uptime;
break;
}
}
// Release lock.
mLock.unlock();
// 处理一切的监控摄像头带有linux回调的 response 
for (size_t i = 0; i < mResponses.size源码下载(); i++) {
Response& response = mResponses.editItemAt(i监控摄像头);
if (java环境变量装备respo源码编辑器nse.request.ident =linux中文乱码视频= POLL_CALLBACK) {
int fd = response.request.fd;数据结构在计算机内存中的表明是指
int events = response.e数据结构教程第5版李春葆答案ven监控ts;java怎样读
void*linux中文乱码视频 data = response.request.data;
// 处理央求的回调,这儿需求留神,回调有或许会封闭掉文监控摄像头监控软件描绘符,所以在实施完回调后,需求移除
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callblinux中文乱码视频ackResult == 0) {
// 根除文件描绘Java
removeFd(fd, response.requjava编译器est.seq);
}
// 清空 respons数据结构c语言版严蔚敏e 引证的一切回调方法
response.request.callback.clear();
result = POLL_CALLBACK监控他人微信聊天记录;
}
}
// 这儿回来了,就代表 java 层就能够轮询一次了
return result;
}
Looper::awoken() 和 Looper::pushResponse()

简略看一下Lolinux指令oper::awoken(源码年代):

void Loo源码年代per:java模拟器:awoken() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ awoken", this);
#endif
uint64_t counte源码之家r;
// 调用 eventfd 的 read 读取唤醒的信号,当线程在接收到信号之后,就会从睡觉中转为 runnig 状况
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}

下面是 Looper::pushResponse() :

void Looper::p数据结构教程第5版李春葆答案ushResponse(int e数据结构题库及答案vents, const Request& requeslinux必学的60个指令t) {
Re源码之家spoLinuxnse response;
response.events = events;
response.request = request;
mResponses.push(response);
}

暂时小结一下:

  1. 在 java 层获取音讯时会经过监控摄像头 Looper.loop() 不断的轮询从 MessageQueujava怎样读e 中Java获取音讯;
  2. 从 MessageQueue 中获取音讯的方法是 MessageQueue.next() 方法,该方法有或许会堵塞,等候数据结构 nativlinux指令e 层的音讯处理结束;
  3. 在 nati数据结构在计算机内存中的表明是指ve 层也有相同有相似的音讯传递,经过 Request 和 Response 的方法来完成音讯的处理;
  4. Android 会优先java游戏处理 njava游戏ative 层的音讯,java游戏当处理完 native 层的消监控怎样衔接手机息,才会翻开堵塞让 java监控软件 层处理音讯;
  5. native 层运用了 kernel 层的 epoll 机制协作 eventfd 来处理音讯。

下面开端介绍 epoll。

epoll 机制

epoll 是 Linux 上的一种 IO 多路复用机制。在没有 epoll 之前,Linux上运用的是 select 和 poll 的 IO 多路复用机制。这两种方法有以下缺陷:

  1. select 、poll 机制监控中,单个进程能够监控的文件描绘符不得逾越进程可翻开的文件个数的上限,默许为 1024。即便批改,也会回到功用问题;
  2. select、poll 轮询功率会跟着监控的文件描绘符linux必学的60个指令的数量的添加下降;
  3. select 、poll 从内核空间回来到用户空间的是整个文件描绘符数组,程序还需求额外在遍历整个数组才干知道那里写文件描绘符出发了相应的作业。

epoll 是在Linux 内监控眼核2.6中提出的,是 select 和 poll 的增强版。epoll 愈加灵敏,没有文件描绘符的捆绑。epoll 运java名优馆在线六区用一个文件描绘符处理多个文件描绘符,将用户联系的文件描绘符的作业存放到内核的一个作业linux表中,这样用户空间和内核linux空间之间只需数据结构求 copy 一次。这便是闻名的 mmapjava怎样读关于 mmap 往后再介绍,感兴趣的能够参看 仔细分析mmap:是什么 为什么 怎样用

epoll 操作过程中首要需求三个接口:

int epoll_create(int __size);
int epoll_ctl(int __epoll_fd, int __op, int __fd, struct epoll_event* __event);
int epoll_wait(int __epoll_fd, strlinux必学的60个指令uct epoll_event* __events, int __event_cojavahdxxunt, int __timeout_ms);

在 Android 升级过程中,在 api 21 和 28 又添加了以下的三个接口:

int epoll_create1(int __flags) __INTRODUCED_IN(21);
int epoll_pwait(int __epoll_fd, struct epoll_event* __events, int __event_count, int __timeout_ms, const数据结构题库及答案 sigset_t* __mask) __INTRODUC监控装置流程ED_IN(21)监控家用长途手机;
int epoll_pwait64(int __epoll_fd, struct epoll_event* __events, int __e数据结构与算法vent_数据结构与算法cou数据结构严蔚敏nt, int __timeout_ms, c数据结构题库及答案onst sigset64_t* __mask) __INTRODUCED_IN(28);

不过 epoll 的中心功用不变,到毕竟都会调用到上面的三个接口。咱们首要介绍先期的三个接口。

  1. int epoll_create(int __size);
    创立一个 epoll 文件描绘符句柄, size 是指定内核需求监数据结构知识点总结听的文件描绘符的数量。这个 size 不是捆绑 epoll 所监听的描绘符的最大个数,他仅仅数据结构c语言版严蔚敏对内核初始linux体系装置分配内部数据结构的一个主张。
    当创立好 epoll 句柄后,它就会数据结构题库及答案占用一个文件描绘符,在运用完 ep源码下载oll 后有必要经过 close() 封闭,否则文件描绘符或许会被耗尽。
  2. **ijavascriptnt epoll_ctl(int epoll_fd, int op, int fd, structjava根底知识点 epollinux常用指令l_event_ evelinux是什么操作体系nt)_
    对指定的文件描绘符进行 op 操作,即作业的 增,删,改。

    • epoll_fd: epoll_create 回来的句柄
    • op: 操作类型,有 EPOLL_CTL_Alinux中文乱码视频DD(添加) , EPOLL_CTL_DE(删去) , EPOLL_CTL_MOD()(批改) 三种操作码
    • fd:需求监听的文件描绘符
    • eventjavahdxx:一个叫做 epoll_event 的结构体,用来奉告内核监听的作业内容,详细代码如下 epoll_event.h :
#pragma once
#include &lt监控他人微信聊天记录;sys/cdefs.h>
#include <stdint.h>
/** The union o监控器什么牌子最好明晰度高f possible data types for an `epoll_event`. */
typedef union epoll_data {
void* ptr;
int fd;
uint32_t u32;
uint64_tlinux必学的60个指令 u64;
} epoll_data_t;
/** The type rep监控软件resenting an epoll() event. */
struct epoll_event {
uint32_t events;
epoll_data_javascriptt data;
}
#ifdef __x86_64__linux常用指令
__packed
#endif
  1. **int epoll_wait(int epoll_fd, struct epoll_event_ events, int event_count, int timeout_ms);_
    等候 epoll_fd 上的 ILinuxO 作业,最多回来 event_count 。

    • epoll_fd:epoll_create 回来的句柄
    • events:监控的作业的集结
    • event_count: 内核监控的作业有多大,它不能大于 在 epoll_create() 中的 size
    • timeout_ms: 超时时刻,单位毫数据结构难学吗秒。假定传入 负数 标明不确定,假定 events 的数量为0 而且 timeLinuxout 还有剩下,或许为负数,则会去查询现已组织稳当的作业列表。源码共享网

说明:
当进程调用 epoll_create() 方法时, 内核就会创一个 eventpoll 的结构体,这个eventpoll 结构体里会保护一个红黑树,当调用 epoll_ctl() 时会创立一个双向链表,并将新添加的文件描绘符java名优馆在线六区添加到 eventpoll 结构体中的红黑树上,然后向内核注册有作业到来的回调h函数,当设备上的作业来暂时,回调函数会向双向链表中刺进组织稳当的文件描绘符,并唤醒 epoll_wait 进程。当实监控行 epoll_wait() 就会当即回来链表中的数据。

epoll 小结

epoll 经过 epoll_ctl()监控摄像头品牌排行 注册一个文件描绘符,当这个文件描绘符组织稳当时,内核会采用回调的方法,迅速激活文件描绘符,当进程调用 epoll_wait() 时便会得到奉告linux,epoll 通java编译器过每个文件描绘符定义的回调函数来呼应作业,而不是像 select、poll 一样经过轮询的方法。
别的,epoll 监督的文件描绘符数量不受捆绑,而且IO 功率也不会跟着监督的文件描绘符数量的增加而下降。
好了 epoll 就简略介绍到这儿了,java编译器更详细的内容请参数据结构在计算机内存中的表明是指看 Linux IO方法及 select、poll、epojava模拟器ll详解 和 Linux 2.6 中 epoll 相关的源码

持续处理音讯

回到上面的 Loopejava怎样读r::数据结构在计算机内存中的表明是指pollInner() 和 Looper::awoken() 中

	struct epoll_eLinuxvent eventItems[EPOLL_MAX_EVENTS];
// 调用源码怎样做成app软件 kernel 层java怎样读的 epoll_wait,等候 epoll 监听的文件描绘符上的哦作业,最多,监听 EPOLL_MAX_EVENTS 个
// 回来的是需求处理的作业数量,假定逾越0标明超时
int eventCount = epollinux指令l_wait(mEpollFd.get(), eventItemjava游戏s, EPOLL_MAX_EVENTS,linux重启指令 timeoutMill监控怎样衔接手机is);

在这儿监听的 mWajava根底知识点keEventFd 和 mEpollFd 这个问价描绘符,是在哪里经过 epoll_ctl 注册的呢?

首要看Looper的结构函数:

Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolli监控器什么牌子最好明晰度高ng(false),
mEpollRebuildRequired数据结构期末考试题及答案(fajavahdxxlse),
mNextRequestSeq(0),
mRes数据结构严蔚敏ponseIndex(0),
mNJavaextMessageUp数据结构教程第5版李春葆答案time(LLONG_MAX) {
// 重置 mWakeEvenjava怎样读tFd 的值
mWakeE数据结构难学吗ventFd.reset(event监控眼fd(0, EFD_NON数据结构与算法BLOClinux重启指令K | EFD_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mjava模拟器WakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));
AutoMutex _l(mjavahdxxLock);
rebuildEpollLocked();
}

持续看 Looper::rebuil源码编辑器编程猫下载dEpollLocked()数据结构与算法:

Looper::rebuildEpollLLinuxocked()
void Looper::rebuildEp源码之家ollLocked() {
// Close old epoll instancjava名优馆在线六区e if we havelinux必学的60个指令 one.
if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
// epoll 现已创立过句柄了, 重置 mEpollFd
mEpollFd.reset();
}
// 经过 epoll_create1 创数据结构严蔚敏建 epoll 句柄
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC监控怎样衔接手机));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %数据结构s", strerror(errno));
struct epoll_java怎样读event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd.get();
// 将唤醒java游戏体系的 mWakeEventFd 参加 epoll 的监控眼描绘符表中
intjava面试题 result = epoll_ctl(mE数据结构难学吗pollFd.gejavascriptt(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0,源码 "Could not add wake ev源码站ent fd to epoll instance: %s",
strerror(errno));
for (sizelinux指令_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.val监控怎样衔接手机ueAt(i);javahdxx
struct epoll_event eventItem;
reque监控st.initEventItem监控怎样衔接手机(&eventItem);
// 将 request 列表中的作业参加 epoll 的描绘符表中
int epollResult = epoll_ctl(mEpollFd.ge源码年代t源码怎样做成app软件(), EPOLL_CTLLinux_ADD, request.fd, &eventItem);
if (epollResult < 0) {
ALOGE("Err数据结构or adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, str源码error(errno));
}
}
}

这样就将唤醒和request的作业参加到 epoll 监听的作业列表中。当经过 eventfjava面试题d 的 read 或许 epoll 的 epoll_wait 时就会得到奉告。源码之家

小结:

  1. 在native 层监控家用长途手机也是经过java游戏 Handler 和 Looper 的方法进行音讯传递;
  2. native 层经过 epoll 和 eventfd 进行线程的唤醒;
  3. native 层构建 Looper 时就会创立 epoll 文件描绘符,并将唤醒的文件监控装置流程描绘符参加到epoll作业中;
  4. epoll 的利益在于 mmap 和 作业的结构。mmap 减少一次用户空数据结构期末考试题及答案间和内核空间之间只需求 copy 一次,而经过红黑树和链表使作业查询更便当

经过上面的描绘,咱们丰富一下 Handler 机制的原理图:

由浅入深了解 Handler

总结

  1. Andrlinux重启指令oid 在运用主张数据结构时,会在 ActivityThread 被创立时会初始化Looper,这也是为什源码编辑器编程猫下载么 UI 线程默许就能够运用 Looper原因;
  2. Looper.loop() 是一个死循环,那为什么不会导致运用卡死呢?虽然当没有音讯时会线程会自动休眠。而且数据结构在计算机内存中的表明是指运用linux体系装置的卡死是由于主线程的操作在 onCrealinuxte/onStart/onResume 等回调中的操作耗时过长,而导java编译器致 ANR 。Looper.loop() 不会导致运用卡死;
  3. 当 Handler 发送音讯后,会在 java 层将音讯入队到 MessageQueue,假定线程现已休眠就会经过 eventfd 和 epoll的方法java编译器唤醒;
  4. 在 native 层也有一套 Handler/Loopjava名优馆在线六区er/MessageQujava名优馆在线六区eue 的音讯机制,而且 Java 层的获取音讯时,有必要比及 native 层的音讯处理结束之后才会将堵塞翻开。