Android Handler進階篇

在Handler基礎篇中講述了Handler原理和使用,下面是從Handler源碼進一步解析Handler。android

1、源碼解析app

1. Handler的構造函數less

 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源碼async

  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獲取到消息隊列。ide

  在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的構造函數實現中建立的oop

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

  當使用Handler在將來某個時刻經過UI線程更新UI,那麼這個Handler建立就要在UI線程的Activity中實現,而不能在內部類中實現,post

1 Handler handler = new Handler(new Handler.Callback{});

 

  在Activity中建立Handler時就已經將Handler和UI線程的Looper及MessageQueue關聯。這樣,在使用handler.sendMessage()或者handler.post()時,將消息添加到消息隊列中。ui

  那麼,在MessageQueue中的Message,Looper是如何獲取將交給Handler執行的。那麼,就要看Looper中的loop()方法this

 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對象存儲的數據。

2、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的消息隊列。

3、總結:

  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存儲的數據,只能由指定線程獲取和刪除及對數據的其它操做。  

相關文章
相關標籤/搜索