Android 性能优化(二)Handler运行机制原理,源码分析

目录java

前言面试

1、Looper轮询器安全

2、Handler处理者数据结构

3、MessageQueue消息队列ide

4、Message消息载体oop

5、小结post

面试题:ui


前言

Handler机制是Android中很是重要的技术点,几乎全部的面试都会涉及到这个问题,那么为何说它这么重要呢?由于Handler机制几乎贯穿了整个应用从启动到结束,包括Throwable抛出异常、View绘制、事件分发、Activity启动、Activity生命周期等都涉及到Handler机制。this

咱们知道,主线程中不建议耗时操做,子线程中不容许更新UI,因此通常咱们在开发中都会Handler机制来保证线程安全。Handle运行依赖于Looper、MessageQueue和Message ,总体流程以下:spa

在应用启动时会开启一个主线程,默认会建立一个Looper对象关联当前线程,且系统规定一个线程只容许建立一个Looper实例对象。另外,建立Looper对象同时实例化了一个MessageQueue对象,所以一个线程中对应惟一的Looper对象和MessageQueue对象。主线程中完成Looper对象建立后会调用Looper.loop()方法,进行消息循环,不断地获取MessageQueue中的Message对象,而后调用与Message绑定的Handler对象的dispatchMessage方法实现接口回调,从而更新handle所在的主线程的UI。

流程图以下:

 

1、Looper轮询器

Looper是Handler机制运行工做的核心,因此咱们先搞懂Looper是怎么回事很关键。咱们知道应用的启动经过main方法做为入口,这个main方法在主线程中,查看这个类的main方法能够看到有两个方法:

Looper.prepareMainLooper()、Looper.loop()。

public static final void main(String[] args) {
        ...
        //主线程中调用Looper.prepareMainLooper()方法建立Looper
        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(),开始轮询,取消息
        Looper.loop();
       ...
    }
}

首先,ActivityThread首先调用的Looper.prepareMainLooper()方法,咱们来看一下这个方法源码都作了些什么?

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

原来,该方法中首先调用本类中的prepare()方法建立Looper对象,而且把该对象setMainLooper()绑定到当前线程中。在这里既然是主线程调用的,那么该Looper对象天然是在主线程当中。继续看里面的prepare()这个方法:

在Looper类中
public static final void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //把Looper绑定到当前线程
    sThreadLocal.set(new Looper());
}

这个方法只作了一个事情,就是保证当前线程只能存在一个Looper对象,若是已经存在就会抛出异常(Android规定一个线程只容许关联一个Looper)。

继续看new Looper()这个构造方法,咱们能够看到:

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

Looper这个类的构造方法是private私有的,不容许外界直接new出来Looper对象,而最重要的是:在建立Looper的时候,同时建立了一个消息队列MessageQueue。因此请注意!MessageQueue是依赖Looper一块儿建立的。

而后,看ActivityThread调用Looper.loop()方法,开始轮询消息:

public static final void loop() {
        Looper me = myLooper();
        //经过Looper对象,获取MessageQueue
        MessageQueue queue = me.mQueue;
       ...
        //死循环获取消息
        while (true) {
            //调用MessageQueue的next()方法取消息,这个过程也是死循环
            Message msg = queue.next(); 

            if (msg != null) {
                ...
                //取到消息以后,交给发送该消息的Handler取处理消息
                msg.target.dispatchMessage(msg);
               ...
                //回收消息,在Message中维护的有一个消息池
                msg.recycle();
            }
        }
    }

从上述源码中简单的注解,咱们能够直观明白主线程的Looper和对应的MessageQueue之间最直接的关系了。可是咱们带着一个问题如何把Message消息从子线程交给Handler在主线程中处理的?继续分析msg.target.dispatchMessage()源码:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {//注意这个方法调用
                return;
            }
        }
        handleMessage(msg);
    }
}

咱们能够知道Looper.loop()方法里的死循环是不断地获取MessageQueue中的Message,而后调用与Message绑定的Handler对象的dispatchMessage方法。最后,咱们终于明白原来Handler的handleMessage就在这里调用的,其实就是一个接口回调啊。

 

2、Handler处理者

咱们知道Handler的工做主要就是发送和处理消息。其实Handler的构造方法有多种,但都会获取当前线程的Looper对象和MessageQueue对象。这个其实也没什么,看看就好:

//Handler的构造方法1
public Handler() {
    ...
    //获取当前线程的Looper对象
    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 = null;
}

//Handler的构造方法2
public Handler(Callback callback) {
   ...
    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的构造方法3
public Handler(Looper looper) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = null;
}

//Handler的构造方法4
public Handler(Looper looper, Callback callback) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
}

发送消息能够经过一系列send方法,也能够经过一系列post方法,不过post方法最终仍是经过send方法去实现。
用send方法,最终也会调用一个方法以下:

//在Handler中
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
    boolean sent = false;
    MessageQueue queue = mQueue;
    if (queue != null) {
        //调用msg.target的方法把Message和发送它的Handler绑定,因此Looper可以把正确的Message交给发送它的Handler处理
        msg.target = this;
        //调用MessageQueue的enqueueMessage方法把消息加入消息队列
        sent = queue.enqueueMessage(msg, uptimeMillis);
    }
    else {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
    }
    return sent;
}

能够看到,Handler发送消息的过程就是向消息队列中插入一条消息。前面已经讲到,MessageQueue调用next方法去轮询消息,那么当MessageQueue拿到消息以后,把消息传递给Looper,最终交给Handler去处理,即dispatchMessage方法会被调用。此时,Handler开始处理消息。值得一提的是,在消息队列中可能有不一样Handler发送的多个消息,经过在发送消息的时候把Message和发送它的Handler绑定,Looper就会把消息正确的交给发送它的Handler来处理。dispatchMessage方法以下:

//Handler中
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

Handler处理消息的过程:

第一步,查看Message的callback是否为null,不为null就经过handleCallback(msg)方法处理消息。这里的callback实际上就是一个Runnable对象。若是以post方式去发送消息,最终就会调用handleCallback(msg)方法去处理,这个方法内容为:

private final void handleCallback(Message message) {
     message.callback.run();
 }

第二步,检查mCallBack是否为null,若是不为null就调用mCallBack的handleMessage(msg)方法。这个mCallBack是CallBack接口的子类对象,前面已经说过Handler的构造方法中有两个能够用到这个CallBack。若是mCallBack为null,最终就会调用Handler的handleMessage(msg)方法,这个方法一般是在建立Handler时被使用者重写的方法。


须要说明:在主线程当前咱们建立Handler时并无本身建立Looper对象,这是由于主线程已经为咱们建立好了;若是要在子线程当前建立Handler,必定要在以前建立Looper对象,即调用Looper.prepare()方法。

 

3、MessageQueue消息队列

前面的内容已经讲了不少关于MessageQueue的东西,这里就总结下了。MessageQueue主要包含两个操做:插入消息(enqueueMessage方法)和读取消息(next方法)。读取的过程也伴随着删除操做。MessageQueue的的内部其实是经过一个单链表的数据结构来维护消息列表,这主要也是由于单链表在插入和删除上比较有优点。

final boolean enqueueMessage(Message msg, long when) {
    ...
        Message p = mMessages;
        if (p == null || when == 0 || when < p.when) {
            // 当前发送的message须要立刻被处理调,needWake唤醒状态置true
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; // new head, might need to wake up
         } else {
              // 当前发送的message被排队到其余message的后面,needWake唤醒状态置为false
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
    }
    // 是否唤醒主线程
    if (needWake) {
        nativeWake(mPtr);
    }
    return true;
}

enqueueMessage的主要操做其实就是单链表的插入操做,根据时间看当前发送的Message是否须要立刻处理。这个enqueueMessage方法是Handler发送消息的时候调用。

下面来看next方法:

final Message next() {
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;

    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(mPtr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            final Message msg = mMessages;
            if (msg != null) {
                final long when = msg.when;
                if (now >= when) {
                    mBlocked = false;
                    mMessages = msg.next;
                    msg.next = null;
                    if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
                    return msg;
                } else {
                    nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                }
            } else {
                nextPollTimeoutMillis = -1;
            }

        ...
}

注意"return msg",是next方法返回一个Message对象。next方法中也是采用阻塞的方式去获取消息队列中的消息,一旦有消息当即返回而且将它从单链表中移除。若是没有消息就一直阻塞。前面已经提到,这个取消息的next方法是在Looper的loop()方法中调用。

 

4、Message消息载体

Message只有一个无参构造方法,可是Message有多个obtain静态方法来返回Message对象。

采用哪一种方式建立Message对象均可以,可是建议采用obtain方法来建立。这是由于Message经过在内部构建一个链表来维护一个被回收的Message对象池。当用户调用obtain方法时会优先从池中获取,若是池中没有则建立新的Message对象。同时在使用完毕以后,进入池中以便于复用。这个在Looper.loop()方法能够看到一点端倪,在使用完毕时候调用了Message的recycle()方法。

下面是obtain方法建立Message对象的流程:

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

 

5、小结

  • 在应用启动时会开启一个主线程,默认建立一个Looper对象绑定当前线程,且不一样线程间的Looper不能共享。
  • 一个Thread线程对应一个Looper对象、对应一个MessageQueue对象。
  • 主线程中调用Looper的loop()方法,会开启消息循环,不断的从消息队列中拿到消息。
  • Looper拿到消息以后调用Handler的dispatchMessage方法来处理消息。
  • 一个Thread线程对应多个Handler。
  • Handler经过send、post方法,把消息加入消息队列MessageQueue中。
  • 子线程中建立Handler需先建立Looper对象,Thread与Handler共享一个Looper与MessageQueue。

 

面试题:

Looper.loop()为何不会阻塞主线程?

IdHandler(闲时机制)?

postDelay()的具体实现?

post()与sendMessage()区别?

使用Handler时怎么解决内存泄漏的问题的?