Handler 機(jī)制和原理

Handler我們在項(xiàng)目中經(jīng)常用到,因?yàn)锳ndroid中避免在主線程中處理耗時操作,我們就會借助Handler來更新數(shù)據(jù),使用方式就在主線程中創(chuàng)建Handler對象,在例如請求網(wǎng)絡(luò)數(shù)據(jù)時的子線程中發(fā)送消息,在主線程中收到消息,更新ui。

Handler的消息處理由以下幾個部分組成

Looper, Message, MessageQueue,ThreadLocal

我們先看一下各個名詞的源碼描述部分:

Meeage

image.png

翻譯一下:定義一個消息,該消息包含描述和任意的數(shù)據(jù)對象,用來發(fā)送給Handler,Message包含2個int的字段和一個額外的對象字段。
可以通過obtain Message.obtain()或Handler.obtainMessage()來的到Message

/**
     * User-defined message code so that the recipient can identify
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
    public int what;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;

    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     *
     * <p>Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     */
    public Object obj;

what用來命名的,區(qū)別其他的Handler
arg1arg2用來存儲int數(shù)據(jù)的
what是用來攜帶對象數(shù)據(jù)的

MessageQueue

/**
 * Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 *
 * <p>You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */

被Looper分配的消息列表,Messages不是直接添加到MessageQueue中的,而是通過Looper相關(guān)聯(lián)的Hander添加的
你可以通過Looper.myQueue來檢索當(dāng)前的MessageQueue

Looper

/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  */

Looper用于為線程運(yùn)行消息循環(huán)的,線程默認(rèn)是沒有消息循環(huán)的,要創(chuàng)建一個,請調(diào)用Looper.prepare在執(zhí)行循環(huán)的線程中,然后在調(diào)用Looper.loop讓它處理消息,直到循環(huán)停止。
示例如下:

class LooperThread extends Thread {
        public Handler mHandler;
  
        public void run() {
            Looper.prepare(); 
            mHandler = new Handler() {
 
              public void handleMessage(Message msg) {
 
                  // process incoming messages here
               }
  
          };
              Looper.loop();
      }

這段文案描述了Handler和Looper的使用情況,但是我們在主線程中使用Handler并沒有使用Looper.prepare()和 Looper.loop()
類似這種寫法:

public class TestHandlerActivity extends AppCompatActivity {


        private static final String TAG = "TestHandlerActivity";

        private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //獲得剛才發(fā)送的Message對象,然后在這里進(jìn)行UI操作
                Log.e(TAG,"------------> msg.what = " + msg.what);
            }
        };


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler_test);
            initData();
        }

        private void initData() {

            //開啟一個線程模擬處理耗時的操作
            new Thread(new Runnable() {
                @Override
                public void run() {

                    SystemClock.sleep(2000);
                    //通過Handler發(fā)送一個消息切換回主線程(mHandler所在的線程)
                    mHandler.sendEmptyMessage(0);
                }
            }).start();

        }   

很奇怪是不是

我們看一下ActivityThread的啟動函數(shù)Main函數(shù)

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // Install selective syscall interception
        AndroidOs.install();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

原來是在main方法中調(diào)用Looper.prepareMainLooper()和Looper.loop()

我們來看一下prepareMainLooper這個方法

/**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

main looper是Android應(yīng)用啟動時就創(chuàng)建了,你用不著來調(diào)用這個方法。
在子線程中創(chuàng)建Handler,示例如下:

//開啟一個線程模擬處理耗時的操作
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mHandler.sendEmptyMessage(0);

                    //調(diào)用Looper.prepare()方法
                    Looper.prepare();

                    mHandlerThread = new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.e("sub thread","---------> msg.what = " + msg.what);
                        }
                    };

                    mHandlerThread.sendEmptyMessage(1);

                    //調(diào)用Looper.loop()方法
                    Looper.loop();
                }
            }).start();

ThreadLocal

我們來看一下ThreadLocal的文字描述部分

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
 * and remains unchanged on subsequent calls.
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId {
 *     // Atomic integer containing the next thread ID to be assigned
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // Thread local variable containing each thread's ID
 *     private static final ThreadLocal&lt;Integer&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() {
 *             &#64;Override protected Integer initialValue() {
 *                 return nextId.getAndIncrement();
 *         }
 *     };
 *
 *     // Returns the current thread's unique ID, assigning it if necessary
 *     public static int get() {
 *         return threadId.get();
 *     }
 * }
 * </pre>
 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the {@code ThreadLocal}
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).
 *
 * @author  Josh Bloch and Doug Lea
 * @since   1.2
 */

ThreadLocal 不是 Thread,是一個線程內(nèi)部的數(shù)據(jù)存儲類,通過它可以在指定的線程中存儲數(shù)據(jù),對數(shù)據(jù)存儲后,只有在線程中才可以獲取到存儲的數(shù)據(jù),對于其他線程來說是無法獲取到數(shù)據(jù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容