- handler的基本使用:
@OnClick(R.id.test_handler_activity)
public void testHandler(View view) {
handler.sendEmptyMessageDelayed(TEST1,1000);
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int what = msg.what;
switch (what) {
case TEST1:
startActivityBtn.setText("handle test");
break;
}
}
};
- handler源码分析:
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
实际就是封装了一个message对象,调用sendMessageDelayed方法。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
最终都会调用到这个sendMessageAtTime方法。
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);
}
注意,这个uptimeMillis指的就是什么时间点进行执行。是SystemClock.uptimeMillis() + delayMillis。我们这里默认把它当成delayMillis也行,因为仅仅只是加上一个固定值而已。
拿到mQueue消息队列,就调用enqueueMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 这里msg.target进行了赋值,后面有用到
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg就是我们sendMessage发过来的消息。最后会调用MessageQueue的enqueueMessage方法:
MessageQueue.java
// when就是上面的uptimeMillis,就是sendMessageDelayed调用时传的delay值
boolean enqueueMessage(Message msg, long when) {
// msg.target = this;就是当前handler对象
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 当前message是否正在使用中
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(); // 标记该message正在使用中
msg.when = when; // when是delay
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 {
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;
}
}
// 把message2放进链表的后面
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
......
}
return true;
}
上面enqueueMessage方法,主要就是做了一件事,就是把msg消息,放进队列里面。怎么放进队列里面,就会进行一系列的判断:
(1)首先判断队列的消息是否为null,第一次放message时。mMessages肯定为null。msg就是我们要发送的message。
msg.when = when; // 当前消息的when是delay
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;
}
所以走进这个if。让msg.next = p。 mMessages = msg。这样mMessages链表里面就存放了第一个message。
(2)判断当前when<p.when。 when是指当前发送的消息延迟时间。p.when是指链表的第一个消息的延迟时间。如果当前要发送的消息延迟时间更小。那就让msg.next = p。 mMessages = msg。让当前发送的消息排在链表的最前面。
(其实就是消息按when时间从小到大插入链表。)
(3)后面的else也是遵循这个规律,把消息放入链表中,按照when的大小,从小到大进行排列插入:
else {
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;
}
}
// 把message2放进链表的后面
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
当p == null。或者当前消息小于当前链表当前节点。那就退出for循环。然后把当前要发送的消息插入进链表就可以了。
if (p == null || when < p.when) {
break;
}
prev记录的是要插入的节点的前一个节点。prev.next就是下一个要指向的就是msg。msg.next = p那就成功把msg插入链表中。
msg.next = p; // invariant: p == prev.next
prev.next = msg;
扯这么多,就是为了做一件事,就是把当前要发送的message插入到链表适当的位置,何为适当位置,按照when从小到大插入进来。
为什么要用到链表结构。因为这中间做的插入操作较为频繁,相对于数组结构效率会提高很多。
- 那怎么执行到handlerMessage的呢?
其实我们在new Handler时,前面和后面,都执行了Looper.prepare和Looper.loop()方法。
从两处可以看到:
1、看Handler的构造方法:
public Handler(Callback callback, boolean async) {
.....
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;
}
这里看到
mLooper = Looper.myLooper();
如果mLooper循环器为空,那就会抛出异常。我们运行代码没有抛出异常,说明,在调用构造方法前,就已经初始化了mLooper对象了。看看Looper.myLooper方法:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
是通过ThreadLocal的get方法,拿到的looper对象。在这之前肯定是有地方set过的。
2、在ActivityThread的main方法。
Looper.prepareMainLooper(); // 入口函数main,已经帮我们写了Looper.prepare()
......
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); // 循环。
可以看到Looper.prepareMainLooper方法。我们去Looper.java看:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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方法这里,有进行了ThreadLocal的set方法。把Looper对象,设置进去。这个ThreadLocal本质上就是一个map存放键值对。而这个map,一个线程仅有一份map。而这个map的key是存放ThreadLocal对象,value存放的是looper对象。
为什么要这样做:
其实就是创建一个Looper,并且保证一个Thread线程中,只有一个Looper对象!
看ThreadLocal源码就知道:
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 void set(T value) {
Thread t = Thread.currentThread(); // 通过线程拿到map
ThreadLocalMap map = getMap(t); //拿到map集合
if (map != null)
map.set(this, value); // 设置looper对象
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
sThreadLocal就是Looper的一个常量。所以key值都是相同的,都是sThreadLocal常量对象。不一样的是,存放的map不一样。一个线程对应一个map。可以通过当前线程t,拿到当前线程的ThreadLocalMap对象。
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
拿到当前线程对象,就可以拿到对应的ThreadLocalMap对象。ThreadLocalMap就是Thread对象的一个成员变量。
Thread.java
// 一个线程对应一个
ThreadLocal.ThreadLocalMap threadLocals = null;
拿到当前线程对应的map之后,就可以map.set(this, value);。
以当前sThreadLocal对象作为key,以looper对象作为value。
总结:Looper.prepare方法就是做了一件事,创建Looper对象,设置进当前线程的map集合里面。
当我们创建handler实例时:
public Handler(Callback callback, boolean async) {
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 static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
就会去调用sThreadLocal.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();
}
通过当前线程对象,找到对应的map集合,sThreadLocal对象(key),拿到对应的entry组合,再通过entry获取到对应的value:
mQueue = mLooper.mQueue;
原来mQueue是在这里被赋值的,刚刚在sendMessage那里就看到你了!在sendMessageAtTime的时候就看到了。
- 最终究竟是怎么调用handlerMessage方法的:
讲了这么多,还是没到handlerMessage方法。那就是Looper的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;
.....
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
.....
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
.....
msg.recycleUnchecked();
}
}
myLooper拿到当前线程的looper对象,就可以拿到对应的队列,进入for无限循环语句,把队列里面的消息一个个next出来。然后调用:
msg.target.dispatchMessage(msg);
这个msg.target不就是刚刚handler对象嘛,去
Handler.java:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这不就调用了handlerMessage了。结果不重要,重要的是思路。
- 重点:
1、handler在sendMessage时,做了什么:
就是把消息放进链表中,按照发送的时间顺序从小到大插入进链表。频繁插入操作,使用链表比较合适。
2、handler是否保证一个线程,只有一个Looper对象。怎么保证的?
肯定要保证一个线程只有一个Looper对象。不然就会出现线程间消息接收错乱。出现脏读现象,就是已经接收处理完成的消息,在其他线程又处理一遍。
怎么保证一个线程只有一个Looper对象,用ThreadLocal。一个线程对应一份map集合。以ThreadLocal对象作为key,以Looper对象作为value。存放进这个map集合里面。后面要获取,可以根据当前线程对象,拿到对应的map集合,就可以拿到对应的looper对象。
- 我自己手写一个简易版的Handler框架。
1、把握核心要点:
(1)使用链表存储Message对象
(2)使用ThreadLocal存储Looper对象
public class MyHandler {
MyMessageQueue mQueue;
public MyHandler() {
// MyLooper.prepare();
MyLooper looper = MyLooper.myLooper();
if(looper == null){
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue= looper.mQueue;
}
public void handleMessage(MyMessage msg) {
}
public void sendMessage(MyMessage MyMessage) {
sendMessageDelayed(MyMessage,0);
}
public final boolean sendMessageDelayed(MyMessage msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, System.currentTimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(MyMessage msg, long uptimeMillis) {
MyMessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MyMessageQueue queue, MyMessage msg, long uptimeMillis) {
msg.target = this;
return queue.enqueueMessage(msg, uptimeMillis);
}
}
public class MyLooper {
static final ThreadLocal<MyLooper> sThreadLocal = new ThreadLocal<>();
public static void prepare() {
sThreadLocal.set(new MyLooper());
}
MyMessageQueue mQueue;
public MyLooper() {
mQueue = new MyMessageQueue();
}
public static void loop() {
MyLooper looper = myLooper();
for(;;){
MyMessageQueue queue = looper.mQueue;
MyMessage message = queue.next();
if(message == null){
return;
}
message.target.handleMessage(message);
}
}
static MyLooper myLooper() {
return sThreadLocal.get();
}
}
public class MyMessage {
public Object obj;
public MyHandler target;
public long when;
public MyMessage next;
}
public class MyMessageQueue {
private MyMessage mMessages;
public MyMessage next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
for (;;) {
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = System.currentTimeMillis();
MyMessage prevMsg = null;
MyMessage 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);
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up
// when it is ready.
} else {
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
return msg; // 拿到了当前要处理的消息
}
} else {
return null;
}
if (pendingIdleHandlerCount <= 0) {
continue;
}
}
}
}
public boolean enqueueMessage(MyMessage msg, long when) {
synchronized (this) {
msg.when = when;
MyMessage p = mMessages;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
} else {
MyMessage prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
return true;
}
}
记得要在Handler构造方法之前,调用Looper.prepare方法,创建好Looper对象。然后在sendMessage之后调用Looper.loop方法,轮询器开始进行轮询消息。
- 享学总结:
1、MessageQueue是负责管理message消息的容器,线程间内存是共享的,所以主线程和子线程可以共享MessageQueue这个消息队列。所以主线程在main函数调用Looper.loop保证死循环不退出,就是不断地循环从MessageQueue取出消息,将取出的消息传递到handler.dispatch去处理取出来的消息。(跨线程通信的背后逻辑:内存共享,这里的内存共享指的是主线程和子线程之间内存共享)
2、Handler的内存泄露:
(1)涉及了JVM的垃圾回收,判断对象是否需要回收的两种方法
(2)handler匿名内部类,持有外部类的Activity.this对象。而Message又持有Handler对象。MessageQueue又持有Message对象。Looper持有MessageQueue。ThreadLocal持有Looper对象。(等下放源码就可以很清晰)
持有链:static sThreadLocal --> Looper --> MessageQueue --> Message --> handler --> Activity.this。
sThreadLocal就是静态对象,即GCRoot生命周期和跟随整个应用程序。所以在这条GCRoot链相关的对象,都不会被垃圾回收。当Activity执行onDestory时,里面的message还没有被拿出来处理,那MessageQueue还是会持有着Message对象,这样activity对象是不会被回收掉的,那就会内存泄露了。只有当Message消息处理完了,Message对象才不会被MessageQueue持有。
(3)解决Handler内存泄露的方法:
①让Handler设为static静态匿名内部类,静态对象不具备持有外部类对象,如果真的需要外部类Activity对象,可以通过Handler构造方法,传入Activity对象,并且使用弱引用持有Activity对象。
MyHandler myHandler = new MyHandler(this);
private static class MyHandler extends Handler {
private final WeakReference<Activity> weakReference;
public MyHandler(Activity activity) {
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
②在Activity执行distroy时,把当前activity的message消息从MessageQueue从remove掉。
只要打断其中一个持有链,就可以回收Activity对象了。
匿名内部类,持有外部对象的引用:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void testHandler(View view) {
handler.sendEmptyMessageDelayed(TEST1,1000);
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int what = msg.what;
switch (what) {
case TEST1:
Log.i(TAG, "handleMessage: ");
break;
}
}
};
}
MessageQueue.java的enqueueMessage方法:
使得MessgaeQueue持有msg对象的引用。
mMessgaes = msg;
....
prev.next = msg;
3、为何主线程可以new Handler? 如果想要在子线程new Handler该做什么准备?
(1)ActivityThread的main方法就是在主线程上运行的。main方法上,做了Looper.prepare,初始化Looper对象。以及在创建了handler对象之后,做了Looper.loop方法,可以轮询队列消息。Handler在初始化时,会去拿Looper对象:
public Handler(Callback callback, boolean async) {
.....
mLooper = Looper.myLooper(); // 关键!!!!
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
....
}
如果想要在子线程创建handler对象,在创建handler对象之前没有做Looper.prepare动作,就会抛异常。 在创建handler对象之后没有Looper.loop就不会从消息队列取出消息进行对应处理,整个handler就不会运转起来。
4、子线程创建handler涉及并发同步问题。因为想拿到子线程创建Handler对应的Looper对象,会有可能出现并发问题:
public class HandlerActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handlerThread.start();
}
MyHandlerThread handlerThread = new MyHandlerThread();
Handler handler1 = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
public class MyHandlerThread extends Thread{
private Looper looper;
@Override
public void run() {
super.run();
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
public Looper getLooper() {
return Looper.myLooper();
}
}
两个线程,主线程先执行了getLooper方法,子线程再执行MyHandlerThread里面的run方法。那很有可能主线程getLooper拿到的looper对象是为null,因为子线程还没执行run方法,looper.prepare还没执行,即还没初始化。
解决方法:SDK已经提供了一个类叫HandlerThread。让getLooper方法获取looper对象和 run方法中初始化looper对象,进行互斥访问。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); // 互斥访问
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
// 内置锁 锁的互斥访问机制
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
(1)wait立刻阻塞状态,会释放锁对象。sleep挂起,不释放锁,等待指定时间。
(2)notifyAll唤醒所有wait代码,因为有可能有多处地方调用getLooper方法,那就有多个wait方法需要唤醒,而notify只是唤醒一个wait代码
(3)为什么使用while循环,而不是if语句:因为有可能在别处没有初始化mLooper变量的地方使用了synchronized代码,并且用的锁是同一个对象,并且在里面调用了notifyAll,从而把getLooper方法给唤醒,如果使用if语句,那被唤醒之后,mLooper变量还是为null。使用while可以防止别处地方唤醒导致mLooper变量为null。
5、如果不同的handler处于不同线程中,如何保证存消息和取消息线程安全?(注意:子线程可以创建Handler,一个线程可以创建多个Handler对象,但一个线程只能对应一个Looper对象,这是ThreadLocal机制,一个Looper对应一个MessageQueue。但是可以多个线程拿到同一个Looper对象来进行存取消息,因为ThreadLocal线程隔离一般不这么做):
(1)在enqueueMessage加上synchroized锁,只能同时有一个线程,往消息队列添加消息。
(2)next取消息,也是要加synchroized锁,目的是存消息和取消息是互斥访问的,保证存和取消息同时进行时是线程安全的。
MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 多个线程,往一个MessageQueue里面添加消息。 同步锁,线程安全
synchronized (this) {
// 存消息
}
return true;
}
Message next() {
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
// 存消息和取消息,不是在同一个线程,保证存消息和取消息互斥访问,在同一瞬间只有一个线程在存或者取
synchronized (this) {
// Try to retrieve the next message. Return if found.
//取消息
return msg;
}
}
}
6、Handler的消息Message对象,是怎么管理的?是直接new创建Message对象,然后处理完消息之后,就直接交给JVM垃圾回收对象吗?:
创建消息是用obtain方法,obtain方法就是负责从sPool池子中取Message对象,如果池子是没有Message对象的,才手动new一个Message对象。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
消息对象在Looper.loop使用完了,要进行回收,回收并不是交给JVM垃圾回收对象。而是把消息内容清空,并存入sPool池中。
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
享元模式。对象复用,防止频繁创建对象回收对象导致内存抖动和内存碎片的问题。
7、Looper死循环会不会导致卡死?:消息队列里面没有消息,就让它休息睡眠,把CPU资源让出去,和ANR没关系。(ANR是什么?)
8、什么是epoll。非阻塞忙轮询和阻塞轮询是什么?:
(1)非阻塞轮询,就是不管IO流里面有没有数据,都会一直遍历所有的流,一直忙着在for循环中。如果流没有数据的话,就一直在for循环,一直不会进入if语句处理IO。这样只会浪费CPU资源
while(true) {
for(i -> stream[]){
if(has data) {
// read data until unavalilable
}
}
}
(2)阻塞轮询,使用select函数:
在for循环之前,加一个select语句,可以检测到所有的流中有IO事件发生,就会进入for循环来遍历所有的流。select方法只要有IO事件发生就遍历所有的流,并不知道具体是哪一个流有IO事件。这样只能无差别轮询所有的流,比非阻塞轮询稍微好一点,但一样浪费CPU
while(true) {
select(stream[])
for(i -> stream[]){
if(has data) {
// read data until unavalilable
}
}
}
(3)而epoll,它比select更好一点,当有IO事件发生时,它可以知道具体哪些流发生了怎样的IO事件。这样进入for循环轮询,只轮询有发生IO事件的流就好了
while(true) {
active_stream[] = epoll_wait();
for(i -> active_stream[]){
// read data until unavalilable
}
}
handler中创建MessageQueue对象,就会调用nativeInit方法,初始化epoll。
在MessageQueue的next方法:
nativePollOnce就是刚刚的epoll_wait判断是否需要阻塞。
for(;;){
//....
nativePollOnce(ptr,nextPollTimeoutMillis);
}
在enqueueMessage方法中:
有消息添加进队列,通过nativeWake来唤醒looper循环机制。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
9、同步屏障:
UI刷新消息的优先级是最高的。屏障消息就是target为null的消息。
Message分为三类,同步消息、异步消息、屏障消息。
在ViewRootImpl有一个scheduleTraversals发送一个异步消息(先发送了一个屏障消息,优先级最高。),再setAsynchronous设置为异步消息。
next取消息,怎么取的。仍旧从队列头开始取,