源码--Handler 机制

本文深入探讨Android Handler机制,涉及Handler、Message、MessageQueue和Looper的工作流程。通过源码分析,解释了如何使用Looper.prepare()和Looper.loop()初始化和启动消息循环,以及在消息队列无消息时线程如何被阻塞。文章还提到了为何Loop的死循环不会阻塞主线程的关键点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章未仔细梳理

Handler 机制大致描述:

 这个机制主要牵涉到类 Handler、Message、MessageQueue 和 Looper.  

 这个机制大致工作流程:

    ① 执行 Looper.prepare();方法,为当前线程准备Looper对象;

   ②接下来就是往消息队列中插入消息,也就是我们熟悉的handler.sendMessage(new Message()),我们每插入一条消息          sendMessage(msg),Looper.loop()方法里面就会取出来并分发。

    ③ 执行 Looper.loop();方法,这个方法里面有一个for 循环,一直从消息队列(MessageQueue)中取消息(Message),如果消息队列中存在消息,就从Message中取出target(Handler类型)执行msg.target.dispatchMessage(msg),最终执行handler.handleMessage(msg);

 

Handler机制源码分析:

1. Looper.prepare();

进入 Looper.prepare() 方法,调用prepare(true);

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}

    ①可以看到static修饰的sThreadLocal成员变量,用于存放与线程相关的Looper对象(Looper是如何与线程关联的?下面讲解)

     ②如果 sThreadLocal.get()不等于null,则抛出异常,这里表明一个线程只能有一个Looper对象,一个线程不能多次准备                       Looper,即在同一个线程里不能多次调用Looper.prepare();

      ③ sThreadLocal.set(new Looper(quitAllowed)),线程的Looper对象就是在这里设置的

 1.1接下来分析sThreadLocal.set(new Looper(quitAllowed))源码

public class ThreadLocal{
 ...
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
 ...

}

①可以看到首句代码获取了当前线程对象 t  --> 然后执行ThreadLocalMap map = getMap(t);(其实就是Thread中的成员threadLocals),

如果map不为null,就将value(我们传过来的looper对象)设置进去,

如果为null,就执行createMap(t, value)创建ThreadLocalMapd对象赋值给t.threadLocals,而firstValue(Looper对象)则传入ThreadLocalMapd中,这样当前线程就保存关联了一个looper对象。

--------------------------------------------------

2. 进入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
            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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            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.recycleUnchecked();
        }
    }

①在loop()方法中,首先执行 final Looper me = myLooper(); 这里获取当前线程的Looper对象,

public class Looper{

    // 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();
    }
}

public class ThreadLocal{
 
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

}

在myLooper()方法中调用的是sThredLocal.get() (sThreadLocal前面已经出现过),

在get()方法中,首先获取当前线程,然后执行 ThreadLocalMap map = getMap(t);前面已经知道这个方法返回的是 Thread的成员threadLocals,这个成员是在Looper.prepare()中初始化的,并且它存放了一个Looper对象,所以sThredLocal.get()最终返回的是当前线程的Looper对象。

②回到我们的Looper.loop()继续往下看,如果 myLooper()返回null, 则说明并没有为当前线程准备Looper对象(没有执行Looper.prepare()方法),抛出异常。

 然后执行 final MessageQueue queue = me.mQueue; 这里获取的是Looper中的消息队列(handler.setMessage(msg)消息插入的地方),

接下来就是一个for循环,一直在获取消息,并分发消息

 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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            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.recycleUnchecked();
        }

在Message msg = queue.next();这里,当消息队列里面没有消息时,next()方法也是阻塞的。

所以,在消息队列中没有消息时,线程是处于阻塞状态的。

为什么loop的死循环不会阻塞住主线程?

https://www.jianshu.com/p/8c829dc15950

https://www.jianshu.com/p/4fac27284475

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值