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工作的流程....

转载于:https://my.oschina.net/u/698243/blog/138620

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值