Android 中的Handler原理

来源:互联网 发布:大数据量 数据库 编辑:程序博客网 时间:2024/05/22 13:40

概述

Android的正常运行离不开消息机制的使用,而消息机制对我们最直观的体现就是Handler。

Handler是Android消息机制的最上层的接口,在我们的开发过程中我们只要和Handler进行交互即可,在开发中我们运用的最多的场景是利用Handler将一个耗时操作的运行结果从一个线程中切换到主线程,以辅助更新UI线程。

Android的消息机制主要是由Handler、Looper以及MessageQueue来进行的,其中MessageQue中存储着待处理的消息,以一种队列的方式提供给Handler。而Looper则是负责无限循环的从MessageQueue队列中提出待处理的消息进行处理。

我们调用Handler的send方法的时候,MessageQueue的enqueueMessage方法会被调用,接着Looper发现新来的消息的时候就会将它从消息队列中提出来处理,最后Handler的handleMessage方法被调用。由于Looper运行在handler所在的线程中,故最后信息就被转移到了handler的线程中。

Handler模型

Part.1 ThreadLocal

ThreadLocal是一个数据存储类,从名字上我们就能明白他是属于线程自己的一个数据存储工具,它大概的工作原理是在每个线程内部创造一份变量的拷贝,这份拷贝只有当前线程才能访问调用。

笔者一开始觉得很奇怪,不明白为什么需要这个东西,后来想到这实际上这是解决线程安全性的一个方法。假设我们有一个变量要设置成静态,但是又只能被当前线程修改这个变量,利用ThreadLocal类就能很容易的办到。

public class Test {    public static void main(String[] args) {        MyThread thread = new MyThread();        new Thread(thread).start();        new Thread(thread).start();        new Thread(thread).start();    }}class MyThread implements Runnable{    private static ThreadLocal<Integer> mIntThreadLocal = new ThreadLocal<>();    private int myInt = 0;    @Override    public void run() {        mIntThreadLocal.set(0);        for (int i = 0; i < 3; i++) {            mIntThreadLocal.set(mIntThreadLocal.get() + 1);            myInt++;        }        System.out.println("ThreadLocal: " + mIntThreadLocal.get() + "  int: " + myInt);    }}

输出结果:

ThreadLocal: 3  int: 6ThreadLocal: 3  int: 9ThreadLocal: 3  int: 6

可以看到普通的int由于线程安全问题而被修改了,而被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);    }

可以看到,在ThreadLocal内部有个map类,这个类就是用来存储我们放进去的信息的,注意t.threadLocals这个变量,这个变量是Thread中的内部成员,当新建一个新的线程的时候,该变量就会重新新建一次,而map被赋值到了该变量上,这样子就可以确保每个线程都有一个自己的map,从而也就使得不同线程中的同一变量得到了隔离。

该Map是一个hashmap,利用ThreadLocal的哈希值进行运算得到值所在的位置

Part.2 MessageQueue

MesaageQueue是用单链表实现的,每个Node是Message类。该类主要包含两个操作:插入和读取。读取操作会伴随着删除操作,其中读取的方法是enqueueMessage,而遍历以及删除的方法是next方法

next()方法存在着一个for循环,它的作用是不断遍历消息队列,直到找到能处理的message节点将它从队列中摘下来,然后返回,如果不存在的话就会一直阻塞在该for循环里面。

Part.3 Looper

Looper在Android的消息机制中扮演着一个消息循环的角色,它会不断的从MessageQueue中查看是否存在新的信息,如果有就会提出来处理,否则将一直阻塞。

当我们使用Handler的时候,就必须要在Handler所在的线程新建一个Looper以配套,否则系统会提示我们报错。

public void run() {            Looper.prepare();            mHandler = new Handler() {                public void handleMessage(Message msg) {                    // process incoming messages here                }            };            Looper.loop();        }

上面为Looper的调用方法,我们来看看prepare的实现。

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);        mThread = Thread.currentThread();    }

在prepare方法中我们看到了ThreadLocal.set(),这说明Looper是一个线程独享的对象,每个线程持有着自己的Looper,而Looper的构造方法中又将MessageQueue消息队列构造了出来。

实际上除了Looper.prepare()还提供了Looper.prepareMain()方法,这个方法是特地为主线程(UI)线程提供的,在ActivityThread类中存在着一个Main方法,该方法为整个程序的入口,其中就调用了Looper.prepareMain()来初始化主线程的Looper,这就是为什么在主线程设置Handler的时候不用我们特地去prepare一个Looper的原因。

在prepare完毕之后,就可以调用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();        }    }

主要关注for循环里面,首先loop调用了MessageQueue的next方法,而next方法在上文说了,它也是一个死循环,除非有message否则会一直阻塞,当得到了message后,looper会调用msg.target.dispatchMessage()方法,向目标handler发送该信息,目标handler得到信息后便会调用Handler的handleMessage方法处理对应的事件,最后,loop又开始了新一次的循环…

这里有一点值得我们去思考,这个loop方法是一个死循环,里面的next也是一个死循环,当我们调用了这个方法的时候,整个线程不就进入了一个无休止的循环里面,我们的程序不就ANR么。尽管如此,我们的程序还是成功的运行了下去,这是因为loop方法是启动于整个main方法的最后,在loop之前ActivityThread已经完成了所有程序的准备操作,而程序的各种生命周期是通过AMS进行IPC通知ActivityThread中的handler——绑定在我们looper上的handler,该hanlder内部对所有的生命周期都有对应的处理,因而我们的APP仍旧可以正常的运行,这也是为什么我们总是将要调用的方法放在对应的生命周期中处理。

Part.4 Handler

整个消息机制开始的地方与终结的地方都在Handler里面,它是Message的发送者,也是Message的接受者。

4.1 Handler.sendMessage

    public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }    public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }    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);    }     

经过多步调用,最终来到了sendMessageAtTime方法,在方法的最后调用了enqueueMessage,该方法又调用了queue的enqueueMessage。由此得知,Handler的发送信息只是向消息队列中插入了一条信息。

当消息插入完毕后就会进入到上文提到的next环节,最终又交由到handler中,此时handler首先会检查自己的callback是否为null,若是交由callback的handleMessage进行处理。
由于
Looper运行在消息需要被处理的线程中,而我们在其他线程中使用到了目标线程的handler,因此此时消息就从其他线程回到了目标线程中。

Handler的构造方法规定了我们必须先构造一个Looper再去构造Handler,否则系统会报异常

这里写图片描述

感谢《Android 开发艺术探索》

0 0
原创粉丝点击