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

        

     

  • 相关阅读:
    windows下命令行
    利用border画三角形
    正则
    flex布局
    css笔记
    W3C标准
    SEO相关
    左边固定,右边自适应(解决方案)
    容错性测试的测试点
    Charles安装及使用教程
  • 原文地址:https://www.cnblogs.com/naray/p/8807251.html
Copyright © 2020-2023  润新知