文章独家授权公众号:码个蛋
更多分享:http://www.cherylgood.cn
- 谈到Android开发,就离不开线程操作,而面试中也会常常问到有关异步线程、多线程、Handler等问题,作为面试中中奖率如此之高的一个问题,我们今天不妨来瞅瞅这handler长啥样!
说了那么多废话,我们开始进入正题,首先我们看下handler的官方定义:
A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
Handler允许你通过使用一个与线程的MessageQueue相关联的Message和Runnable对象去发送和处理消息。 每个处理程序实例与单个线程和该线程的消息队列相关联。 当您创建一个新的处理程序时,它绑定到正在创建它的线程的线程/消息队列 - 从那时起,它将向消息队列传递消息和可运行文件,并在消息发出时执行它们 队列。
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
Handler有两个主要用途:(1)在可预见的时间内去调度消息和作为一些点的可运行程序(2)将不同于自己的线程执行的操作排入队列中。
Scheduling messages is accomplished with the post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler’s handleMessage(Message) method (requiring that you implement a subclass of Handler).
消息的调度是通过post(Runnable),postAtTime(Runnable,long),postDelayed(Runnable,long),sendEmptyMessage(int),sendMessage(Message),sendMessageAtTime(Message,long)和sendMessageDelayed(Message,long)来完成的 。 后台版本允许你将接收到的消息队列调用的Runnable对象排入队列; sendMessage版本允许你将包含将由处理程序的handleMessage(Message)方法处理的数据包(要求您实现Handler的子类)的Message对象排入队列。
When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.
当发布或发送到Handler时,你可以在消息队列准备就绪后立即处理该项目或者指定一个延迟时间去处理该消息队列,或者指定一个具体时间处理该消息。 后两者允许您实现超时,定时和其他基于时间的行为。
When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler’s message queue and processed when appropriate.
当为你的应用创建一个进程时,其主线程专用于运行一个消息队列,该消息队列负责管理顶级应用程序对象(activitys, broadcast receivers 等)及其创建的任何窗口。 你可以创建你自己的线程并通过Handler与主应用程序线程进行通信。 这可以通过从你的新线程中调用同样的post或sendMessage方法来实现。 给定的Runnable或Message将在Handler的消息队列中进行调度,并在适当时进行处理。
在查看Handler源码之前,我们先了解几个类:
Handler 、Looper、MessageQueue、Message、ThreadLocation
Handler我们就不在介绍类,上面的官方文档已给出了详细的介绍,我们来看下其余几个:
- 1、ThreadLocal:每个使用该变量的线程提供独立的变量副本,每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。ThreadLocal内部是通过map进行实现的;
- 2、Looper:可以理解为循环器,就是扶着管理一个消息循环队列(MessageQueue)的;
- 3、MessageQueue:消息队列,用来存放handler发布的消息
- 4、Message:消息体,封装了我们传输消息所需的数据结构。
ok,那么我们从哪里开始看起呢,好吧, 从创建一个Handler实例为入口,首先我们看handler的构造方法:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class
看了Handler的构造,接下来我们看下Looper.mLooper:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
-可以看到,Looper.myLooper内部是调用了sThreadlocal.get();这个sThreadLocal其实就是我们之前说的ThreadLocal类的实例,他负责存储当先线程的Looper实例;是不是真的呢?我们看下sThreadLocal在哪里赋值的,很好,我们找到了一个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.get() != null,说明Looper.prepare()只能被调用一次哦,不然就会抛出异常,这样做是为了保证一个线程只有一个looper存在,然后我们的可以看到里面通过new Looper(quitAllowed)获得当先线程的looper,我们继续看Looper的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
从上面的代码中我们可以知道,在new 一个handler的同时,我们就获得了一个handler实例、一个当前线程的looper、一个looper管理的messagequeue,好像拥有了这三个对象,我们就可以发送消息了哦。
大家都知道looper从创建之后,就会开始循环,在looper类的顶部,官方给出了一段代码:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
- 1、 调用 Looper.prepare(); 初始化所需的looper以及messageQueue
- 2、 实例化一个handler对象,我们可以在handleMessage获得message做一些操作,此时handleMessage方法是在当前的Looper中执行的,也就是说,如果当前的looper是UI Looper,那么你可以更新UI,如果当前looper不是UI Looper,那么你更新UI肯定会报错,你可能会说,我用handler时,好像都不用调用Looper.prepare();,我怎么知道我当前的looper是UI的还是不是呢,其实系统一般默认都帮我们获取了UI 的Looper,后面我们会讲解到;
Looper.prepare();我们前面已经分析过了,主要是实例化一个messageQueue,而且只能调用一次;那么我们重点就转移懂到 Looper.loop();看源码:
/**
{@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
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.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();
}
}
1、调用final Looper me = myLooper();获得一个looper,myLooper方法我们前面分析过,返回的是sThreadLocal中存储的Looper实例,当me==null抛出异常;所以,在looper执行loop跑起来之前,我们要记得调用prepare()哦。当获得当前的looper后,调用 final MessageQueue queue = me.mQueue; 获取looper管理的MessageQueue;然后我们可以看到一个很有意思的for语句: for (;;) {…} 这就是循环的开始了,此时我在想,我的天,这不是个无限死循话么?怎么可能呢?当然有退出的条件,不然不就傻逼了么!
looper主要做了如下工作:
- 1、将自己与当前线程关联在一起,通过ThreadLocal存储当前线程的looper,确保当前线程只有一个looper实例;
- 2、创建一个MessageQueue与当前looper绑定,通过prepare方法控制looper只能有一个messageQueue实例;
- 3、调用loop()方法,不断从MessageQueue中去取消息,通过调用msg.target.dispatchMessage(msg)处理;
分析完了looper、接下来当然是hanlder发送消息了,我们又回到了handler中,我们通过handler发消息,自然少不了我们得sendMessag方法,那么我们就从它入手吧:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
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);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
可以看到handleMessage是一个空的方法,我们看handleMessage在哪被调用的呢?
/**
- 1、在Looper.prepare()中会通过sThreadLocal保存一个looper实例,控制当前线程只能有一个looper实例;
- 2、创建looper实例时,会创建一个MessageQueue与looper关联;
- 3、因为looper只会存在一个实例,所以 当前线程也会只存在一个MessageQueue队列;
- 4、调用Looper.loop()让looper跑起来吧,然后looper就可以不停的从MessageQueue把消息拿出来,然后通过调用msg.target.dispatchMessage(msg)处理消息,也是让消息最终进入我们的Handler.handleMessage方法,被我们给处理了;所以我们在实例化handler时需要重写handleMessage方法;
- 5、实例化Handler时,handler中会获得当前线程的looper以及looper的messageQueue;
使用Handler注意事项:
- 1、创建massage对象时,推荐使用obtain()方法获取,因为Message内部会维护一个Message池用于Message的复用,这样就可以避免 重新new message而冲内心分配内存,减少new 对象产生的资源的消耗。
- 2、handler 的handleMessage方法内部如果有调用外部activity或者fragment的对象,一定要用弱饮用,handler最好定义成static的,这样可以避免内存泄漏;为什么呢?因为一但handler发送了消息。而handler内部有对外部变量的引用,此时handler已经进入了looper的messageQueue里面。此时activity或者fragment退出了可是区域,但是handler内部持有其引用且为强引用时,其就不会立即销毁,产生延迟销毁的情况。
更多分享:http://www.cherylgood.cn