Handler

Handler是什么?

handler是線程間消息傳遞的橋梁,主要用來發(fā)送消息和處理消息。

為什么需要做線程間的通信?

Android不能在主線程(UI線程 / ActivityThread)中做耗時操作,而子線程不能更新UI,當子線程需要更新UI時候就需要通過Handler切換到主線程中。

為什么子線程不能更新UI?

Android的UI控件是線程不安全的,如果在多線程中訪問同一個控件,會讓控件處于一個不可知的狀態(tài)。

為何系統(tǒng)不對控件進行加鎖呢?

加鎖機制會讓控件邏輯變得復雜,同時降低了控件的訪問效率,加鎖會阻止某些線程的執(zhí)行。

Handler如何發(fā)送消息和處理消息?

Handler工作流程圖
  • Handler
    負責Message的發(fā)送和處理,按照先進先出執(zhí)行,內部使用的是單鏈表結構。
    sendMessage( )發(fā)送消息去消息池,handleMessage( )進行消息的處理。
  • MessageQueue
    負責管理和存儲由Handler發(fā)送過來的Message。讀取會自動刪除消息,單鏈表維護,插入和刪除上有優(yōu)勢。
  • Message
    需要被傳遞的消息,分為硬件產生的消息和軟件產生的消息。
  • Looper
    Looper通過loop( )開啟無限循環(huán),通過MessageQueue的next( )方法不斷獲取消息,一旦拿到消息就會分發(fā)給Handler進行處理,否則會在next( )中阻塞。當Looper( )的quit( ) 調用會導致MessageQueue中的quit( )調用,next( )返回null,loop退出。

主線程創(chuàng)建的時候會創(chuàng)建一個Looper,同時也會在Looper內部創(chuàng)建一個消息隊列。而在創(chuàng)建Handler的時候取出當前線程的Looper,并通過該Looper對象獲取消息隊列,然后Handler在子線程通過Message.enqueueMessage在消息隊列添加消息。Looper.loop( )開啟循環(huán)從隊列中獲取消息后通過dispatchMessage( )發(fā)送給Handler,最終由handleMessage( )進行處理。

一個線程可以有多個Handler?

一個線程只會有一個Looper和一個消息隊列,但是可以有多個Handler( )。

Looper的死循環(huán)為什么不會導致系統(tǒng)卡死?

主線程的主要方法是消息循環(huán),是要維持主線程一直存活的,一旦消息循環(huán)退出也就意味這應用退出。Looper.loop( )可能會引起線程的阻塞,但只要他的消息循環(huán)沒有被阻塞可以一直處理消息就不會產生ANR。造成ANR的原因不是主線程阻塞,而是處理消息的消息循環(huán)阻塞,無法響應用戶的操作,不能即時的更新UI。阻塞與程序無響應沒有必然聯(lián)系,雖然在沒有消息的時候消息循環(huán)會阻塞在MessageQueue的next( )中(此時主線程會釋放CPU資源進入休眠狀態(tài)),但是只要有新消息的產生可以立刻進行處理是不會發(fā)生程序無響應的。

可以在子線程中創(chuàng)建Handler嗎?

可以創(chuàng)建,但是需要自己維護一個Looper,并且要在所有事情做完后調用quit( )來終止消息循環(huán),否則這個線程會一直處于等待(阻塞)狀態(tài)。主線程在創(chuàng)建的時候會自動創(chuàng)建Looper,會自動管理處理發(fā)送過來的消息。Loooer在那個線程創(chuàng)建,就綁定那個線程,并且Handler是在他關聯(lián)的Looper對應的線程中處理消息的。

                 new Thread(new Runnable() {
                        @Override
                        public void run() {
                            Looper.prepare();
                            new Handler(){
                                @Override
                                public void handleMessage(Message msg) {
                                    super.handleMessage(msg);
                                }
                            };
                            Looper.loop();
                        }
                    });

Handler導致內存泄漏?

1.handler允許我們發(fā)送延時消息postDelay( ) ,如果在延時期間用戶關閉了Activity,那么該Activity就會泄漏。因為Message會持有Handler,又因為Java的特性,內部類會持有外部類,這樣Activity就被Handler所持有,導致泄漏。
1-1.所以,將Handler定義為靜態(tài)內部類,在內部持有外部Activity的弱引用,并且在Activity退出的時候移除所有消息。

2.手動為一個子線程創(chuàng)建Looper,當消息處理完畢后,不手動退出的情況下會一直處于等待狀態(tài),而以quit( )退出Looper后,線程會立刻關閉。

Handler、Thread和HandlerThread的差別?

handler實現(xiàn)了線程間的通信,thread是java進程中執(zhí)行運算的最小單位,也是執(zhí)行處理機調度的基本單位。Android沒有對Java的Thread進行封裝,而是提供了一個繼承自Thread的HandlerThread,內部維護了一個Looper,這是Handler消息機制必不可少的,有了looper才能做到消息的發(fā)放和處理,如果不用HandlerThread就需要自行維護一個Looper。

Message創(chuàng)建方式?

    Message m = new Message();
    Message m2 = Message.obtain();
    Message m3 = mHandler.obtainMessage();

第一種方法是直接實例化一個Message對象,后兩者直接從Android的消息池(單項鏈表,默認消息池數量為10)中獲取一個Message實例,這樣可以避免多生成Message實例浪費內存。

消息池中Message的對象是通過recycle()放進去的.在Looper的loop()方法的最后調用了Message對象的recycle()方法來回收這個Message對象,通過recycle()將這個Message對象的數據清空然后鏈接到消息池中(采用的頭插法)。

其他子線程更新UI的方法?

除了Handler還有

  • runOnUiThread( )
new Thread(new Runnable() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mDiv.setVisibility(View.GONE);
                    }
                });
            }
        }).start();
  • view.Post( )
   mNickName.post(new Runnable() {
                    @Override
                    public void run() {
                        mNickName.setText("********");
                    }
                });
  • AsyncTask
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容