Android源码分析--Handler和Looper机制详解

来源:互联网 发布:linux lamp一键安装 编辑:程序博客网 时间:2024/04/27 23:00

转自:http://blog.csdn.net/smbroe/article/details/44239961

在Android系统中的应用程序,与Java的应用程序相同,都是靠消息驱动,简单的说就是:有一个消息队列,我们可以不断的向这个消息队列中添加消息,并从中取出消息,处理消息。Android中与此工作相关的主要是由Handler,Looper以及Message来完成。

  • Looper类:为一个线程运行着一个消息循环,内部有一个消息队列,每一个线程只允许最多存在一个Looper;
  • Handler类:允许你向一个线程的消息队列中发送消息,处理消息;
  • Message类:消息类。

使用样例

首先,我们通过一个简单的例子来学习如何使用,Looper和Handler,并通过这个例子来研究一下其工作原理; 
1.我们在LooperThread中为消息循环做准备,并创建一个Handler用于处理消息,注意Handler的创建要在调用Looper.prepare()之后;

public class LooperThread extends Thread {    public Handler mHandler;    @Override    public void run() {        // TODO Auto-generated method stub        Looper.prepare();        synchronized (this) {            mHandler = new Handler() {                @Override                public void handleMessage(Message msg) {                    // TODO Auto-generated method stub                    super.handleMessage(msg);                    Log.w("LooperThread", "handleMessage::Thread id---" + getId());                }            };        }        Looper.loop();        notifyAll();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

2.接下来我们在主线程中创建一个新的线程,并通过LooperThread中的Handler向LooperThread线程发送消息;

    final LooperThread mLooperThread = new LooperThread();    mLooperThread.start();    new Thread() {        @Override        public void run() {            // TODO Auto-generated method stub            while (mLooperThread.mHandler == null) {                try {                    wait();//防止在发送消息时Handler还没建立                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }            mLooperThread.mHandler.sendEmptyMessage(0);            Log.w(TAG, "Send Message::Thread id ---" + getId());        }    }.start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

该例子主要是新创建一个Thread,并在该线程中向LooperThread发送消息。为了防止在发送消息时Handler还没建立,进行了同步,在mHandler为null时,阻塞该线程以等待,当建立了Handler后唤醒该线程。可以在log中看到结果: 

Looper分析

由上面的例子我们可以看到如果我们想在本线程中进行消息管理,首先需要调用Looper.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));    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

可以看到在prepare方法中首先判断了该线程中是否已经创建了Looper,接着调用了ThreadLocal的set方法。ThreadLocal是Java中的线程局部变量类,它使得各线程能够保持各自独立的一个对象,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。对于ThreadLocal机制在这里不做过多赘述。

在这里我们为该线程set了一个Looper,接下来然我们看看Looper的构造函数:

    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }
  • 1
  • 2
  • 3
  • 4

可以看到,在构造方法中,我们创建了一个新的消息队列并设置了当前的线程。

接下来我们创建了一个Handler,关于Handler我们一会了分析,在最后我们调用了Looper的loop方法用来进行消息循环。让我们来看看这个方法:

    public static void loop() {        //myLooper()方法就是通过sThreadLocal.get()返回我们刚刚设置的Looper        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;        // 确保线程和本地进程是一致的,并且记录这个identity token        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {            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.dispatchMessage(msg);            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // 确保在分发的过程中该线程没有崩溃            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.recycle();        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

可以看到,loop方法主要是从消息队列中不断的取出消息,并将该消息分发出去。

总结一下,Looper的工作:

  • 封装了一个消息队列,
  • 利用prepare将Looper和调用prepare方法的线程联系起来
  • 利用loop函数分发消息

Handler分析

知道了Looper的工作,让我们来看看如何把消息发送出去。首先来看看它的构造方法,

    public Handler(Callback callback, boolean async) {        ......        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

无参数的构造方法调用了this(null, flase)构造方法,可以看到,在在Handler的成员变量中有一个Looper,首先获取了当前创建Handler的线程的Looper,另外可以看到在Handler中也保存了一个消息队列最终指向了Looper的消息队列。

当我们调用了sendMessage方法之后就向Looper发送了一条消息,让我们看看这个方法,消息是如何被传递的。sendMessage方法最终会调用到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);    }    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

可以看到,sendMessage方法最终调用了queue.enqueueMessage方法将消息加入到了Looper中的消息队列。

在上面我们看到了我们分发消息是调用了dispatchMessage方法:

    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

可以看到,dispatchMessage设置了一套消息处理的优先机制:

  1. 如果Message自带了Callback,则交给Message的Callback处理;
  2. 如果Handler了设置了Callback,则交给Handler的Callback处理;
  3. 如果两者都没有,则调用handleMessage方法处理。

Handler和Looper的同步问题

在上面的例子中我们可以发现,Handler和Looper是存在同步问题的,如果在LooperThread中Handler还没创建起来,在第二个线程中就发送了消息,这样就会引发空指针异常。在上面,我参考了Android中的HandlerThread,利用了wait/notifyAll的方法解决了这个问题,在实际使用时我们完全可以使用HandlerThread来完成。

    final HandlerThread mHandlerThread = new HandlerThread("LooperThread");    mHandlerThread.start();    final Handler mHandler = new Handler(mHandlerThread.getLooper()){        @Override        public void handleMessage(Message msg) {            // TODO Auto-generated method stub            super.handleMessage(msg);            Log.w("LooperThread", "handleMessage::Thread id---" + mHandlerThread.getId());        }    };    new Thread() {        @Override        public void run() {            // TODO Auto-generated method stub            mHandler.sendEmptyMessage(0);            Log.w(TAG, "Send Message::Thread id ---" + getId());        }    }.start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

我们可以来看一下HandlerThread的源码:

    @Override    public void run() {        mTid = Process.myTid();        Looper.prepare();        synchronized (this) {            mLooper = Looper.myLooper();            notifyAll();        }        Process.setThreadPriority(mPriority);        onLooperPrepared();        Looper.loop();        mTid = -1;    }    public Looper getLooper() {        if (!isAlive()) {            return null;        }        // If the thread has been started, wait until the looper has been created.        synchronized (this) {            while (isAlive() && mLooper == null) {                try {                    wait();                } catch (InterruptedException e) {                }            }        }        return mLooper;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

可以看到,这个的思想和我们刚刚例子中所用的是一致的。


阅读全文
0 0
原创粉丝点击