第10章-Android的消息机制

本文详细解析了Android中的Handler、Looper和MessageQueue的工作原理,包括它们的角色、交互方式以及线程间的数据存储类ThreadLocal。还介绍了Looper的loop方法如何处理消息,以及如何通过quit方法退出消息循环,防止线程阻塞。此外,文章探讨了主线程为何不会因loop方法的死循环引发ANR异常,涉及到Linux的pipe/epoll机制。
摘要由CSDN通过智能技术生成

Handler概述

Handler是Android消息机制的上层接口,他的作用不只是用于ui更新,而是整个系统的消息通知处理机制。在ActivityThread类中可以看出,应用启动时,会执行main方法,这是应用的入口,在main中创建了主线程的Looper,并且调用了loop方法,使应用一直处于运行状态。

Handler创建需要Looper的支撑,没有looper则会报错,而Looper的创建,是使用ThreadLocal进行存储,MessageQueue消息队列(单链表)在Looper创建时构建的。

Handler发送消息,MessageQueue存储消息,Looper中的loop方法检测消息了处理消息

Android的消息机制分析

ThreadLocal的原理

这是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储后,只有在指定线程中可以获取到存储数据,对于其他线程来说则无法获取到数据

set方法

保存数据时,首先根据当前线程,查找Thread中的ThreadLocal.ThreadLocalMap threadLocals变量是否有值,如果有值,则通过一系列操作进行保存,如果没有,则创建一个ThreadLocal.ThreadLocalMap。也就是说,设置的值是保存在当前线程的内部属性中,所以不同线程设置的值互不影响

    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;//这个属性是在Thread中的
    }

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

get方法

根据当前线程获取对应的值,如果没有,则返回null

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

例子

fun main() {
    val threadLocal = ThreadLocal<Int>()

    //set方法会根据当前的线程存储值
    threadLocal.set(10)
    //get方法根据当前线程获取是否有保存的值,如果有则获取,没有则返回初始值null
    println("main线程获取threadLocal的值:${threadLocal.get()}")//10

    Thread{
        threadLocal.set(20)
        println("${Thread.currentThread()}获取threadLocal的值:${threadLocal.get()}")//20
    }.start()

    Thread{
        println("${Thread.currentThread()}获取threadLocal的值:${threadLocal.get()}")//null
    }.start()
}

消息队列原理(MessageQueue,单链表数据结构)

MessageQueue是在Looper创建时被创建的,一个线程只能有一个MessageQueue

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

重要的两个操作:插入(enqueueMessage),读取(next),读取方法伴随删除功能

enqueueMessage(插入消息,链表操作)

boolean enqueueMessage(Message msg, long when) {
	...
	...
	 needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
	...
	...
}

next(取出消息,并删除)

Message next() {
...
...
	for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

			synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                }
	}
	...
	...
}

quit(boolean safe)

退出方法,实现逻辑是将下一条消息置为空next=null,这样在Looper的loop方法中检测到Message为空时,跳出死循环,结束检测Message

//Looper.loop方法
        for (;;) {
            Message msg = queue.next(); // might block,没有消息时,会阻塞
            if (msg == null) {//表示调用了quit方法
                // No message indicates that the message queue is quitting.
                return;
            }

Looper原理

私有构造函数,通过Looper.prepare设置,使用Looper.myLooper获取当前线程的Looper对象,一个线程只能有一个Looper对象。在同一个线程中多次调用prepare时,会出现异常

    private Looper(boolean quitAllowed) {
    	//创建消息队列
        mQueue = new MessageQueue(quitAllowed);
        //获取当前线程
        mThread = Thread.currentThread();
    }

Looper.prepare/Looper.myLooper

    private static void prepare(boolean quitAllowed) {
    	//一个线程只能有一个Looper对象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

Looper.prepareMainLooper方法在ActivityThread.main方法中,给主线程设置Looper了,如果在此线程中再次设置,则会异常。
获取主线程的Looper,使用Looper.getMainLooper。

loop

只有调用了loop方法,消息循环系统才会起作用。

        for (;;) {
            Message msg = queue.next(); // might block,没有消息时,会阻塞
            if (msg == null) {//表示调用了quit方法
                // No message indicates that the message queue is quitting.
                return;
            }
...
...

 try {
                msg.target.dispatchMessage(msg);//处理消息
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            }

只有当message为空时,才会跳出循环

quit退出

退出的原理是,将消息队列中的消息置空,从而loop方法中退出死循环,方法执行结束。Looper退出后,Handler发送消息会失败,send方法返回false。
如果在子线程中手动创建Looper后,需要在适当的时候退出Looper,否则在Looper调用loop方法,就会一直处于阻塞状态,从而导致改线程一直处于运行状态

子线程演示创建Looper

class ThreadLooper(private val context: Context) : Thread() {

    private var handler: Handler? = null

    override fun run() {
        super.run()
        Looper.prepare()
        handler = object : Handler(Looper.myLooper()!!) {
            override fun handleMessage(msg: Message) {
                if (msg.what == 100) {
                    Toast.makeText(
                        context.applicationContext,
                        "子线程创建的Handler:${Thread.currentThread().name}",
                        Toast.LENGTH_SHORT
                    )
                        .show()
                } else if (msg.what == 200) {//关闭线程
                    Looper.myLooper()?.quit()
                }
            }
        }
        handler?.sendEmptyMessage(100)
        Looper.loop()//开启后,会阻塞,以下代码不会执行
        //只有当Looper退出后,才会执行
        println("Looper已退出,线程继续执行...")
        handler?.post {//此代码不会执行,因为Looper已退出,消息机制终止
            Toast.makeText(context.applicationContext, "Looper已退出,线程继续执行...", Toast.LENGTH_SHORT).show()
        }
    }

    /**
     * 退出,发送消息,退出Looper
     */
    fun quit() {
        handler?.sendEmptyMessage(200)
    }
}

Handler原理

创建Handler时,必须有Looper对象,才可以创建,否则异常

public Handler(@Nullable Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //此消息队列是在Looper中创建的
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

调用handler的发送方法后,post/send最终都是调用sendMessageAtTime方法,接着调用enqueueMessage方法

enqueueMessage-往消息队列中插入消息

    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);//调用MessageQueue保存消息,单链表保存
    }

此时,如果Looper调用了loop方法后,就会一直获取消息列表中的消息,进行处理。在loop方法中,调用了Handler中的dispatchMessage方法进行处理

dispatchMessage-处理消息

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//如果Message的callback不为空时,则不会执行handleMessage
        }
    }

可以看出,创建Handler可以使用派生类创建,也可以直接创建

直接创建

val h = Handler(Looper.myLooper()!!,object :Handler.Callback{//创建callback
            override fun handleMessage(msg: Message): Boolean {
               return true
            }
        })

派生类创建

handler = object : Handler(Looper.myLooper()!!) {
            override fun handleMessage(msg: Message) {
              
            }
        }

面试题

为什么主线程一直处于死循环状态,但是不会导致主线程ANR异常?

这是因为pipe管道机制和epoll/pipe机制:在没有消息时阻塞线程并进入休眠释放cpu资源,有消息时唤醒线程
Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce() 方法里

当Looper中的loop被调用时,调用了MessageQueue.next的方法,next方法中没有消息时会阻塞,不会产生 ANR 异常, 那是因为此时 messageQueue 中并没有消息,无需处理界面界面更新等操作。
因此主线程处于休眠状态,无需占用 cpu 资源, 而当 messageQueue 中有消息时,系统会唤醒主线程,来处理这条消息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值