消息机制Handler及相关源码分析

一、Handler

1、工作原理:
Handler发送消息是往消息队列MessageQueue中插入一条消息,轮询器Looper会调用loop方法进行轮询,获取到消息后将该消息交给Handler处理。
2、发送消息(调用send和post的相关方法发送消息)
调用sendMessage,然后调用sendMessageDelayed,又调用了sendMessageAtTime。调用post方法,最终也是调用sendMessageAtTime方法。
public boolean sendMessageAtTime(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);
}
会调用MessageQueue的enqueueMessage方法将消息放到消息队列中。Looper发现新消息,会处理。
3、接收消息
#Handler
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);//是一个Runnable对象,实际是Handler的post传递的Runnable参数
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
4、Handler实例化
通过特定的Looper来构造Handler
public Handler(Looper looper) {
    this(looper, null, false);
}

二、Looper:不停的从消息队列MessageQueue中查看是否有新消息。


1、创建Looper。每个线程最多只能有一个Looper对象。
(1)Looper.prepare();  
#Looper
public static void prepare() {
    prepare(true);
}

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);//创建一个MessageQueue.
    mThread = Thread.currentThread();
}
(2) Looper.prepareMainLooper:主线程创建Looper,本质也是通过prepare方法创建
2、获取Looper
(1)Looper.getMainLooper 获取主线程的Looper
(2)Looper.myLooper()获取当前线程的Looper实例
3、退出Looper
#Looper
(1)直接退出Looper
public void quit() {
    mQueue.quit(false);
}
(2)设置一个退出标识,将消息队列的已有消息处理完毕才能安全退出。
public void quitSafely() {
    mQueue.quit(true);
}
会调用MessageQueue的quit方法通知消息队列退出,MessageQueue的next方法返回null。
退出后Handler发送的消息会失败。在子线程中创建Looper,在所有消息完成后调用quit方法来终止消息循环。
4、进行轮询
Looper.looper
public static void loop() {
    .....
    //无限循环
    for (;;) {
        //调用MessageQueue的next方法取值
        Message msg = queue.next();
        if (msg == null) {
            //木有消息了就跳出循环
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        //msg.target指发送消息的Handler,交给Handler的dispatchMessage方法
        msg.target.dispatchMessage(msg);
        .....
    }
}
5、数据存取
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
通过 ThreadLocal可以实现Looper在线程的数据存取。
原理: Looper作用域是线程,不用线程有不用的Looper。而ThreadLocal是一个线程内部的数据存储类。可在指定线程存储数据,其他线程无法获取。
(1)ThreadLocal使用:
ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<>();
public void test() {
    Log.e(TAG, "mainThread:" + mThreadLocal.get());  //主线程 结果是false

    new Thread() {
        @Override
        public void run() {
            super.run();
            mThreadLocal.set(false);
            Log.e(TAG, "thread1:" + mThreadLocal.get());  //线程1 结果是false
        }
    }.start();

    new Thread() {
        @Override
        public void run() {
            super.run();
            mThreadLocal.set(true);
            Log.e(TAG, "thread2:" + mThreadLocal.get());  //线程2 结果是true
        }
    }.start();
}
(2) ThreadLocal的set方法:
public void set(T value) {
    Thread currentThread = Thread.currentThread();//获取当前线程
    Values values = values(currentThread);  //Value的类型是ThreadLocal.Value
    if (values == null) {    //value为null,进行初始化
        values = initializeValues(currentThread);
    }
    values.put(this, value); //调用value的put方法存值
}
//内部存在obect类型的数组
static class Values {
    private Object[] table;
}
(3) ThreadLocal的get方法:
public T get() {
    // Optimized for the fast path.
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread); //获取当前线程value对象
    if (values != null) {
        Object[] table = values.table;     //取出value的table数组
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];   //table的下一个位置存得值就是ThreadLocal的值
        }
    } else {
        values = initializeValues(currentThread); //初始化
    }

    return (T) values.getAfterMiss(this);
}
set和get都是操作的当前线程的value对象的table数组。

三、MessageQueue:

内部以单链表的形式存储数据,包含两个操作,插入enqueueMessage方法和读取next方法。
next是一个无限循环的方法,如果没消息就一直堵塞。如果有新消息到来,从消息队列中取出一条数据,并将其在队列中移除。

四、主线程的消息循环

1、主线程的入口是ActivityThread的main方法
public static void main(String[] args) {
    .....
    //创建主线程的Looper和MessageQueue
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    //获取handler
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    //looper进行轮询
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
返回的Handler是ActivityThread.H。包含了四大组件启动和停止的过程。
private class H extends Handler {}
主线程的消息循环模型:
(1)ActivityThread通过内部类ApplicationThread和ActivityManagerService进行进程间通信
(2)AMS会回调ApplicationThread的Binder方法
(3)ApplicationThread向H发送消息
(4)ApplicationThread将通过H将逻辑切换到ActivityThread中执行(主线程执行)

注意:
  • 一个Looper只能对应了一个MessageQueue。
  • 一个线程中只有一个Looper实例,一个MessageQueue实例,
  • 可以有多个Handler实例。

参考:Android开发艺术探索












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值