本文是在阅读张鸿阳的深入理解 Looper、Handler、Message三者关系的一个自我学习总结
Looper
Looper主要作用:
- 绑定当前线程,new一个MessageQueue并绑定 ,且保证一个线程只有一个Looper
- 不断进行消息轮询,有则分发,无则阻塞
Looper主要是prepare()和loop()两个方法。
prepare()方法
** 作用:初始化Looper,保证一个线程初始化一次**
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true));
}
sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。将一个Looper的实例放入了ThreadLocal,判断sThreadLocal是否为null,否则抛出异常。** 说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例.**-
构造方法Looper()
** 作用:绑定当前线程,new一个MessageQueue并绑定**private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
new了一个MessageQueue(消息队列)给全局变量mQueue,获取当前线程赋给全局变量mThread,在prepare()调用了构建方法.
-
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 Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); 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.recycle(); } } public static @Nullable Looper myLooper() { return sThreadLocal.get();}
a. 首先在sThreadLocal取一个Looper.
b. 然后不断轮询消息队列,当有消息取出的时候, msg.target.dispatchMessage(msg);msg.targer实际为Handle,那么这个** Handle怎样与Message关联,Message怎么进入消息队列的呢? **
c. 释放msg,msg.recycle();
Handle
-
构造方法Handle()**
作用:获取当前线程中Looper,获取 Looper的Queue赋给全局变量 **
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following 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 that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async;
}
-
发送消息sendMesssage(Message msg)
** 作用:使用全局mQueue发送消息,并且将msg.targer设为this(Handle),解释了Handle如何与MessageQueue,msg关联 **private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
-
接收处理消息
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
覆写Handle的handleMessage就能处理消息了.
Message,Messenger
Message作为传递中的数据载体.使用Message#obtian()进行构建,obj用Bundle传递序列化参数.
Messenger相当于Handle的封装,用于多进程间通信.
后记
面试上我有写这个,面试的时候老喜欢让我讲讲Handle,笔试碰到几率也高。我一般三句话就完了:
1.Looper轮询器,不断轮询,有消息取出,没则阻塞。
2.每个线程只有一个轮询器,消息队列。
3.message是消息载体。
这答案我自己听了都想哭。
所以,我回来再看一般,然后整理一个答案:
1.初始化Handle实例的时候获取当前线程的Looper轮询器。
2.将Looper中的消息队列赋值给Handle中对象中全局变量。
3.Handle利用消息队列发送消息,消息发的targer设置成自己。所以能够跨线程通信。
4.Looper的parper方法保证每个线程只初始化一次,构造方法绑定当前线程,new出消息队列。loop方法不断轮询消息,有读取,无阻塞。
还有会让你说说线程间通信,Handle是要讲的,用在主线程与子线程通信,因为子线程无法更新ui.
但是一般线程间的通信其实是共享同进程内存,读取共享的内存变量(大家都能访问到,比如全局变量,普通类的static变量)就能实现简单的通信了。