Android Handler 消息机制

常用场景:

子线程发送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.");
        }
......
}
  1. 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)

  1. 消息回收(消息复用) 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)执行

  1. 锁的是this,就相当于锁的是 MessageQueue 相当于 调用同一个MessageQueue的对象是互斥的

  2. 一个线程持有一个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 - 掘金

相关推荐
Erwooow31 分钟前
Android 16k jni修改
android
l软件定制开发工作室1 小时前
基于Android的景点旅游信息系统App
android
张可1 小时前
一个KMP/CMP项目的组织结构和集成方式
android·前端·kotlin
林林要一直努力1 小时前
AOSP Settings模块问题初窥
android·学习·bug·android studio
顾林海1 小时前
Android 性能优化:启动优化全解析
android·java·面试·性能优化·zygote
钟智强4 小时前
Flutter 前端开发中的常见问题全面解析
android·前端·flutter·ios·前端框架·dart
解牛之术5 小时前
Android展示加载PDF
android·pdf
peakmain95 小时前
AGP 8 下TheRouter和bcprov的神坑
android
whysqwhw5 小时前
OkHttp-TLS 模块概要分析
android
byte轻骑兵6 小时前
【Bluedroid】蓝牙协议栈enable流程深度解析
android·c++·bluedroid