Android Handler进阶篇

在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存储的数据,只能由指定线程获取和删除及对数据的其它操作。  

    

 

posted @ 2018-04-25 11:14  naray  阅读(1628)  评论(0编辑  收藏  举报