功能划分
Handler的使用必须与几个组件一起。
*Message: Handler接收和处理的消息对象,类似于一个业务类,封装了一些变量。
*MessageQueue:一个队列容器,采用先进先出的原则管理Message。程序创建Looper对象的时候会在构造函数中创建MessageQueue对象。
*Looper:负责管理MessageQueue和Message对象,读取到MessageQueue中的Message之后就会采用sendMessage的方式把消息发送给对应(发送该消息)的Handler来处理。
要在一个线程中使用Handler,则该线程中必须要有一个MessageQueue对象。而MessageQueue是由Looper来管理的。所以在一个线程中必须要有一个Looper对象,而且每个线程只能有一个Looper对象。
而根据线程的不同分类,Handler的使用就大致可以分为两种情况。
UI线程使用Handler
分析
在UI线程中无需创建Looper对象,因为在应用启动,UI线程启动的时候 系统已经自动创建了一个Looper对象。
这里就要提到一个类ActivityThread,这个类是程序启动的入口
下面是该类的部分源码
public final class ActivityThread {
......
public static final void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
Looper.loop();
......
thread.detach();
......
}
}
其中可以看到该类中的main函数,这个函数就是进程的入口函数。在main函数中调用了 Looper.prepareMainLooper();
就是在当前线程中创建Looper对象,所以在UI线程中我们是不需要 手动创建Looper的。
接下来再看Looper的源码。
public final class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you‘ve called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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));
}
这里面的public 修饰的prepare()方法会调用private的prepare方法,然后去检查当前线程是否有Looper对象,没有则创建,如果当前线程已经有了Looper对象,再次调用Looper.prepare()方法的话,会抛出异常的。
这里的ThreadLocal可以把它理解成一个容器吧。关于该类的详细信息,可以自己查看源码。
总结
在UI线程中要使用Handler,只需要创建Handler对象,重写Handler中的 handlerMessage方法,发送消息 即可。
子线程(非UI线程)使用Handler
分析
在子线程中使用Handler,我们必须手动为当前线程创建一个Looper对象,调用Looper.prepare()方法即可。
创建完成之后记得启动Looper的轮询,调用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) {
throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
}
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 (;;) {
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
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();
}
}
loop方法可以看出,它会无线循环检查MessageQueue。
总结
在子线程中使用Handler
1.需要手动为当前线程创建Looper对象
2.创建Handler对象,重写handlerMessage方法
3.调用Looper的loop方法启动Looper
这里只是简单的介绍一下 三者之间的关系,与工作原理。Handler是系统通信的重要组件,它的用法远不止这些,更深层次的用法。可以查看罗升阳前辈的博文