源码分析之Handler

Handler是Android中的消息机制实现,可以实现UI线程和子线程的消息传递,这里就来深入了解Android的消息机制,来分析Handler的源代码

入手实例

在Android开发中,子线程与主线程通信是再寻常不过的事情了,那么为何需要子线程和主线程通信呢,相信只要是做过Android开发都知道这是为啥,为了博客的尽可能详尽,这里还是说说原因

举个简单例子,以前刚做Android开发的时候,啥也不懂,记得有一次,在主线程中做了耗时操作,具体记不得啥操作了,然后一运行,报了个无响应,也就是常说的ANR,然后就知道了主线程阻塞,耗时的操作放在子线程,主线程一旦阻塞超过5s就会报ANR,既然主线程不做耗时操作,那么子线程里面执行怎么和主线程互动呢,由此便引生出了Handler,当然AsyncTask也是基于同样的道理,AsyncTask的源代码在之前已经分析过了,这里就不再赘述

那么在开发中Handler是怎么使用的呢,这里结合一个例子作为讲解

public class MainActivity extends Activity {

    private static final String TAG = "cj5785";
    private TextView textView;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "handleMessage: " + Thread.currentThread().getName());
            String str = (String) msg.obj;
            textView.setText(str);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text_view);
    }

    public void buttonClick(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "run: " + Thread.currentThread().getName());
                Message message = Message.obtain();
                message.obj = "更新UI";
                handler.sendMessage(message);
            }
        }).start();
    }
}

这是一个简单的更新UI的例子,通过log可以清楚的看到,两个打印语句是处在不同的线程中的,然而却实现了主线程和子线程的消息传递

D/cj5785: run: Thread-13741
D/cj5785: handleMessage: main

如果直接在子线程中更新UI则会报出以下错误

Only the original thread that created a view hierarchy can touch its views.

那么这样的线程通信是怎么实现的呢,下面就来分析一下源代码

源代码分析

分析源代码,尤其是涉及到几个类的这种代码,找准切入点很关键,那么这个分析的切入点在哪呢,子线程发出消息,主线程接收,那么很明显是子线程的发出点,那么就应该先看子线程里面代码的处理

Message message = Message.obtain();
message.obj = "更新UI";
handler.sendMessage(message);

Message部分

首先是生成了一个Message对象,这里有两种方式,一种是直接new一个出来,一种是使用obtain(),那就去看看这个对象是怎么生成的

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

这里主要关注的是next参数,那么next是啥呢,有C语言基础的一看就知道是啥,没有也没关系,我们继续往下看

// sometimes we store linked lists of these things
/*package*/ Message next;

private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;

相信这句注释已经很明显了,这不就是链表么,Java中的链表使用对象引用实现,那么也就是说这里判断有没有Message有的话就实现复用,没有的话就new一个,而sPoolSync就是一个同步锁而已,到这里,并没有给出主线程和子线程消息传递的理由,那么继续往下看,message.obj = "更新UI";这里只是设置了变量,也没有参考价值,那么只剩handler.sendMessage(message);了,到此貌似Message的任务就完成了,那么来看看Message里面的一些值得注意的地方吧
照例先从类开始

public final class Message implements Parcelable {
	···
}

Message实现了Parcelable接口,Parcelable和Serializable都是实现序列化,用于消息的传输,Message是一个final类,简而言之无法继承
再看看Message的成员变量,常用那几个就不说了,这里谈一谈重要的一些

/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;

private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;

首先是Bundle,这也就是Message为何可以设置Bundle的原因所在了,这里出现了Handler,也就是说在这里涉及到了Handler,这里简单来说就是Message会持有Handler的引用,这里注意到似乎只用带参的obtain()以及setTarget()才会初始化Handler,MAX_POOL_SIZE说明了Message的大小,gCheckRecycle则是清除信息的标志位

Handler部分

那么到这里已经没啥线索了,可以猜测在Handler中对target进行了初始化,那看看sendMessage()做了啥吧

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}

这里意外发现了常用的延迟设置,再继续看

public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

所以从这就可以看出,当延迟小于零时会将其设为0,这里注意一下,SystemClock.uptimeMillis()是一个native方法,返回的是boot时间,再继续追寻

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组成的重要部分,那么MessageQueue是啥呢,熟悉数据结构的一眼就看出来了,这其实是个队列,稍后再看这个队列做了啥,队列不为空就要调enqueueMessage()了,那么继续

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

这里,对target赋值了,然后设置了异步状态标识,然后回到了MessageQueueenqueueMessage()方法,至此,有必要暂停Handler的分析,去看一看MessageQueue

MessageQueue部分

MessageQueue中找到handler调用的方法,由于方法比较长,不便于描述,将其描述用注释形式写在代码中

boolean enqueueMessage(Message msg, long when) {
	//target不能为空
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
	//msg不能正在被使用
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

	//同步代码块,保证线程安全
    synchronized (this) {
		//未退出
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle(); //清除Message
            return false;
        }
		
        msg.markInUse(); //标记正在使用
        msg.when = when; //得到延迟
        Message p = mMessages; //赋值,在第一次调用此方法前,mMessages为null
        boolean needWake;
        if (p == null || when == 0 || when < p.when) { //由于是短路或,不会执行到第三个条件
            // New head, wake up the event queue if blocked.
            msg.next = p; //将msg的next指向p
            mMessages = msg; //将msg赋值给mMessages
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev; //定义前节点
            for (;;) { //条件循环
                prev = p; //设置前节点为p
                p = p.next; //p的后节点赋值给p
                if (p == null || when < p.when) { //循环终止条件(到达尾节点或者when值小于后面的节点)
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; //msg的下一节点指向p
            prev.next = msg; //prev的下一节点指向msg
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

如果只是一个msg,那么就只形成一个节点,如果多个msg,则会按时间顺序形成一条链表,虽然形成了链表,那么我们最关心的主线程与子线程的通信去哪了,或者说我们的分析过程问题出在哪里,注意到前面的mQueue,这个东西是哪里来的

final MessageQueue mQueue;

那么是在那里初始化的,注意到在MainActivity中,调用前生成了Handler对象,使用的是无参的构造方法,那么来看看这个方法

public Handler() {
    this(null, false);
}

额,好像没啥用,继续

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

关于第一个if判断,这里说明一下:FIND_POTENTIAL_LEAKS标志设置为true以检测扩展的Handler类,如果不是静态的匿名,本地或成员类, 这类可能会产生泄漏。也就是我们常见构造时的警告说明!至于消除警告方法一般是设置成静态或弱引用
接下来发现有个mLooper,这是一个循环器,MessageQueue和Handler之间桥梁,那么这里就跳转到Looper吧

Looper

首先看看Looper.myLooper()干了啥

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

结合注释知道了,这里返回带有当前线程的Looper,下面是get啥呢,继续来看get()

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

/**
 * Variant of set() to establish initialValue. Used instead
 * of set() in case user has overridden the set() method.
 *
 * @return the initial value
 */
private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

结合注释可知,这里返回的是线程值,注意sThreadLocal的生成

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

嗯,也就是说在get前要调用prepare(),从而可以推导出在ActivityThread里面调用了这个方法,查看一下

public static void main(String[] args) {
    ···
    Looper.prepareMainLooper();
	···
    Looper.loop();
	···
}

看来没错,那么再看看Looper.prepareMainLooper()

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

重点关注第一个方法,那么这个方法一定就是关键了

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

这里还需要注意一点,这里的set,使用的是当前线程,也就保证了一个Thread只对应一个Looper
好像还差一点,再来,感觉到曙光了

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

到这里,终于找到了关键点,也就是说主线程和子线程通过MeaasgeQueue建立起了连接,这个Queue是同一个对象,那么具体是怎么执行的,前面执行了Looper.prepareMainLooper();,后面还有Looper.loop();,那么就来看看这里面做了什么吧

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
	得到当前线程Looper
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
	//得到当前线程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.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) { //条件循环
		//执行queue的next()方法,不断从queue中取出Message
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            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 {
			//之前设置了target,这里调用Handler的dispatchMessage()方法
            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();
    }
}

这里就是不断循环得到Message,然后分发消息,这里的重点在于dispatchMessage()做了啥

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

可以看到,这里由于前面没有设置callback,所以执行的是handleMessage(),那么再继续看看

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

这里并没有实现这个方法,注明了要实现这个方法才能收到消息,到此,在子线程发出的消息在主线程就能够得到了
那么到这里,消息机制的源代码分析也就完成了

思路整理

这里画个图就简单明了
源码分析之Handler-时序图
其工作原理就是通过Handler将消息发送给MessageQueue,而MessageQueue又是由Looper创建的,这里Handler和Looper使用的是同一个Queue,此时在Looper中通过loop()会不断从MessageQueue中读取消息,当msg全部都发送以后,回收消息。
Message: 消息对象
MessageQueen: 存储消息对象的队列
Looper: 读取MessageQueue里面的消息,交由Handler处理
Handler:发送消息和处理消息
handler一定要记得在页面退出时remove

至此,Handler消息机制就分析完毕了

posted @ 2019-04-06 23:56  cj5785  阅读(123)  评论(0编辑  收藏  举报