常用场景:
子线程发送Message
主线程处理Message
子线程发送消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageDelayed(msg, delayMillis);}public final boolean sendEmptyMessage(int what){return sendEmptyMessageDelayed(what, 0);}
不管那种方式发送小时最终都走到 sendMessageAtTime
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
通过enqueueMessage将消息按照时间先手插入到MessageQueue中
主线程处理消息
应用启动:
frameworks\base\core\java\android\app\ActivityThread.java
main方法中关注Looper.prepareMainLooper()和Looper.loop().
Looper.prepareMainLooper()
public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {//Looper的唯一性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));}private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();//绑定当前线程}MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();}void quit(boolean safe) {if (!mQuitAllowed) {throw new IllegalStateException("Main thread not allowed to quit.");}
......
}
1. prepare创建 Looper 并设置给了sThreadLocal 后面loop中要获取
2.prepare创建不可退出的MessageQueue(因为在主线程)
Looper.loop()
public static void loop() {final Looper me = myLooper();......me.mInLoop = true;final MessageQueue queue = me.mQueue;for (;;) { //死循环Message msg = queue.next(); // might block......try {msg.target.dispatchMessage(msg);if (observer != null) {observer.messageDispatched(token, msg);}dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} catch (Exception exception) {if (observer != null) {observer.dispatchingThrewException(token, msg, exception);}throw exception;} finally {ThreadLocalWorkSource.restore(origWorkSource);if (traceTag != 0) {Trace.traceEnd(traceTag);}}......msg.recycleUnchecked();}}public static @Nullable Looper myLooper() {return sThreadLocal.get();}
1.Looper持有了当前线程的MessageQueue
2.通过queue.next() 获取当前时间下一次要执行的Message
3.处理消息 msg.target.dispatchMessage(msg) 分发到内部类中处理(msg.target就是当前消息绑定的Handler)
4. 消息回收(消息复用) msg.recycleUnchecked()
流程:应用启动-->ActivityThread main 启动 --> 准备Looper --> Looper死循环 一直取队列中的消息 -->处理消息 --> 消息回收处理
PS: 应用异常(Runtime)时压栈最底下ActivityThread.main() 倒数第二行Looper.loop() 这也印证了启动的顺序
Handler创建
常见创建:
Handler handler=new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}
};public Handler() {this(null, false);}public Handler(@Nullable Callback callback, boolean async) {mLooper = Looper.myLooper();//获取Looperif (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 interface Callback {boolean handleMessage(@NonNull Message msg);}
消息创建
new Message
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).*/public Message() {}
Google 没有详细说明new Message 当通过我们实际直接new 就完事了无需多言,不过Google推荐使用Message.obtain() 去创建
方法一:
Message msg = Message.obtain()
--------------------------------源码-------------------------------public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();}
方法二:
Handler handler = new Handler();
Message msg = handler.obtainMessage();//绑定了handler
--------------------------------源码-------------------------------
public final Message obtainMessage(){return Message.obtain(this);// this==handler}public static Message obtain(Handler h) {Message m = obtain();m.target = h;return m;}
不管那种方式创建最终都是走无参的obtain方法
1.sPool等于空时通过new 创建Message
2.sPool 是在Looper dispatch 出去后通过recycleUnchecked清空后到Message
3.sPool最前面的空Messsage返回回去,sPool指针后移队列到下一个保证下一个obtain可以正确获获取到sPool中的空Message
Message和handler的绑定
1.可以在创建Message是绑定,参考Message创建的方法二
2.消息发送是绑定
回到发送Message
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //绑定动作
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
总结:
同步问题
Handler是如何保证自己是线程安全?
从总结图可以看出整个过程只要保证MessageQueue 中msg的入队和出队即可
enqueueMessage方法中 通过synchronized (this) {} 锁住了关键的代码块
1,synchronized 是内置锁 锁的lock 和unlock 是系统(JVM)执行
2. 锁的是this,就相当于锁的是 MessageQueue 相当于 调用同一个MessageQueue的对象是互斥的
3. 一个线程持有一个Looper,一个Looper持有一个MessageQueue
所以:主线程只有一个MessageQueue子线程 发送消息的时候,子线程一次只能处理一个消息,其他的消息需要等锁,这样就保证了不管有多少子线程发送消息 主线程的MessageQueue时刻始终只处理一个消息的入队
next() 方法中 同样通过synchronized (this) {} 锁住了按照时间节点返回消息的关键代码
既然这里都是一直要放回当前队列的时间最靠前的msg(头消息),加锁的意义在哪里?
这里就是 锁this的魅力 锁住了MessgaeQueue,锁的范围是所有 this正在访问的代码块都会有保护作用,即代表next方法和enqueueMessage方法能够实现互斥统一时间MessageQueue只能入队或者出队这样就保证了MessageQueue的有序性。
HandlerThread
首先我们看下下面这段代码创建Handler
Thread thread = new Thread(new Runnable() {Looper looper;
@Override
public void run() {Looper.prepare();looper =Looper.myLooper();Looper.loop();
}
public Looper getLooper() {return looper;
}
});thread.start();Handler handler = new Handler(thread.getLooper());
子线程去获取Looper对象然后通过子线程拿到Looper,这段代码看似么有问题其实有雷运行时可能是出现new Hanlder 中参数Looper 为空
1.可能在执行new Handler对象时子线程没有走完导致looper没有赋值完成
解决:thread.start 后延时去new Handler 从而保证looper不为空,但此时线程依旧是不安全的
看看Goolge是如何解决子线程获取Looper 且线程安全?
public class HandlerThread extends Thread {Looper mLooper;@Overridepublic void run() {mTid = Process.myTid();Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}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.HandlerThread 是Thread的子类 new HandlerThread start 执行其run方法
prepare 后通过获取Looper关键代码
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
getLooper关键代码
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
1.通过synchronized (this) 实现了run和getLooper的互斥
2.两种情况
2.1 :run 先拿到锁
synchronized锁住的代码先执行,完完全全拿到Looper后通过notifyAll()通知所有 HandlerThread 对象 结束等锁,准备拿锁 ,然后释放锁
2.2:getLooper 先拿到锁
此时当线程活着时mLooper肯定为空线程执行wait() 等待且释放锁,getLooper 释放锁的同事run方法就会持有锁,因为HandlerThread 就这两个方法会获取锁。
如果是sleep() 线程会阻塞 不会释放锁。
此类写法就保证了不管什么样的情况下当你通过HandlerThread 去getLooper是一定能获取到线程的唯一Looper.此时线程是安全的。
消息机制之同步屏障
揭秘 Android 消息机制之同步屏障:target==null ?我们知道,Android的消息机制就是Handle - 掘金