舉個栗子來分析handler原理

看了網(wǎng)上的一些handler原理分析,是從源碼級別直接看的。但是總覺得沒有從實戰(zhàn)上分析嗎,感覺有點蹩腳。這篇文章算是自己分析的,也參考了《android開發(fā)藝術探索》,還有網(wǎng)上的一些其他資料,如有侵權請私信

先來舉個栗子吧

一般在使用handler的時候,用其來更新UI,也就是說在主線程進行更新界面操作,當時子線程請求網(wǎng)絡數(shù)據(jù),如此handler剛好派上用場。先看實例吧

public class MainActivity extends Activity {
 
    private TextView text;
    private Handler handler = new Handler() {
 
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    text.setText("使用Handler更新了界面");
                    break;
            }
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.id_text);
 
        new Thread("#Thread1") {
 
            @Override
            public void run() {
                //...你的業(yè)務邏輯;
                Message message = new Message();//發(fā)送一個消息,該消息用于在handleMessage中區(qū)分是誰發(fā)過來的消息;
                message.what = 1;
                handler.sendMessage(message);
            }
        }.start();
    }
}

以上的例子呢,應該算是handler使用的最普遍的,當然還有其它用法,handler創(chuàng)建在主線程中,也就是Activitythread,在"#Thread1"中執(zhí)行耗時操作,在主線程中更新UI,創(chuàng)建handler。
在創(chuàng)建handler之前需要注意:
在創(chuàng)建handler之前,必須先在相同線程內(nèi)創(chuàng)建一個Looper,每個線程中最多只能有一個 Looper 對象,由 Looper 來管理此線程里的 MessageQueue (消息隊列)。



如果不在主線程創(chuàng)建handler的話,應該這么寫代碼:

new Thread("Thread#2"){
@override
  public void run(){
    Looper.prepare();
    Handler handler = new Handler();
    Looper.loop();
  }
}

首先利用Looper.prepare()創(chuàng)建looper,然后創(chuàng)建handler,然后調用Looper.loop()開啟消息循環(huán)。這是在非UI線程,但是我們在之前的handler使用例子中并未簡單looper的創(chuàng)建啊,其實looper 已經(jīng)在activityThread當中給你創(chuàng)建完了??梢詠砜纯丛创a

public static void main(String[] args) {
        、、、、、此處省略部分代碼
        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

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

        AsyncTask.init();

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

        Looper.loop();

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

可以看出activitythread執(zhí)行了兩個looper方法

 Looper.prepareMainLooper();
 Looper.loop();

所以我們只需要創(chuàng)建handler就可以了。那我們接著看我們文章開頭那個實例,重寫了handler的handlemessage方法,然后新建線程,利用handler發(fā)送消息。然后主線程的handler就會接收到消息,并在handlemessage中處理消息。上面整體思路沒問題吧,這也是最初在使用handler的時候,大家的通常思維,那handler到底是怎么切換線程的,從thread1切換到了主線程???
先上一張整體的流程圖

handler消息機制.png

這張圖是我自己畫的,可能畫的不太好,也看了一些大佬的流程圖,畢竟我基礎差,總覺得他們的圖理解起來不是很容易。


MessaggeQueue類詳解

根據(jù)我們的操作先來看啊,主線程已經(jīng)準備就緒,handler創(chuàng)建完了,然后調用sendmessage方法,最后調用的事enqueuemessage()方法,然后就到了messgaeQueue類,這個類又是從哪來的呢????這個類其實是從Looper里面蹦出來的,在創(chuàng)建Looper的時候,就會生成MessageQueue,來看一下源碼

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    /**
     * 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函數(shù)中,已經(jīng)調用了prepareMainLooper()方法,而后prepareMainLooper()方法又調用了 prepare()方法,在這個方法里面,我們調用looper的構造函數(shù),并將新建的looper存入到了ThreadLocal里面(ThreadLocal后邊講),然后我們來看這個looper的構造函數(shù)

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

可以清晰的看到Looper在構造函數(shù)中創(chuàng)建了MessageQueue,知道了MessageQueue的由來,我們簡單來說一下MessageQueue這個類啊
enqueueMessage():這個是通過維護一個單鏈表,handler添加消息以后,MessageQueue會將消息添加到最后。
next():用于取出消息,并將消息傳遞給Looper 。注意這個next方法很重要,是個死循環(huán),當沒有消息的時候會一直阻塞在這里,直到有消息會將消息傳遞出去,代碼如下,不用看的很詳細。

 Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

Looper類詳解

我們這時候來看看looper,looper是在主線程的,剛剛說過了,looper在handler創(chuàng)建之前就已經(jīng)創(chuàng)建完成了,順帶把MessageQueue和looper.loop()方法都執(zhí)行了。這時候我們來看看這個loop方法是干嘛地

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

和上面next方法一樣,這塊也是個死循環(huán),一直在等待messagequeue的消息。等它拿到消息以后,就會調用msg.target.dispatchMessage(msg);這行代碼,這行代碼其實就是調用了handler的dispatchMessage方法,后邊經(jīng)過一些列的判斷會調用handler的handleMessage()方法。這是整體過程。

總結

下面從別的文章上抄來的圖,簡單粗暴。很明顯就能看出如何切換線程的,首先主線程就相當于A線程,B線程就相當于#Thread1
POST就相當于在thread1里面使用handler發(fā)送消息,然后把消息放在messageQueue當中,主線程里面的Looper就回調用loop方法,一直從messagequeue類的next方法中獲取消息,然后再次調用handler消化這個消息。

image.png


引用的技術文章
https://blog.csdn.net/AdobeSolo/article/details/75195394
https://mp.weixin.qq.com/s/D1v7b5CUT-3JMhxR6iCpcA
https://blog.csdn.net/CHENYUFENG1991/article/details/46910675
《android開發(fā)藝術探索》

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

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

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