深入了解Android的Looper

之前我们说了使用Thread+Handler来处理子线程和主线程交互的问题, 今天我们来说下Handler的原理。这里面会涉及到Looer,和ThreadLocal的知识点,通常也是面试时的常见问题。

Handler和Looper

Handler我们都知道,它需要和Looper绑定,当Handler在主线程创建,则会默认绑定主线程的Looper,当是在子线程创建,则需要在Handler的构造方法里传入子线程的Looper的对象。

Handler mHandler;
Thread worker = new Thread(){
    @Override
    public void run() {
        Looper.prepare();
        mHandler = new Handler(Looper.myLooper()){
            @Override
            public void handleMessage(Message msg) {
                ...
            }
        };

        Looper.loop();
    }
};

private void sendMessage(Object obj){
    Message msg = new Message();
    msg.obj = obj;
    mHandler.sendMessage(msg);
}

上面是在子线程创建一个Handler的demo,通过这个Handler我们可以把消息发送到子线程,让子线程去进行对应的操作。 其实到这里只是知道如何用Handler和Looper来实现线程通信,想知道真正的原理,还需要看Looper的源码。

Looper的原理

我们先来看看Looper的构造方法,

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

所以Looper其实不暴露构造方法给外部,只通过 prepare()给外部调用,我们再看看prepare()方法

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

到这里就可以看出来,Looper通过public的prepare()方法,构造了一个Looper对象,并保存在sThreadLocal中。我们在不同线程里创建的所有Looper都会保存在它里面。下面是sThreadLocal在Looper里的代码,

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

每当我们用 Looper.myLooper()获取当前线程的Looper时,就会从 sThreadLocal 中获取。这里涉及到一个有趣的东西,ThreadLocal的使用保证了当前线程只能获取到当前线程创建的Looper,这是ThreadLocal的特性。

当我们调用Looper.loop()之后,当前线程对应的Looper就会循环不断的从MessageQueue中拿消息,并扔给Handler去处理,回过头来看Looper的构造方法,就可以看到MessageQueue对象了。

最后我们再简单看一下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;
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        ...
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
    }
}

到这里就明白,其实Loop就是开始一个无限循环,并从当前线程的Looper里去获取MessageQueue,然后从中读取Message并分发给Handler的过程。 当然这里面还有其他几个细节,我们留着下次继续分析,比如 1 既然loop()是个无限循环,它为什么不会造成资源无限消耗 2 ThreadLocal是怎么做到当前线程只能获取到它自己的Looper的

总结

Looper的原理可以总结如下, · 每个线程都可以创建一个Looper,它会保存在当前线程对应的ThreadLocal里 · Handler需要绑定对应线程的Looper对象 · 线程在创建完Looper后,需要调用Looper.loop()以让它循环的去读取并分发消息 · 跟Looper绑定的Handler会在接收到消息后在对应的线程里处理消息

以上就是Android线程交互的原理啦,希望下次面试遇到这个问题时能有帮助~

原文发布于微信公众号 - Android每日一讲(gh_f053f29083b9)

原文发表时间:2018-03-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏刘望舒

AsyncTask源码解析

导语 AsyncTask对Android开发者来说并不陌生,当有耗时任务并涉及UI交互,AsyncTask可是处理异步任务的利器。AsyncTask使用也很方...

2155
来自专栏知识分享

Android中AsyncTask的使用

https://blog.csdn.net/liuhe688/article/details/6532519

862
来自专栏Jack的Android之旅

AsyncTask源码深入解析

最近想写一篇关于源码解析的文章,发现AsyncTask代码量不多,可里面的东西却是很值得学习的,所以故那这来“开刀”

902
来自专栏Android 研究

Android Handler机制13之AsyncTask源码解析

AsyncTask是一个抽象类,我们需要创建子类去继承它,并且重写一些方法。AsyncTask接受三个泛型的参数:

831
来自专栏Phoenix的Android之旅

自带光环的HandlerThread

Thread之前说的多了,HandlerThread了解么,今天来说说他和Thread的区别

941
来自专栏aCloudDeveloper

internet 的一词多义

这是在《unix网络编程》中看到的比较全面的解释,在此作为一个整理。 一 是网际网,采用TCP/IP协议族通信的任何网络都是网际网,因特网就是一个网际网。 二 ...

1848
来自专栏CodingBlock

Android查缺补漏(线程篇)-- AsyncTask的使用及原理详细分析

本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8515304.html 一、AsyncT...

4427
来自专栏开发之途

Android多线程之AsyncTask源码解析

1273
来自专栏项勇

笔记38 | Android线程之解析Looper()和HandlerThread()

1645
来自专栏向治洪

android异步任务asyntask详解

在Android中实现异步任务机制有两种方式,Handler和AsyncTask。 Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Han...

2128

扫码关注云+社区

领取腾讯云代金券