版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
一、源码分析
1.消息入队
我们比较常用的 Handle 的方法是 sendMessage 这个方法,查看其源码。
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
sendMessage 直接调用了 sendMessageDelayed 这个方法,继续查看 sendMessageDelayed 方法源码。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
sendMessageDelayed 调用了 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);
}
sendMessageAtTime 里面调用了 enqueueMessage 方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage 中调用了成员变量的 MessageQueue 的 enqueueMessage 方法,在这里实现了消息入队。
2.消息出队
在 Handle 的成员变量里有一个 Looper (轮询器),Looper 里面主要的方法是 loop,
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;
// 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
...
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
}
在里面使用了一个死循环进行轮询,不停的调用 queue 的 next 方法获取下一个,根据注解这个方法可能被阻塞,然后调用了 msg.target.dispatchMessage(msg), msg.target 就是我们创建的那个 Handle,即又调用了 Handle 的 dispatchMessage 方法。
Handle 的 dispatchMessage :
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
创建 Handle 的时候是可以传递一个回调,如果这个回调存在的话则调用该回调方法,正常情况下我们是没有进行设置回调,所以调用到了 handleMessage 这个方法。
Handle 的 handleMessage:
public void handleMessage(Message msg) {
}
Handle 的 handleMessage 是一个空实现,所以我们使用 Handle 的时候,需要对这个方法进行重写。
3.大体流程
Handler 发送消息,保存到 MessageQueue,通过轮询器 Looper 不停的进行轮询,获取 MessageQueue 中的消息,传递给 Handler,调用 Handler 的 handleMessage 方法。
这是一个消息的机制,并不是说 Handler 这个消息机制是用来更新 UI,主要的作用是在主线程与子线程之间进行通信,经典例子就是在子线程中调用 UI 主线程,进而更新 UI 界面。
4.Handler 与 Looper、MessageQueue
每一个 Handler 下有一个 Looper mLooper 和 MessageQueue mQueue,这个是在 Handler 的构造函数中获取到的,即一创建 Handler 就有的。
Handler 构造函数:
public Handler() {
this(null, false);
}
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;
}
Looper 是通过 Looper.myLooper() 获取到的。
Looper 的 myLooper:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal 是一个 ThreadLocal,ThreadLocal 是用来进行线程数据隔离,保证各个线程中 ThreadLocal.get() 获取到的是当前线程的对象。
在定义 sThreadLocal 上面有个备注,调用 ThreadLocal.get() 之前必须要先调用 prepare() 方法,否则将会返回 null。
Looper 的 prepare:
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));
}
sThreadLocal 的 set 是在 prepare 里面被调用的,即在这里完成 Looper 的初始化。
在 Looper 的 loop()方法中有使用到 Looper,所以 loop()方法肯定是在 prepare()之后调用。
public static void loop() {
final Looper me = myLooper();
...
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
loop()方法跟 prepare()方法的调用是在 ActivityThread (安卓程序的入口) 中的 main 函数被调用。
ActivityThread 的 main:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
}
Looper 的 prepareMainLooper:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
ActivityThread 的 main 先调用了 Looper 的 prepareMainLooper,在这里调用了 Looper 的 prepare 方法,然后再调用了 Looper 的 loop 方法。
注: 主线程创建的时候调用 Looper 的 prepare 方法,则创建了第一个 Looper,即安卓程序至少有一个 Looper。
Looper 有个 MessageQueue 的成员变量,这个在构造函数中初始化的,Handler 中的 MessageQueue 直接获取的是 Looper 中的,两个指的是同一个对象。
二、手写实现
根据上面的源码分析进行简单的手写实现 Handler 机制,这边不需要新建安卓项目,直接 java 项目即可。
1.Message
public class Message {
public int what;
public Object object;
Handler target;
@Override
public String toString() {
// TODO Auto-generated method stub
return object.toString();
}
}
Message 只存储三个数据,what、object 和 Handler,在安卓的 Handler 下 Message 还有进行复用机制的处理,可以一定程度上减小内存的损耗,优化性能。
2.Handler
public class Handler {
//轮询器
final Looper mLooper;
//消息队列
final MessageQueue mQueue;
public Handler() {
//获取当前线程的 Looper
this.mLooper = Looper.myLooper();
this.mQueue = mLooper.mQueue;
}
/**
* 发送消息,压入队列
*/
public void sendMessage(Message msg) {
msg.target = this;
mQueue.enqueueMessage(msg);
}
public void dispatchMessage(Message msg) {
handleMessage(msg);
}
public void handleMessage(Message msg) {
}
}
Handler 拥有两个属性,Looper 和 MessageQueue,根据源代码的实现,Looper 和 MessageQueue 在构造函数的时候初始化,Looper 为当前线程的 Looper(这个会在 Looper 这个类体现),MessageQueue 获取的就是 Looper 下的 MessageQueue 。
同时,还提供了主要调用的方法 sendMessage,和 Looper 出栈后消息回调的方法 handleMessage。在入栈的时候指定消息所属的 Handler 为当前 Handler。
3.Looper
public final class Looper {
//每一个线程都有一个 Looper
//Looper 对象保存在 ThreadLocal 中,保证线程数据隔离
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//一个 Looper 有一个消息队列
final MessageQueue mQueue;
private Looper() {
mQueue = new MessageQueue();
}
public static void prepare(){
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
/**
* 获取当前线程的 Looper
* @return
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
/**
* 轮询消息队列
*/
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) {
continue;
}
msg.target.dispatchMessage(msg);
}
}
}
Looper 使用 ThreadLocal 进行 Looper 的线程数据隔离,拥有一个 MessageQueue 属性,在构造函数的时候初始化。同时,Looper 在方法 prepare 中进行初始化,获取到当前线程的 Looper 只能通过方法 myLooper 进行获取。
核心代码是 loop 方法,先获取当前线程的 Looper,并判断不为空,保证在调用 loop 之前必须要调用 prepare 方法。然后就是一个死循环,不停的获取消息队列的下一个消息进行转发给 Handle 进行处理。
4.MessageQueue
public class MessageQueue {
//这边采用数组方式进行存储消息
Message[] mMessages;
//存放的索引
int putIndex;
//获取的索引
int getIndex;
//消息的个数
int count;
//互斥锁
private Lock lock;
//条件变量
private Condition noEmpty;
private Condition noFull;
public MessageQueue() {
//这边写死大小
this.mMessages = new Message[50];
this.lock = new ReentrantLock();
this.noEmpty = this.lock.newCondition();
this.noFull = this.lock.newCondition();
}
/**
* 加入队列
* @param msg
*/
public void enqueueMessage(Message msg) {
try {
lock.lock();
//消息队列满了,子线程停止发送消息,阻塞
while (count == mMessages.length) {
try {
noFull.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
mMessages[putIndex] = msg;
//等于 50,置 0
putIndex = (++putIndex == mMessages.length) ? 0 : putIndex;
count ++;
//添加了消息,通知 Looper 进行轮询
noEmpty.signal();
} finally {
lock.unlock();
}
}
/**
* 出队列
*/
public Message next() {
Message msg;
try {
lock.lock();
//消息队列为空,子线程 Looper 停止轮询,阻塞
while (count == 0) {
try {
noEmpty.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
msg = mMessages[getIndex];
//取完之后,消息置空
mMessages[getIndex] = null;
//等于 50,置 0
getIndex = (++getIndex == mMessages.length) ? 0 : getIndex;
count --;
//消费了消息,通知 Handler 可以继续添加
noFull.signal();
} finally {
lock.unlock();
}
return msg;
}
}
MessageQueue 跟源码的实现有点不一样。这边消息的存储使用一个数组进行实现,源码使用的是一个链表的结构实现 Message。
为了避免消息无限制的添加,内存不停增大,对消息队列大小进行限制,这边写死为50。
提供了 enqueueMessage 和 next 两个方法,分别实现了消息的入队和出队功能。按消息数组索引进行循环存储和读取。
考虑到两种特殊的情况:1.消息队列满了,子线程停止发送消息。2.消息队列为空,子线程 Looper 停止轮询。这两种情况下需要进行线程的堵塞,等待消息的入队和出队,使用了一个互斥锁进行管理实现。
5.HandleTest
public class HandleTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
final Random random = new Random();
//轮询器初始化
Looper.prepare();
//Handler 存放于主线程
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println(Thread.currentThread().getName()
+ "received " + msg.toString());
};
};
//在子线程进行调用 Handler
for (int i = 0; i < 10; i ++){
new Thread(){
@Override
public void run() {
while(true){
Message msg = new Message();
msg.what = 1;
msg.object = Thread.currentThread().getName()
+ "send message " + random.nextInt(100);
System.out.println(msg);
handler.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}.start();
}
//轮询器开启轮询
Looper.loop();
}
}
这是带 main 函数的类,入口,模拟 ActivityThread,先调用 Looper.prepare() ,再调用 Looper.loop(),中间进行新建子线程发送消息。可以在结果中看到,对应的 sendMessage 方法发送消息都有对应的 handleMessage 方法接收到并处理。
结果: