在Handler基础篇中讲述了Handler原理和使用,下面是从Handler源码进一步解析Handler。
一、源码解析
1. Handler的构造函数
1 /** 2 * Use the {@link Looper} for the current thread with the specified callback interface 3 * and set whether the handler should be asynchronous. 4 * 5 * Handlers are synchronous by default unless this constructor is used to make 6 * one that is strictly asynchronous. 7 * 8 * Asynchronous messages represent interrupts or events that do not require global ordering 9 * with respect to synchronous messages. Asynchronous messages are not subject to 10 * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. 11 * 12 * @param callback The callback interface in which to handle messages, or null. 13 * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for 14 * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. 15 * 16 * @hide 17 */ 18 public Handler(Callback callback, boolean async) { 19 if (FIND_POTENTIAL_LEAKS) { 20 final Class<? extends Handler> klass = getClass(); 21 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 22 (klass.getModifiers() & Modifier.STATIC) == 0) { 23 Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 24 klass.getCanonicalName()); 25 } 26 } 27 28 mLooper = Looper.myLooper(); 29 if (mLooper == null) { 30 throw new RuntimeException( 31 "Can't create handler inside thread that has not called Looper.prepare()"); 32 } 33 mQueue = mLooper.mQueue; 34 mCallback = callback; 35 mAsynchronous = async; 36 }
在构造函数中有mLooper和mQueue获取。其中Looper和MessageQueue是如何创建,下面请看Looper源码
1 // sThreadLocal.get() will return null unless you've called prepare(). 2 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 3 private static Looper sMainLooper; // guarded by Looper.class 4 5 final MessageQueue mQueue; 6 7 private static void prepare(boolean quitAllowed) { 8 if (sThreadLocal.get() != null) { 9 throw new RuntimeException("Only one Looper may be created per thread"); 10 } 11 sThreadLocal.set(new Looper(quitAllowed)); 12 } 13 14 /** 15 * Initialize the current thread as a looper, marking it as an 16 * application's main looper. The main looper for your application 17 * is created by the Android environment, so you should never need 18 * to call this function yourself. See also: {@link #prepare()} 19 */ 20 public static void prepareMainLooper() { 21 prepare(false); 22 synchronized (Looper.class) { 23 if (sMainLooper != null) { 24 throw new IllegalStateException("The main Looper has already been prepared."); 25 } 26 sMainLooper = myLooper(); 27 } 28 } 29 30 /** 31 * Returns the application's main looper, which lives in the main thread of the application. 32 */ 33 public static Looper getMainLooper() { 34 synchronized (Looper.class) { 35 return sMainLooper; 36 } 37 } 38 39 /** 40 * Run the message queue in this thread. Be sure to call 41 * {@link #quit()} to end the loop. 42 */ 43 public static void loop() { 44 final Looper me = myLooper(); 45 if (me == null) { 46 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 47 } 48 final MessageQueue queue = me.mQueue; 49 50 // Make sure the identity of this thread is that of the local process, 51 // and keep track of what that identity token actually is. 52 Binder.clearCallingIdentity(); 53 final long ident = Binder.clearCallingIdentity(); 54 55 for (;;) { 56 Message msg = queue.next(); // might block 57 if (msg == null) { 58 // No message indicates that the message queue is quitting. 59 return; 60 } 61 62 // This must be in a local variable, in case a UI event sets the logger 63 final Printer logging = me.mLogging; 64 if (logging != null) { 65 logging.println(">>>>> Dispatching to " + msg.target + " " + 66 msg.callback + ": " + msg.what); 67 } 68 69 final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; 70 71 final long traceTag = me.mTraceTag; 72 if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { 73 Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); 74 } 75 final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); 76 final long end; 77 try { 78 msg.target.dispatchMessage(msg); 79 end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); 80 } finally { 81 if (traceTag != 0) { 82 Trace.traceEnd(traceTag); 83 } 84 } 85 if (slowDispatchThresholdMs > 0) { 86 final long time = end - start; 87 if (time > slowDispatchThresholdMs) { 88 Slog.w(TAG, "Dispatch took " + time + "ms on " 89 + Thread.currentThread().getName() + ", h=" + 90 msg.target + " cb=" + msg.callback + " msg=" + msg.what); 91 } 92 } 93 94 if (logging != null) { 95 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 96 } 97 98 // Make sure that during the course of dispatching the 99 // identity of the thread wasn't corrupted. 100 final long newIdent = Binder.clearCallingIdentity(); 101 if (ident != newIdent) { 102 Log.wtf(TAG, "Thread identity changed from 0x" 103 + Long.toHexString(ident) + " to 0x" 104 + Long.toHexString(newIdent) + " while dispatching to " 105 + msg.target.getClass().getName() + " " 106 + msg.callback + " what=" + msg.what); 107 } 108 109 msg.recycleUnchecked(); 110 } 111 } 112 113 /** 114 * Return the Looper object associated with the current thread. Returns 115 * null if the calling thread is not associated with a Looper. 116 */ 117 public static @Nullable Looper myLooper() { 118 return sThreadLocal.get(); 119 }
在Handler的构造函数中,通过Looper.myLooper()方法获取Looper对象,再通过Looper.mQueue获取到消息队列。
在Looper中的myLooper()方法中通过ThreadLocal.get()返回的Looper对象,在什么时候将Looper对象set到ThreadLocal中。是Looper中的prepare方法实现中创建的Looper对象和将Looper对象set到ThreadLocal中。
1 private static void prepare(boolean quitAllowed) { 2 if (sThreadLocal.get() != null) { 3 throw new RuntimeException("Only one Looper may be created per thread"); 4 } 5 sThreadLocal.set(new Looper(quitAllowed)); 6 }
那么,MessageQueue是什么时候创建的,MessageQueue是在Looper的构造函数实现中创建的
1 private Looper(boolean quitAllowed) { 2 mQueue = new MessageQueue(quitAllowed); 3 mThread = Thread.currentThread(); 4 }
当使用Handler在未来某个时刻通过UI线程更新UI,那么这个Handler创建就要在UI线程的Activity中实现,而不能在内部类中实现,
1 Handler handler = new Handler(new Handler.Callback{});
在Activity中创建Handler时就已经将Handler和UI线程的Looper及MessageQueue关联。这样,在使用handler.sendMessage()或者handler.post()时,将消息添加到消息队列中。
那么,在MessageQueue中的Message,Looper是如何获取将交给Handler执行的。那么,就要看Looper中的loop()方法
1 /** 2 * Run the message queue in this thread. Be sure to call 3 * {@link #quit()} to end the loop. 4 */ 5 public static void loop() { 6 final Looper me = myLooper(); 7 if (me == null) { 8 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 9 } 10 final MessageQueue queue = me.mQueue; 11 12 // Make sure the identity of this thread is that of the local process, 13 // and keep track of what that identity token actually is. 14 Binder.clearCallingIdentity(); 15 final long ident = Binder.clearCallingIdentity(); 16 17 for (;;) { 18 Message msg = queue.next(); // might block 19 if (msg == null) { 20 // No message indicates that the message queue is quitting. 21 return; 22 } 23 24 // This must be in a local variable, in case a UI event sets the logger 25 final Printer logging = me.mLogging; 26 if (logging != null) { 27 logging.println(">>>>> Dispatching to " + msg.target + " " + 28 msg.callback + ": " + msg.what); 29 } 30 31 final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; 32 33 final long traceTag = me.mTraceTag; 34 if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { 35 Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); 36 } 37 final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); 38 final long end; 39 try { 40 msg.target.dispatchMessage(msg); 41 end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); 42 } finally { 43 if (traceTag != 0) { 44 Trace.traceEnd(traceTag); 45 } 46 } 47 if (slowDispatchThresholdMs > 0) { 48 final long time = end - start; 49 if (time > slowDispatchThresholdMs) { 50 Slog.w(TAG, "Dispatch took " + time + "ms on " 51 + Thread.currentThread().getName() + ", h=" + 52 msg.target + " cb=" + msg.callback + " msg=" + msg.what); 53 } 54 } 55 56 if (logging != null) { 57 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 58 } 59 60 // Make sure that during the course of dispatching the 61 // identity of the thread wasn't corrupted. 62 final long newIdent = Binder.clearCallingIdentity(); 63 if (ident != newIdent) { 64 Log.wtf(TAG, "Thread identity changed from 0x" 65 + Long.toHexString(ident) + " to 0x" 66 + Long.toHexString(newIdent) + " while dispatching to " 67 + msg.target.getClass().getName() + " " 68 + msg.callback + " what=" + msg.what); 69 } 70 71 msg.recycleUnchecked(); 72 } 73 }
在loop()方法中有一个无限循环,不停的从MessageQueue中获取Message(Message msg = queue.next()),再通过msg.target.dispatchMessage(msg)将Message分发给对应的Handler执行。
那么,msg.target的这个target是什么,去Message类中去看一下这个target是什么。
1 public final class Message implements Parcelable { 2 /*package*/ Handler target; 3 ... 4 5 }
从上面源码中可以看出target就是Handler。那么,Message是如何持有Handler引用?
1 /** 2 * Pushes a message onto the end of the message queue after all pending messages 3 * before the current time. It will be received in {@link #handleMessage}, 4 * in the thread attached to this handler. 5 * 6 * @return Returns true if the message was successfully placed in to the 7 * message queue. Returns false on failure, usually because the 8 * looper processing the message queue is exiting. 9 */ 10 public final boolean sendMessage(Message msg) 11 { 12 return sendMessageDelayed(msg, 0); 13 } 14 15 /** 16 * Sends a Message containing only the what value. 17 * 18 * @return Returns true if the message was successfully placed in to the 19 * message queue. Returns false on failure, usually because the 20 * looper processing the message queue is exiting. 21 */ 22 public final boolean sendEmptyMessage(int what) 23 { 24 return sendEmptyMessageDelayed(what, 0); 25 } 26 27 /** 28 * Sends a Message containing only the what value, to be delivered 29 * after the specified amount of time elapses. 30 * @see #sendMessageDelayed(android.os.Message, long) 31 * 32 * @return Returns true if the message was successfully placed in to the 33 * message queue. Returns false on failure, usually because the 34 * looper processing the message queue is exiting. 35 */ 36 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { 37 Message msg = Message.obtain(); 38 msg.what = what; 39 return sendMessageDelayed(msg, delayMillis); 40 } 41 42 /** 43 * Sends a Message containing only the what value, to be delivered 44 * at a specific time. 45 * @see #sendMessageAtTime(android.os.Message, long) 46 * 47 * @return Returns true if the message was successfully placed in to the 48 * message queue. Returns false on failure, usually because the 49 * looper processing the message queue is exiting. 50 */ 51 52 public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { 53 Message msg = Message.obtain(); 54 msg.what = what; 55 return sendMessageAtTime(msg, uptimeMillis); 56 } 57 58 /** 59 * Enqueue a message into the message queue after all pending messages 60 * before (current time + delayMillis). You will receive it in 61 * {@link #handleMessage}, in the thread attached to this handler. 62 * 63 * @return Returns true if the message was successfully placed in to the 64 * message queue. Returns false on failure, usually because the 65 * looper processing the message queue is exiting. Note that a 66 * result of true does not mean the message will be processed -- if 67 * the looper is quit before the delivery time of the message 68 * occurs then the message will be dropped. 69 */ 70 public final boolean sendMessageDelayed(Message msg, long delayMillis) 71 { 72 if (delayMillis < 0) { 73 delayMillis = 0; 74 } 75 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); 76 } 77 78 /** 79 * Enqueue a message into the message queue after all pending messages 80 * before the absolute time (in milliseconds) <var>uptimeMillis</var>. 81 * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> 82 * Time spent in deep sleep will add an additional delay to execution. 83 * You will receive it in {@link #handleMessage}, in the thread attached 84 * to this handler. 85 * 86 * @param uptimeMillis The absolute time at which the message should be 87 * delivered, using the 88 * {@link android.os.SystemClock#uptimeMillis} time-base. 89 * 90 * @return Returns true if the message was successfully placed in to the 91 * message queue. Returns false on failure, usually because the 92 * looper processing the message queue is exiting. Note that a 93 * result of true does not mean the message will be processed -- if 94 * the looper is quit before the delivery time of the message 95 * occurs then the message will be dropped. 96 */ 97 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 98 MessageQueue queue = mQueue; 99 if (queue == null) { 100 RuntimeException e = new RuntimeException( 101 this + " sendMessageAtTime() called with no mQueue"); 102 Log.w("Looper", e.getMessage(), e); 103 return false; 104 } 105 return enqueueMessage(queue, msg, uptimeMillis); 106 } 107 108 /** 109 * Enqueue a message at the front of the message queue, to be processed on 110 * the next iteration of the message loop. You will receive it in 111 * {@link #handleMessage}, in the thread attached to this handler. 112 * <b>This method is only for use in very special circumstances -- it 113 * can easily starve the message queue, cause ordering problems, or have 114 * other unexpected side-effects.</b> 115 * 116 * @return Returns true if the message was successfully placed in to the 117 * message queue. Returns false on failure, usually because the 118 * looper processing the message queue is exiting. 119 */ 120 public final boolean sendMessageAtFrontOfQueue(Message msg) { 121 MessageQueue queue = mQueue; 122 if (queue == null) { 123 RuntimeException e = new RuntimeException( 124 this + " sendMessageAtTime() called with no mQueue"); 125 Log.w("Looper", e.getMessage(), e); 126 return false; 127 } 128 return enqueueMessage(queue, msg, 0); 129 } 130 131 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 132 msg.target = this; 133 if (mAsynchronous) { 134 msg.setAsynchronous(true); 135 } 136 return queue.enqueueMessage(msg, uptimeMillis); 137 }
从上面源码中可以看出,在调用mHandler.sendMessage(msg)方法时,在将Message添加MessageQueue之前,将Handler的this对象传递给了Message.target对象。从而在Looper中loop()方法执行过程中,通过Message.target.dispatchMessage(msg)方法将Message交给Handler执行。
1 /** 2 * Handle system messages here. 3 */ 4 public void dispatchMessage(Message msg) { 5 if (msg.callback != null) { 6 handleCallback(msg); 7 } else { 8 if (mCallback != null) { 9 if (mCallback.handleMessage(msg)) { 10 return; 11 } 12 } 13 handleMessage(msg); 14 } 15 }
2. ThreadLocal是什么?
ThreadLocal是线程内部的一个数据存储类,可以在指定线程中存储数据,数据存储后,只能在指定的线程中获取ThreadLocal中存储的数据。其它线程是不能修改和获取在指定线程中ThreadLocal对象存储的数据。
二、Handler引起的内存泄漏及解决办法
1 public class MainActivity extends Activity 2 { 3 4 private Handler mHandler = new Handler(); 5 6 @Override 7 protected void onCreate(Bundle savedInstanceState) 8 { 9 super.onCreate(savedInstanceState); 10 setContentView(R.layout.activity_main); 11 Button enterBtn = (Button) findViewById(R.id.enter); 12 enterBtn.setOnClickListener(new View.OnClickListener() 13 { 14 @Override 15 public void onClick(View v) 16 { 17 18 } 19 }); 20 } 21 }
在Activity中创建一个Handler对象,由于Java原因,非静态Handler在创建时会匿名持有Activity对象引用。 一句话就是在Java类中创建一个非静态的成员变量,成员变量会匿名的持有外部类的对象引用。
泄漏原因:静态内部类持有外部类的匿名引用,导致Activity对象在销毁时无法释放。
解决方法:
1. 在Handler内部将Activity引用改为弱引用;
1 private Handler mHandler = new LeakHandler(this); 2 3 private static class LeakHandler extends Handler { 4 5 private WeakReference<MainActivity> mWeakActivity; 6 7 private LeakHandler(MainActivity activity) { 8 mWeakActivity = new WeakReference<MainActivity>(activity); 9 } 10 11 @Override 12 public void handleMessage(Message msg) { 13 super.handleMessage(msg); 14 } 15 }
2. 将Handler声明静态对象;
1 private static Handler mHandler = new Handler();
3. 在Activity的生命周期方法onDestroy()中调用removeCallbacksAndMessage(null)方法;
1 @Override 2 public void onDestroy() { 3 super.onDestroy(); 4 5 mHandler.removeCallbacksAndMessages(null); 6 }
使用removeCallbacksAndMessages(null)方法意图删除当前mHandler中指定Message消息。而参数传null是意图删除当前mHandler的消息队列。
三、总结:
1. Looper
在Handler中创建Looper时,通过调用Looper.myLooper()方法返回Looper对象。
首先,Looper是通过prepare()方法创建Looper对象并将其保存在ThreadLocal中。
然后,通过Looper.loop()方法开启循环完成消息的分发。
最终,在loop()方法中的无限循环中通过msg.target.dispatchMessage(msg)方法,将Message交给Handler执行。
2. Handler
作用:
(1)发送消息
(2)接收消息
(3)处理消息
3. ThreadLocal
ThreadLocal是在指定线程中数据存储类,由ThreadLocal存储的数据,只能由指定线程获取和删除及对数据的其它操作。