Android从源码分析一:Looper,Handler消息机制

本文详细解析了Android中Handler机制的核心概念、使用方法及其内部工作原理,包括Looper、MessageQueue和消息的发送与接收过程,旨在帮助开发者理解和掌握Android应用中消息传递的高效实现。

首先,大家都知道Android中只能在主线程中做UI处理,当涉及到子线程中更新UI的处理时,就需要一个消息机制把子线程的消息发送到UI线程,这个时候Handler就腾空出世、闪亮登场,来做这个消息的搬运工!
我们来看Handler一般的用法:

Handler myHandler = new Handler() {  
    // 子类必须重写此方法,接受数据 
          public void handleMessage(Message msg) {   
               switch (msg.what) {   
                   //做接受到消息后处理(比如说UI更新)。
               }   
               super.handleMessage(msg);   
          }   
     };  
     //子线程里使用Handler发送消息
     class MyThread implements Runnable { 
        public void run() { 
            try { 
                Thread。sleep(10000); 
            } catch (InterruptedException e) { 
                // TODO Auto-generated catch block 
                e。printStackTrace(); 
            }            
            Message msg = new Message(); 
            Bundle b = new Bundle();// 存放数据 
            b.putString("color""我的"); 
            msg.setData(b); 
            myHandler.sendMessage(msg); //向Handler发送消息,更新UI 
        } 
    } 
当然发送消息的方式有send系列(sendEmptyMessage(int), sendMessage(Message),sendMessageAtTime(Message,long),sendMessageDelayed(Message, long)>和post( post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long))系列。

下面我们来一步一步的通过源码来解读整个消息的发送和接受的过程。
1、Looper
首先我们还要提一个东西:消息怎么产生的?这里我们要谈一下Looper.有人形象的把它称之为消息泵,不断地从MessageQueue中抽取Message执行。一个MessageQueue需要一个Looper。

对于Looper主要是prepare()和loop()两个方法。
首先看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));
    }

sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看出Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例~否则会抛出异常.

下面是Looper的构造方法:

 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

在构造方法中,创建了一个MessageQueue(消息队列)。
然后我们看loop()方法:

 /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
    //方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //拿到Looper里面的mQueue。
        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) {
                // 取出一条消息,如果没有消息则阻塞。
                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方法去处理,据说target就是Handler。
            msg.target.dispatchMessage(msg);

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

总而言之:Looper类用来为一个线程开启一个消息循环:
先调用prepare(),与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
然后调用loop(),不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。
好了,现在消息有了,队列也排好了。就缺一个发送消息的对象了。Handler来也!
2、Handler
我们先来看一下它的构造方法:

public Handler(Callback callback, boolean async) {
        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对象,通过它获取保存了的MessageQueue(消息队列)。
        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;
    }

通过 Looper.myLooper();获得mLooper对象,然后又通过mLooper.mQueue获得了mLooper保存的消息队列,这样就保证了Handler与Looper实例中的MessageQueue关联上了。
然后看我们的sendMessage()是怎样发送消息的?
我们从源码中看到,

        sendMessage-->sendEmptyMessageDelayed
        -->sendMessageDelayed-->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);
    }

几次调用之后,最终通过sendMessageAtTime()方法内部构建了一个新的MessageQueue ,并且将其与传过来的Message一起调用了enqueueMessage()方法,我们再来看:

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
 //把Handler对象作为Looper里面的msg.target;并将msg通过MessageQueue类放到消息队列里面。最终把这个消息发送出去。
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

如果大家有心,应该会记得Looper里面的loop()方法中的无限循环里面的

 msg.target.dispatchMessage(msg);//最终Looper交给msg.target去处理,

而今,handler里面将msg.targer赋值为this;也就是把当前的handler作为msg的target属性。最终会调用MessageQueue 的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去!
现在已经很清楚了,就是Looper会调用perpare和loop方法,在当前执行的线程中保存一个Looper实例,并且这个实例会保存一个MessageQueue对象,然后当前线程进入无限循环当中,不断从MessageQueue中读取msg.target也就是Handler发送过来的消息。接下来我们就看Handler是如何接收的:刚刚说到了

      msg.target.dispatchMessage(msg);//处理

       /**接下来我们看这个函数是如何处理的:
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

无论如何最终都调用了handleMessage();
下面我们来看一下handleMesssage()这个方法:

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

空的,上面的注释是说,子类必须重写这个方法来接收消息。所以知道我们为啥写Handler的时候都要重写handleMessage()方法了吧!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值