Android--Handler,Looper,MessageQueue的关系

MessageQueue

对外提供插入删除的工作,但是只是消息的存储单元,并不能处理消息。

Looper—-android.os.looper

Looper填补了MessageQueue不能处理消息的缺陷,主要用Looper.loop()方法来死循环查找是否有新消息,如果有,就交给Handler去处理;若没有,则一直等待着。唯一跳出循环的方式是在当MessageQueue.next()方法返回null的时候(即只有当消息队列里没有消息了之后Looper才跳出循环)。另外,Handler创建的时候会采用当前线程的Looper来构造消息循环系统(从228行和229行可看出Handler在创建的时候会带上一个Looper对象和MessageQueue对象)。看框架源码android.os.handler:
Android--Handler,Looper,MessageQueue的关系_第1张图片
Android--Handler,Looper,MessageQueue的关系_第2张图片
需要注意的是,线程是默认没有Looper的,如果需要使用Handler就必须为线程创建Looper,我们经常提到的主线程,也叫UI线程,它就是ActivityThread,ActivityThread被创建的时候就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。

Handler—-android.os.handler

它的工作主要包括发送接收两个过程。通过查看源码可以发现,Handler发送消息的过程仅仅是向消息队列中插入一条消息,然后MessageQueue.next()方法就会返回这条消息给Looper,Looper收到消息后就开始处理了。最终消息(Message)由Looper交由Handler处理(即Handler.dispatchMessage()方法会调用),这个时候Handler就进入了处理消息的阶段。注意Looper是运行在创建Handler所在的线程中的,这样一来Handler中的业务逻辑就被切换到创建Handler所在的线程中去执行了

ActivityThread分析(与上面相关)

以前一直都说Activity的人口是onCreate()方法。其实android上一个应用的入口,应该是ActivityThread,即我们常说的UI Thread,即主线程。框架源码为android.app.ActivityThread。和普通的java类一样,入口是一个main()方法。

public static final void main(String[] args) {
        SamplingProfilerIntegration.start();
       ……
        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
       ……
        Looper.loop();
       ……
        thread.detach();
        ……
        Slog.i(TAG, "Main thread of " + name + " is now exiting");
    }

以上转载自这篇文章

  • Looper.prepareMainLooper():
    主线程(ActivityThread)中之所以可以用Handler进行异步通信,就在于主线程中已经创建了Looper,而这个Looper就是在这里创建的。其他线程如果需要用Handler进行通信就要自己创建Looper。而主线程是特殊的,ActivityThread被创建的时候就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。代码如下:
ActivityThread thread = new ActivityThread();

关于ThreadLocal

Looper中有ThreadLocal的身影。
这里写图片描述
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,只能在该指定线程中获取到存储的数据,而其他线程是无法访问到的。关于ThreadLocal的使用场景,一般来说,当某些数据是以线程为作用域,并且不同线程有不同副本的时候,就可以考虑采用ThreadLocal。即,虽然在不同线程中访问的是同一个ThreadLocal对象,但是它们对ThreadLocal获取到的值却是不一样的,这就是ThreadLocal的奇妙之处。ThreadLocal之所以有这么奇妙的效果,是因为当不同线程访问同一个ThreadLocal对象的get()方法的时候,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找出对应的value值
比如对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域是线程,并且不同线程具有不同的Looper,那么系统就必须提供一个全局的HashMap供Handler查找指定线程的Looper,这样一来就得提供一个类似于LooperManager的类了,而系统没这么做,而ThreadLocal可以做到这一点(ThreadLocal<Looper>)。

ThreadLocal的API

ThreadLocal类的API很简单,只有四个方法:

void set(Object value)
    设置当前线程的线程局部变量的值。
public Object get()
    该方法返回当前线程所对应的线程局部变量。
public void remove()
    将当前线程局部变量的值删除
protected Object initialValue()
    ...

ThreadLocal的实现原理

ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

ThreadLocal总结

当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。
以上参考这篇文章

总结自《Android开发艺术探索》by任玉刚。

你可能感兴趣的:(android,消息机制)