Handler简单介绍与使用

说起Handler,大多数Android开发者会想到:在子线程中更新UI,这确实是Handler的主要用途之一。分析Handler的运行机制,就从最简单的使用开始吧:

private Handler mHandler;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Log.i("thread",Thread.currentThread().getName());
            Bundle data = msg.getData();
            Log.i("message",data.getString("msg"));
            return true;
        }
    });

    new Thread(new Runnable() {
        @Override
        public void run() {
            Message message = Message.obtain();
            Bundle bundle = new Bundle();
            bundle.putString("msg","This is in the child thread message");
            message.setData(bundle);
            mHandler.sendMessage(message);
        }
    }).start();
}

上面的代码应该没什么好解释的,首先在onCreate方法中第三行创建了Handler对象,然后在子线程中创建出一个Message对象,并往Message对象中set了一个Bundle,最终调用Handler的sendMessage方法。Handler的handleMessage方法会被调用,这个handleMessage方法是回调在主线程的,我们也就成功实现了将子线程的消息发送给主线程处理,因此控制台输出如下:

I/thread: main
I/message: This is in the child thread message

对于使用而言,知道这么用就行了,但本文是对Handler运行机制的总结,因此会继续深入。

先来看Handler的构造器:

public Handler(Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
            + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

从这里我们至少能得到以下信息:Handler与Looper,MessageQueue有关。在构造方法中第一行就是获取Looper,跟进到myLooper方法中:return sThreadLocal.get();实际上是获取当前线程的Looper,最后将当前线程的Looper对象引用赋给了自己的成员变量mLooper,如果获取到的Looper是null,就会抛出异常。接着看mLooper.mQueue,这个mQueue实际上就是当前线程的Looper中的MessageQueue,在Handler的构造器中,将当前线程Looper中的MessageQueue对象引用赋给了自己的成员变量mQueue。

为何在子线程中创建Handler会报异常?

还记得刚才Handler的构造器中需要获取当前线程的Looper吗?如果获取不到,就会抛出异常,我们实验一下:

new Thread(new Runnable() {
    @Override
    public void run() {
        mHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });
    }
}).start();

果然GG:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

这是因为子线程中,默认是没有Looper的,所以当然会报错。那按照它提供的方法,在创建Handler前,调用一下Looper.prepare方法,就不会有问题了。那这个prepare方法究竟做了什么?实际上猜都能猜出来,肯定是给当前线程创建了一个Looper:

public static void prepare() {
    prepare(true);
}

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.set(new Looper(quitAllowed));这里就往当前线程中设置了Looper。子线程有了Looper,Looper中有MessageQueue,满足了创建Handler的条件。虽然这样写,创建Handler是没有问题了,但是此时Handler是无法处理Message的,还必须要调用下Looper.loop方法,让Looper开始循环取出MessageQueue中的消息交给Handler。那为何主线程不需要调用prepare方法就能直接创建Handler了呢?因为在ActivityThread中,已经帮我们创建好了Looper,所以无需我们手动再去创建。因此,如果我们想在子线程中创建Handler,并正常使用,应该这样写:

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        mHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });
        Looper.loop();
    }
}).start();

Handler的工作流程

其实说了这么多,依然不知道Handler、Looper、MessageQueue三者间到底是什么关系,是如何协作工作的。在这里,就先简单说一下它们三者间的关系,后面再具体分析每一个。当创建了Handler以后,Handler就获取到了当前线程的Looper和MessageQueue。MessageQueue顾名思义就是消息队列的意思,维护着当前线程未处理的消息列表。虽然叫队列,但是它内部的数据结构是单链表,这样做的优点就是增加和删除速度更快,因为对于MessageQueue来说,最主要的操作就是新增、读取和删除。当调用Handler的send系列方法时,会向MessageQueue中放入这个Message,放入了Message后,怎么才能交给Handler处理呢?答案是Looper。Looper的作用简单理解就是一个死循环,不断的从MessageQueue中取出Message交给Handler,Handler就会根据dispatchMessage的分发对消息进行处理。注意Looper所在的线程是创建Looper的线程,我们在主线程中创建Handler使用的是主线程的Looper,因此handleMessage的调用是在主线程,而如果我们在子线程通过Looper.prepare创建了Looper,这个Looper是属于子线程的,自然Looper中的MessageQueue也是在子线程中的。这个时候我们在子线程创建Handler,这个Handler获取到的Looper就是这个子线程的Looper,那么Looper循环MessageQueue时取出的Message在交给handleMessage处理时,也是处于子线程的。不要以为只要用了Handler,它的handleMessage就一定是回调在主线程,这个是和Handler所使用的Looper有关的。

MessageQueue

前面提到过MessageQueue是Android中的消息队列,虽然叫队列,但是内部实现并不是队列,而是单链表。当我们调用Handler的sendMessage方法的时候,其实最终都是向MessageQueue中插入了一个Message:

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

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

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

最终,调用的是enqueueMessage方法,这个方法里面,就会去调用当前线程的Looper中的MessageQueue的enqueueMessage方法,这个方法就是往MessageQueue中插入一个Message。光插入了消息,如何才能取出来交给Handler处理呢?这就是Looper的事情了。

Looper

Looper是与线程所关联的,一个线程只能有一个Looper,主线程初始化时,默认就会创建Looper,这也是为何能直接在主线程中创建使用Handler的原因。主线程创建Looper是在ActivityThread.java中的main方法中创建的,这也是Android应用程序的入口:

//省略部分代码
public static void main(String[] args) {
    //创建Looper
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
        LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    //开始消息循环
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

我们知道,要让Looper开始消息循环,需要调用Looper.loop方法,这也是为什么我们在子线程中创建Handler后,还要调用Looper.loop的原因,如果不调用,loop方法是不执行的,也就不会有循环读取消息队列中的消息交给Handler处理了。重点看下Looper的loop方法:

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    ...
    for (;;) {
        //取出Message
        Message msg = queue.next(); 
        if (msg == null) {
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
        msg.recycleUnchecked();
    }
}

首先获取了当前线程的Looper和MessageQueue,然后开起了死循环,不停的从MessageQueue中取出Message(通过MessageQueue的next方法)。如果取出的Message不为null,就会执行msg.target.dispatchMessage(msg); target是什么呢?我们再回过头看看Handler将Message放进MessageQueue的代码:

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

target就是你正在使用的那个Handler的对象。因此当Looper从MessageQueue中取出一条Message后,就会调用对应的Handler对象中的dispatchMessage方法。如果MessageQueue中已经没有任何消息了,就会一直阻塞在MessageQueue的next方法,直到有新消息到来。

Handler

既然Looper从MessageQueue中取出Message后,调用了Handler的dispatchMessage方法,我们就来看下dispatchMessage方法中,做了什么事情?

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

感觉有点疑问,msg.callback,这个callback是什么?查看Message类的成员变量发现:Runnable callback,什么时候用到呢?我们回想一下Handler中有post这个方法,用法很简单,例如我们要在子线程中更新UI,也可以这样做:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final Handler handler = new Handler();
    new Thread(new Runnable() {
        @Override
        public void run() {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    Log.i("thread", Thread.currentThread().getName());
                }
            });
        }
    }).start();
}

这个Runnable中的run方法就是回调在主线程的,其实原理都是一模一样的,我们看Handler的post方法

public final boolean post(Runnable r) {
    return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

原来,和我们调用send系列方法一样,最终都是将Message放进MessageQueue中!只不过send系列方法是你显示传入一个Message,而post是你传入一个Runnable,它内部调用getPostMessage方法给你隐式构建一个Message,并且把构建的Message对象的callbak字段赋值为传过来的Runnable。因此回到Handler的dispatchMessage方法,如果我们使用post方式,则msg.callback就不为null,就会执行handleCallback:

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

这样就调用了Runnable中的run方法。而如果我们使用的send方式,那么msg.callback就是null,就会执行下面的:else语句块:

if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
        return;
    }
}
handleMessage(msg);

这里的mCallback还有印象吗?回想下我们之前看的Handler的构造器中有这么一句:

public Handler(Callback callback) {
    this(callback, false);
}

public Handler(Callback callback, boolean async) {
    ...
    mCallback = callback;
    ...
}

这个mCallback就是我们之前在创建Handler时传的匿名内部类,因此我们匿名内部类中的handleMessage方法就会被回调。注意看最后一行,还有一个handleMessage,这个在什么情况下会用呢?比如你这样创建Handler时:

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
    }
};

这样创建Handler,mCallback就为null,因此回调的就是Handler的handleMessage了。
通过查看dispatchMessage源码,我们现在可以说:优先级最高的是handleCallback,其次是Callback中的handleMessage,这个回调中的返回值决定了是否还要继续回调Handler中的handleMessage,优先级最低的就是Handler中的handleMessage

关于Handler内存泄漏

这里再说一下Handler的内存泄漏问题,如果我们日常开发中这样来使用Handler,就可能导致内存泄漏:

public class MainActivity extends AppCompatActivity {

    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
            }
        };
    }
}

上面这种方式采用了匿名内部类的写法,在java中,内部类会隐式持有外部类的引用,在这里,Handler就持有了MainActivity的引用。通过前面的学习我们又知道,只要使用handler发送了消息,消息中都会持有Handler的引用,而MessageQueue中又持有Message的引用。最终形成了这样一个持有链:Activity被Handler持有,Handler被Message持有,Message被MessageQueue持有。一旦我们退出Activity时,MessageQueue中仍有未处理的Message,就会发生内存泄漏。常规的解决方法如下:

  1. 使用静态内部类,为了让Handler能调用Activity中的方法,因此让这个静态内部类持有Activity的弱引用
  2. 在Activity结束的时候,清空MessageQueue中未处理的Message
public class MainActivity extends AppCompatActivity {

    private MyHandler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler = new MyHandler(this);
    }

    private static class MyHandler extends Handler {

        private WeakReference<MainActivity> activity;

        MyHandler(MainActivity activity) {
            this.activity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity mainActivity = activity.get();
            if (mainActivity != null) {
                //处理message
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (handler != null) {
            handler.removeCallbacksAndMessages(null);
        }
    }
}

总结

要使用Handler,就必须要有Looper,有了Looper,就自然有了MessageQueue。主线程中,自带了Looper,因此不需要手动创建Looper就可以正常使用Handler。而子线程中,默认是没有Looper的,因此要想在子线程中使用Handler,就必须要手动为当前线程创建Looper。创建的方法是Looper.prepare,这样当前线程就拥有了Lopper和MessageQueue,再创建Handler,就能与这个线程的Looper和MessageQueue关联了。但是要想实现消息循环,还需要调用Looper.loop。
当你使用Handler的send系列方法或是post系列方法时,本质上都是将Message放入与当前Handler所关联的消息队列中,通过Looper循环取出Message再交给Handler处理。同样的,Activity中的runOnUiThread也就没什么好神奇的了,只要你搞懂了Handler的原理,一看代码就懂了:

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

好了,到这里就总结完成了。如果文中对知识的理解有错误,欢迎指正,共同学习进步。谢谢!



Android技术      Handler 消息机制 源码解析

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!