Handler和Looper详解

关于这个话题,其实很早之前就想撰写相关来的稳当来加加深映像,或者当做技术笔记,但是一直都不知道从哪里开始比较合适.先看看网上给出的相关的定.
Hanlder作用:
1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器
2)线程间通信。在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息。当你创建子线程时,你可以再你的子线程中拿到父线程中创建的Handler对象,就可以通过该对象向父线程的消息队列发送消息了。由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面。


 
一、    角色描述
1.Looper:(相当于隧道) 一个线程可以产生一个Looper 对象,由它来管理此线程里的Message Queue( 车队,消息隧道) 。
2.Handler: 你可以构造Handler 对象来与Looper 沟通,以便push 新消息到Message Queue 里;或者接收Looper( 从Message Queue 取出) 所送来的消息。
3.Message Queue( 消息队列): 用来存放线程放入的消息。
4.线程:UI thread 通常就是main thread ,而Android 启动程序时会替它建立一个Message Queue 。
每一个线程里可含有一个Looper 对象以及一个MessageQueue 数据结构。在你的应用程序里,可以定义Handler 的子类别来接收Looper 所送出的消息。

二、 执行过程


当我们在子线程创建一个Handler的时候,目的就是就可以通过该对象向父线程的
消息队列发送消息了,那么这个handler对象是怎么实现的呢? 其实,当创建好一个Handler的时候,在Handler的构造方法中会获得一个主线程的消息队列监听器 —— Looper.这里简单说一下Looper,,我们可以理解它为一个隧道或者是一直循环的监听器,我们通常说的Looper都是主线程Looper,当主线程启动的时候,它会通过Looper的prepareMainLooper()方法,获得当前线程的Looper实例,然后让Looper调用loop方法不断地循环来处理message方法.看下面



代码

public static final void main(String[] args) {
        SamplingProfilerIntegration.start();

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

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

        Looper.loop();



好吧,可能这里很难理解,所以我们一点儿一点儿地来分析.然后再说handler怎么
发挥作用的.

先看 Looper.prepareMainLooper(),这里发生了什么?

public static final void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        if (Process.supportsProcesses()) {
            myLooper().mQueue.mQuitAllowed = false;
        }
    }

看上面的代码可以知道,首先Looper的prepareMainLooper()方法中调用了 prepare()方法,这个方法作用是赋予当前线程一个新的Looper对象,看代码:

public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created 
per thread");
        }
        sThreadLocal.set(new Looper());
    }

好了,明白了这个方法,那下一个方法setMainLooper(myLooper())呢? 继续看源代码!我们先来看myLooper()方法.

public static final Looper myLooper() {
        return (Looper)sThreadLocal.get();
    }

这个方法很简单,它的作用就是获得当前线程的Looper对象,前面我们刚刚赋予了 一个Looper对象给当前线程,这里便获取了它,是不是有种遥相呼应的感觉.. (再次声明,这里的当前线程指的是主线程或是UI线程),我们再来看看 setMainLooper()方法:

private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }

也很简单,和之前的myLooper()方法就是把从当前线程获得Looper赋给一个mMainLooper的变量.

好了,总结一下,Looper.prepareMainLooper();的作用就是我们之前说的获得当前线程的Looper实例.下面再到 Looper.loop();看看

public static final void loop() {
        Looper me = myLooper();
        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();
       
        while (true) {
            Message msg = queue.next(); // might block
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit 
message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);
                if (me.mLogging!= null) me.mLogging.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("Looper", "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. Looper me = myLooper();刚才说过,就是获得当前线程(即主线程)的Looper.

2. MessageQueue queue = me.mQueue;这个2.MessageQueue在前面已经做了详细 的介绍,所以就不做解释了,但是突然出现在这里,有点莫名其妙......其实非也, 我们可以看到queue是来至于me.mQueue,就是Looper中的一个成员变量,这个 queue是什么时候产生的呢? 其实还记得刚才的prepare() 方法么.这个方法中 实例化了一个Looper——new Looper().queue就是来自于Looper的构造方法,请 看代码就会明白了.

private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

所以不是莫名其妙,是有根据的.

3. Message msg = queue.next(),在循环体中看到的这段代码当然就是不断地将queue中的message一个一个地取出来啦!

4. msg.target.dispatchMessage(msg);经过之前一系列的判断处理之后,来到了loop()方法中非常核心的地方,这里作用就是把这个从queue中获得的message分发给相应的地方来进行处理.好了。。。说了半天,终于能回到我们开始说的Handler了.

当时Handler怎么做才能和之前的步骤产生联系呢? Handler的使用我们必须先实例化并实现它的handleMessage方法.呵呵,其实这个 只是一个引子......当我们实例化Handler的时候其实就是和之前说过的Looper还有什么queue,产生联系的时候,继续看代码:


  public Handler(Callback callback) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || 
klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be 
static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

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

这下看Handler的构造方法是不是感觉很轻松?那就好.....之前那么长的铺垫没有白费,如果还不懂,再结合者前面的讲解看几遍.总结下,当实例化一个Handler的时候,这个handler会获得主线程中的Looper对象,和Looper中的成员变量mQueue,好了,所有的拼图碎片已经筹齐我们开始拼图了.我们平常实例化了Handler是不是就可以使用它的sendMessage方法呢?我们看看sendMessage到底干了什么,它其实调用了一个sendMessageAtTime()方法.

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

哦.... 相信大家已经能明白了.
首先msg.target = this;是不是很熟悉,我帮你们回忆一下.在Looper的looper的讲解中,第四个重点.

msg.target.dispatchMessage(msg)

是不是有种醍醐灌顶的感觉.这下所有的拼图组成一张图了,从这句核心代码就明白了,主线程Looper负责把所有的Message分发给相对应的handler来处理,然后把这个Message再次放入队列.而target就是指的Handler.最后指定的Handler的HandleMessage来处理这个消息,

这个就是整个Handler和Looper工作的流程....

 

猜你喜欢

转载自alton12345.iteye.com/blog/1738293