Handler源码分析

来源:互联网 发布:mac 查看音轨 编辑:程序博客网 时间:2024/06/10 01:39

分析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线程。

1 0
原创粉丝点击