简述1: Android机制中有个限制,子线程是不能够访问UI的,否则会报错。而主线程中不允许执行耗时操作,因为如果在一定的时间没有处理完,就会阻塞主线程而出现ANR异常。所以一些耗时的任务,比如IO流读写,网络请求等操作就需要创建一个子线程中去处理,任务结束后如果涉及到UI的更新操作,就必需再切换到主线程中去做后续的处理。而这个线程的切换就用到了Handle这么一个机制。
简述2:Hander机制的四个重要成员:Looper Handler Message MessageQueue。大致流程就是 :
1.先在在当前线程中,创建一个Looper对象,此时在其内部也会创建一个MessageQueue,并把这个Looper对象保存到当前线程的sThreadLocal中。
2.在当前线程中,创建一个Hander对象并重写handMessage()方法,创建Handler对象的过程中会从sThreadLocal取到当前线程的looper对象以及其中的MessageQueue,这样handler对象中就有这个MessageQueue了。
3.Looper.loop(),进行轮询即是不停的取Looper内部的MessageQueue中的消息,如果消息队列中没有消息则会阻塞。
4.当在某个其他的线程中调用handler.handeMessage(msg)发送一个消息的时候,这个msg就会把这个handler包装到msg中,并将这个msg添加到这个MessageQueue中。此时创建此handler对象的线程中,对应的Looper就会轮询到这个msg,并调用这个handler的handleMessage()方法去处理对应的逻辑。
5.由于这个looper的轮询是在创建这个looper对象的线程中,所以就实现了线程的切换。
注意:
1.handler发送的消息,做所以能被对应的looper轮询到,是因为他们都拥有同一个MessageQueue。
2.之所以不会出现不同线程的handler和looper的错乱,是因为Looper的存取是通过sThreadLocal进行的。Handler和Looper基于同一个线程创建的,并共有同一个消息队列。
上面叙述的流程其实也包含了一些原理,下面我们来分析一下源码去更好更全面的理解这些步骤:
1.首先Looper的创建是通过调用Looper.prepare();方法:
public static void prepare() { prepare(true); } //初始化Looper,并把这个对象存储到sThreadLocal这个对象中去。 private static void prepare(boolean quitAllowed) { //quitAllowed 代表是否允许Looper的退出。 一般都允许,主线程除外。 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
//下面代码是创建Looper对象,此时构造参数中会创建mQueue消息队列,并得到当前的线程mThread。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
ThreadLocal知识点:这个类是用来存储各种泛型的数据对象,和其他存储工具类不一样的是,同一个mThreadLocal对象在不同的线程中进行数据的存取操作是相互独立的,即是每个线程都有这个mThreadLocal副本,他们各存各的,各用各的,互不干扰。Looper类中的sThreadLocal这个对象是静态全局的变量,他可以在各个线程中对创建的Looper对象进行存储和获取。他互不干扰的原因就是他存取使用的key是当前的线程,即实现了Looper和Threader的一一对应。下面看看源码吧:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); //存储数据:获取当前的线程,然后调用getMap(t)方法获取ThreadLocalMap对象 public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
//然后根据当前线程 t 取出这个线程t的ThreaLocaMap对象,然后调用 set方法进行存储
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
private void set(ThreadLocal<?> key, Object value) { Entry[] tab = table; //创建一个数组 int len = tab.length; int i = key.threadLocalHashCode & (len-1);//根据当前的sThreadLocal,通过算法计算出一个i即是index角标 tab[i] = new Entry(key, value); //然后将此线程中的sThreadLocal和Looper封装成Entry并赋值给table数组的index int sz = ++size; //到此及完成了存储 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
如果是第一次用,getMap为空,需要重新创建一个ThreadLocalMap对象,并赋值给这个r的threadLocals变量,在new对象的过程中完成存储,和上面一样的。
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }
思路:获取当前线程 ---> 然后找这个线程对应的ThreadLocalMap对象 -----> 在对象获取数组table---->将value封装成生成Entry并存储到table[i]里。
取数据和存数据原理一样就是逆着来:所以同一个sThreadLocal对象,在不同线程中取到的只是该线程下的looper对象,不会出现错乱,looper不错乱了。
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(); }
2.Handler的创建
情况一:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
源码:
public Handler() {
this(null, false);
}
//这个过程就将looper和handler绑在了一条线程上,并共用一个messageQueue消息队列。由于一个线程的looper是确定的,所以这个线程的handler也是确定的。
public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper();//获取当前线程的Looper if (mLooper == null) { //所以在此线程中创建handler之前一定确保此线程中已经创建looper对象 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
情况二:实现了Handler.Callback这个接口中的handleMessage()方法
Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
情况三:此时handler所在的线程即是Looper.myLooper()所在的线程。
Handler handler=new Handler(Looper.myLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } }) 或 Handler handler=new Handler(Looper.getMainLooper()){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } };
3.发送消息:
情况一:send发送
handler.sendEmptyMessage(100);
情况二:其实也是把Runnable打包到Message中去了,即是Message.callback==Message.runnable,然后再send发送
handler.post(new Runnable() { @Override public void run() { } });
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
所以我们接着看send方法:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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); }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
//MessageQueue消息队列添加发送过来的消息,如果之前处于阻塞状态,则会激活。否则就直接往里面添加消息。
//MessageQueue 是通过单链表的结构来维护消息的,效率高。
boolean enqueueMessage(Message msg, long when) {
.......
synchronized (this) {
if (mQuitting) {//如果looper调用了quie ,消息队列也调用quit(),此时mQuitting=true消息队列退出并释放。
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 消息队列为空的时候,指针指头
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;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
//如果之前MessageQueue是处于阻塞状态,当有新消息插入的时候,会唤醒MessageQueue,从而继续轮询读取 Msg和处理
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
4.Looper.poop();轮询读取MessageQueue中的消息,并且进行处理。先进添加的先处理。
public static void loop() { final Looper me = myLooper(); final MessageQueue queue = me.mQueue; for (;;) { Message msg = queue.next(); // 从当前线程获取looper,并得到messageQueue,然后调用其next()不停的取数据 if (msg == null) { // 如果队列中没有消息,则messageQueue会阻塞,从而导致loop()的阻塞 return; } ............... //如果有消息就会调用msg 中的handler(即是发送消息的handler)中的dispatchMessage(msg)去处理。 msg.target.dispatchMessage(msg); .............. } 先看消息队列是咋取消息的:
Message next() { ....... for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //第二个参数代表等待多久又返回。0则无需等到直接返回,如果是-1 则会阻塞。直到重新有消息添加唤醒。 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { ..... if (msg != null) { if (now < msg.when) { 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 { //没有消息则为-1,下次循环便会阻塞 nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } } }
再看看handler.dispatchMessage()是咋处理消息的:(他是在loop轮询的线程处理的,也就是创建looper,handler的线程,所以便实现了切换线程的目的)
public void dispatchMessage(Message msg) { if (msg.callback != null) {//如果是post(Runnable),此时msg.callback==runnable,则执行runnable任务 handleCallback(msg); } else { if (mCallback != null) {//如果创建handler的构造传handle.CallBack,则优先执行其中的handMeaasge(msg) if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//否则执行 重写的handMessage(msg); } }
Looper的退出:
public void quit() { mQueue.quit(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();//安全退出,执行完所有的msg,队列退出 } else { removeAllMessagesLocked();//直接退出 } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
注意:如果是手动创建的Looper,则任务执行完需要调用quit()退出,否则Looper会一直处于轮询状态,消耗性能。
到此也就算详细的走了一遍流程,包括涉及到的一些原理。可以根据这个流程跟一下主线程是咋回事,当然了主线程也是按照这个原理工作的,只是他比较特殊,有专门的方法去初始化这块机制,另外还不让执行Looper的退出。