常用场景:
子线程发送Message
主线程处理Message
子线程发送消息
java
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
java
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()
java
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.");
}
......
}
- prepare创建 Looper 并设置给了sThreadLocal 后面loop中要获取
2.prepare创建不可退出的MessageQueue(因为在主线程)
Looper.loop()
java
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)
- 消息回收(消息复用) msg.recycleUnchecked()
流程:应用启动-->ActivityThread main 启动 --> 准备Looper --> Looper死循环 一直取队列中的消息 -->处理消息 --> 消息回收处理
PS: 应用异常(Runtime)时压栈最底下ActivityThread.main() 倒数第二行Looper.loop() 这也印证了启动的顺序
Handler创建
常见创建:
java
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
mLooper = Looper.myLooper();//获取Looper
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 interface Callback {
boolean handleMessage(@NonNull Message msg);
}
消息创建
new Message
java
/** 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() 去创建
java
方法一:
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 flag
sPoolSize--;
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
java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //绑定动作
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
总结:
handler 机制就是一个传送带
1 ) MessageQueue 就是堆放货物履带。
2 )Activity Thread电机给履带滚动的动力
3)loop中的死循环就像开关,死循环开启履带滚动 轮询 messageQueue按照时间分发出去
4 )Message就是要传输得货物
同步问题
Handler是如何保证自己是线程安全?
从总结图可以看出整个过程只要保证MessageQueue 中msg的入队和出队即可
enqueueMessage方法中 通过synchronized (this) {} 锁住了关键的代码块
1,synchronized 是内置锁 锁的lock 和unlock 是系统(JVM)执行
-
锁的是this,就相当于锁的是 MessageQueue 相当于 调用同一个MessageQueue的对象是互斥的
-
一个线程持有一个Looper,一个Looper持有一个MessageQueue
所以:主线程只有一个MessageQueue子线程 发送消息的时候,子线程一次只能处理一个消息,其他的消息需要等锁,这样就保证了不管有多少子线程发送消息 主线程的MessageQueue时刻始终只处理一个消息的入队
next() 方法中 同样通过synchronized (this) {} 锁住了按照时间节点返回消息的关键代码
既然这里都是一直要放回当前队列的时间最靠前的msg(头消息),加锁的意义在哪里?
这里就是 锁this的魅力 锁住了MessgaeQueue,锁的范围是所有 this正在访问的代码块都会有保护作用,即代表next方法和enqueueMessage方法能够实现互斥统一时间MessageQueue只能入队或者出队这样就保证了MessageQueue的有序性。
HandlerThread
首先我们看下下面这段代码创建Handler
java
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 且线程安全?
java
public class HandlerThread extends Thread {
Looper mLooper;
@Override
public 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 - 掘金