Handler源码分析

分析handler的源码,我们需要提前准备好这几个java类:

  • Handler.java ;
  • MessageQueue.java
  • Looper.java
  • ActivityThread.java
  • ThreadLocal.java

如果没有关联源码,我们可以在adk目录下的sources中选择我们需要查看的API版本,然后在搜索框中搜索上面的类名,就可以找到这几个类的源码,然后将源码拖拽到eclipse中,AndroidStudio中是自动关联源码的,就不用再查找了,


Handler : 1)按计划发送消息或执行某个Runnanble(使用POST方法),类似定时器;
2)从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新UI线程);
例如:handler在ui线程中创建,用来将子线程中的消息发送到ui线程的消息队列中,并且在ui线程中处理消息

Looper :我们都知道Android中的handler机制是消息驱动;这个机制是通过looper.prepare();方法创建消息驱动;并通过looper.loop()开启驱动事件;

MessageQueue :这是一个消息队列,用来存贮handler发送过来的消息,遵循先进先出的原则;但是有一个例外,如果我们调用handler中的sendMessageAtFrontOfQueue()方法,那么这条消息就会被插入到消息队列的最前面,优先处理该消息;


下面我们来研究一下创建一个handler的完整过程:
我们在子线程中创建一个handler:


        new Thread(new Runnable() {
            public Handler handler;

            @Override
            public void run() {
                //1、准备Looper对象
                Looper.prepare();
                //2、在子线程中创建Handler
                handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);

                    }
                };
                //3、调用Looper的loop()方法,开始轮询消息队列;
                Looper.loop();
            }
        }).start();

上面的代码是在主线程中开启了一个子线程;并在子线程中创建handler;主要分为三步

1.Looper.Prepare();
2.new Handler
3.Looper.loop();

下面我们从源码方面进行分析:


.Looper.Prepare()

//looper.java中:
   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));
    }
//ThreadLocal.java中:
  public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

looper.prepare()最终会执行上面这两段代码:
这个方法有下面几个个作用:
1.是生成Looper对象,
2.是把Looper对象和当前线程对象形成键值对(线程为键),存放在 ThreadLocal当中,所以一个线程只能有一个looper;
3.是在looper的构造方法中初始化了一个MessageQueue的实例,因为一个线程中只有一个looper所以也只能有一个消息队列;



new Handler
Android中handler的作用有下两个方面;
1)按计划发送消息或执行某个Runnanble(使用POST方法),类似定时器;
2)从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新UI线程);

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

在默认构造方法里面,handler会取出当前线程中的looper和
messageQueue;从上面的代码中我们可以看到如果looper为空则会抛出异常”Can’t create handler inside thread that has not called Looper.prepare()”;这里也许会有人问,当我们在主线程中初始化handler时并没有调用looper.prepare()方法为什么没有报错,我们需要了解主线程启动的过程:Zygote进程先启动activityThread,在activitythread类的main方法中会调用 Looper.prepareMainLooper();来初始化looper;这个方法跟looper.prepare(),然后才会执行到我们的oncreate方法;就是我们程序中开始写代码的地方;

  */
    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);
    }

使用handler方法发送消息,我们发现最终都会调用上面这个方法;返回的是enqueueMessage,查看messageQueue我们可以看出来,enqueueMessage方法是将消息压入消息队列中去;也就是说handler发消息其实是将消息放到与handler关联的消息队列中去;

 public final Message obtainMessage(int what, Object obj)
    {
        return Message.obtain(this, what, obj);
    }

handler机制中引入了消息池的该您,调用上面的方法会从一个全局消息池里面获取一个新的Message。在Message池中检索是否存在与handler实例对应的message比创建一个新的Message更高效。如果你不想创建新Message,就是用Message.obtain方法代替。

上面的是handler发送消息,下面我们来看看handler处理消息;

 public interface Callback {
        public boolean handleMessage(Message msg);
    }

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

如上面源码所示,当你实例化一个Handler的时候可以使用Callback接口来避免写自定义的Handler子类。这里的机制类似与Thread与runable接口的关系。
在Handler里面,子类要处理消息的话必须重写handleMessage()这个方法,因为在handler里面它是个空方法,



Looper.loop()
接下来我们看看这个方法的源码


    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
        //looper为空则抛出异常;
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 把当前looper的queue赋值给局部变量queue
        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.
       //确保当前线程属于当前进程,并且记录真实的token
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

//从这里开始进入重点:无限for循环
        for (;;) {
        //与MessageQueue中的enqueueMessage(入队)相对应queue.next()是从消息队列中取出消息,即(出队);
            Message msg = queue.next(); // might block 有可能阻塞 ;
            if (msg == null) {
                // No message indicates that the message queue is quitting. 如果msg为空则表明消息队列停止工作;
                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.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
   //msg.target实际上就是这个消息队列所绑定的handler,这里就是调用handler中的 dispatchMessage(msg)看这个方法的源码,实际上就是调用了 handleMessage(msg)方法来处理消息  
      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();
        }
    }

注释已经表达很清楚了,这里就不多做介绍了

handler原理就简单分析到这里;接下来说一下面试中经常问道的几个问题:
1.Android是如何检查非ui线程更新ui的:
在activityThread中 会调用ViewRootImpl impl = decor.getViewRootImpl();来实例化ViewRootImpl;ViewRootImpl中有一个方法invalidateChildInParent就是来判断当前线程是否是主线程;但是,ViewRootImpl这个类是在activity的onResume()方法中创建的。就算在子线程中更新UI,只要在ViewRootImpl创建之前更新UI(比如,程序在执行onCreate方法时,我就去执行setText方法区更新UI),就可以逃避掉checkThread()的检查

ViewParent是一个接口类,其实现类是ViewRootImpl,通过查看invalidateChild()方法里面的代码就可以看到会他调用checkThread()方法。checkThread()

2.当messageQueue中没有消息是,会阻塞,为什么ui线程不会崩溃,而我们在ui线程中做耗时操作操作就会倒置ANR:

由于应用的UI线程需要保持一直运行,要有一个循环保持这个线程不会死掉。但UI线程又必须保持阻塞,以减少CPU的消耗。这个循环就是通过Looper实现的。而Looper就是处理MessageQueue中的消息,即使消息为空,也不会造成ANR,因为这个就是UI线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值