Android個人對Handler機(jī)制的理解

一.系統(tǒng)為什么不允許子線程訪問UI線程:

這是因?yàn)閁I線程里面的控件都是非線程安全的,如果在多線程并發(fā)訪問可能會導(dǎo)致UI控件處于不可預(yù)期的狀態(tài)。那么為什么不給控件訪問加上鎖呢?首先,加鎖之后會導(dǎo)致訪問邏輯變得復(fù)雜,其次鎖機(jī)制會降低UI訪問的效率,因?yàn)殒i機(jī)制會阻塞某些線程的執(zhí)行。

二.消息機(jī)制的幾個重要對象:

Handler 發(fā)送和接收消息
Looper 用于輪詢消息隊列,一個線程只能有一個Looper
Message 消息實(shí)體
MessageQueue 消息隊列,存放消息實(shí)體的載體

三.創(chuàng)建Looper

在ActivityThread中的main方法中為我們prepare了

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        //其他代碼省略...
        Looper.prepareMainLooper(); //初始化Looper以及MessageQueue

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

        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");
    }

Looper.prepareMainLooper();

 public static void prepareMainLooper() {
        prepare(false);//消息隊列不可以quit
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepare有兩個重載的方法,主要看 prepare(boolean quitAllowed) quitAllowed的作用是在創(chuàng)建MessageQueue時標(biāo)識消息隊列是否可以銷毀, 主線程不可被銷毀 下面有介紹

  public static void prepare() {
        prepare(true);//消息隊列可以quit
    }
    //quitAllowed 主要
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//不為空表示當(dāng)前線程已經(jīng)創(chuàng)建了Looper
            throw new RuntimeException("Only one Looper may be created per thread");
            //每個線程只能創(chuàng)建一個Looper
        }
        sThreadLocal.set(new Looper(quitAllowed));//創(chuàng)建Looper并設(shè)置給sThreadLocal,這樣get的時候就不會為null了
    }

創(chuàng)建MessageQueue以及Looper與當(dāng)前線程的綁定

 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//創(chuàng)建了MessageQueue
        mThread = Thread.currentThread(); //當(dāng)前線程的綁定
   }

MessageQueue的構(gòu)造方法

MessageQueue(boolean quitAllowed) {
 //mQuitAllowed決定隊列是否可以銷毀 主線程的隊列不可以被銷毀需要傳入false, 在MessageQueue的quit()方法就不貼源碼了
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

Looper.loop()
同時是在main方法中 Looper.prepareMainLooper() 后Looper.loop(); 開始輪詢

public static void loop() {
        final Looper me = myLooper();//里面調(diào)用了sThreadLocal.get()獲得剛才創(chuàng)建的Looper對象
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }//如果Looper為空則會拋出異常
        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 (;;) {
            //這是一個死循環(huán),從消息隊列不斷的取消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                //由于剛創(chuàng)建MessageQueue就開始輪詢,隊列里是沒有消息的,等到Handler sendMessage enqueueMessage后
                //隊列里才有消息
                // 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);//msg.target就是綁定的Handler,詳見后面Message的部分,Handler開始
            //后面代碼省略.....

            msg.recycleUnchecked();
        }
    }

四.創(chuàng)建Handler

最常見的創(chuàng)建handler

  Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

在內(nèi)部調(diào)用 this(null, false);

public Handler(Callback callback, boolean async) {
        //前面省略
        mLooper = Looper.myLooper();//獲取Looper,**注意不是創(chuàng)建Looper**!
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//獲得消息隊列MessageQueue
        mCallback = callback; //初始化了回調(diào)接口
        mAsynchronous = async;
    }

Looper.myLooper();

 //這是Handler中定義的ThreadLocal  ThreadLocal主要解多線程并發(fā)的問題
 // sThreadLocal.get() will return null unless you've called prepare().
 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

五.Handler消息機(jī)制整個流程

1.handler內(nèi)部與Looper關(guān)聯(lián),handler->Looper->MessageQueue,handler發(fā)送消息就是向MessageQueue隊列發(fā)送消息。
2.Looper通過Looper.loop()不斷循環(huán)的方法并把消息(通過先進(jìn)先出的順序)通過dispatchMessage方法回傳給handler自己。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//callback在message的構(gòu)造方法中初始化或者使用handler.post(Runnable)時候才不為空
        handleCallback(msg);
    } else {
        if (mCallback != null) {//mCallback是一個Callback對象,通過無參的構(gòu)造方法創(chuàng)建出來的handler,該屬性為null,此段不執(zhí)行
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//最終執(zhí)行handleMessage方法
    }
}
 private static void handleCallback(Message message) {
        message.callback.run();
    }
流程圖

注意:主線程中的Looper.loop()一直無限循環(huán)為什么不會造成ANR?

也許講到這里,很多人已經(jīng)知道原因了吧!不過習(xí)慣使然,我還是要總結(jié)一下。主線程Looper從消息隊列讀取消息,當(dāng)讀完所有消息時,主線程阻塞。子線程往消息隊列發(fā)送消息,并且往管道文件寫數(shù)據(jù),主線程即被喚醒,從管道文件讀取數(shù)據(jù),主線程被喚醒只是為了讀取消息,當(dāng)消息讀取完畢,再次睡眠。因此loop的循環(huán)并不會對CPU性能有過多的消耗。

最后編輯于
?著作權(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)容