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

        

     

  • 相关阅读:
    1024X768大图 (Wallpaper)
    (Mike Lynch)Application of linear weight neural networks to recognition of hand print characters
    瞬间模糊搜索1000万基本句型的语言算法
    单核与双核的竞争 INTEL P4 670对抗820
    FlashFTP工具的自动缓存服务器目录的功能
    LDAP over SSL (LDAPS) Certificate
    Restart the domain controller in Directory Services Restore Mode Remotely
    How do I install Active Directory on my Windows Server 2003 server?
    指针与指针变量(转)
    How to enable LDAP over SSL with a thirdparty certification authority
  • 原文地址:https://www.cnblogs.com/naray/p/8807251.html
Copyright © 2011-2022 走看看