Handler面試知識小結

前言

今天我們來回顧複習下Handler,處於性能優化的考慮,Android的UI線程是線程不安全的。爲了避免多個線程併發操作UI的引發UI顯示錯亂問題,Android指定只允許在UI線程修改更新UI組件。其他線程更新UI拋出android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.異常,當我們在子線程做完耗時操作時,需要更新UI時,我們就需要通過Handler來實現啦。

1、Handler是什麼?

1、Handler通過發送和處理Message和Runnable對象來關聯相對應線程的MessageQueue。

2、線程之間通信。

2、Handler使用方法

  • post(runnable)

  • sendMessage(message)

3、Handler工作原理

handler1
這裏寫圖片描述

  • Handler: 負責發送消息和處理消息。
  • Looper: 負責管理MessageQueue,從MessageQueue中取出消息,交給消息對應的Handler處理。一個線程只有一個Looper對象。
  • Message: Handler接收處理的消息對象。
  • MessageQueue: 消息隊列,負責管理Message, 先進先出。程序創建Looper對象時,會在他的構造器裏創建MessageQueue對象。

總結:

1、首先Looper.prepare()在本線程中保存一個Looper實例,然後該實例中保存一個MessageQueue對象;因爲Looper.prepare()在一個線程中只能調用一次,所以MessageQueue在一個線程中只會存在一個。

2、Looper.loop()會讓當前線程進入一個無限循環,不斷從MessageQueue的消息隊列中讀取消息,然後回調msg.target.dispatchMessage(msg)方法。

3、Handler的構造方法,會首先得到當前線程中保存的Looper實例,進而與Looper實例中的MessageQueue相關聯。

4、Handler的sendMessage方法,會給msg的target賦值爲handler自身,然後加入MessageQueue中。

5、在構造Handler實例時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。

Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關係

4、Handler引起的內存泄漏

  • 原因:非靜態內部類持有外部類的匿名引用,導致Activity無法釋放。

  • 解決:

    • Handler內部持有外部Activity的弱引用。
    • Handler改爲靜態內部類。
    • Handler.removeCallback()。

5、代碼實踐

第1種在UI線程創建Handler,子線程發送消息,UI線程收到消息進行處理:

由於在Activity的啓動代碼中在當前UI線程調用了Looper.prepare()和Looper.loop()方法,所以我們在Activity創建Handler就可以了,詳情我們可以查看ActivityThread類的main 方法中,我們就會發現調用了Looper.prepareMainLooper()和Looper.loop()方法。

ActivityThread創建Looper的系統源碼:

public final class ActivityThread {
    public static void main(String[] args) {
    // 省略部分代碼
         //prepareMainLooper()方法中調用了Looper.prepare(false); 創建出Looper對象
        Looper.prepareMainLooper();
        // 開始輪詢
        Looper.loop();
        // 如下代碼我們發現在UI線程我們不能手動調用停止Loop,不然會拋異常。
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

例子:在子線程發送消息在UI線程彈出Toast具體代碼:

package com.example.ling.review.handler;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

import com.example.ling.review.R;

import java.lang.ref.WeakReference;

public class HandlerActivity extends AppCompatActivity {

    private Handler mHandler = new MainHandler(this);

    // Handler改爲靜態內部類,避免內存泄漏
    public static class MainHandler extends Handler {

        private WeakReference<HandlerActivity> mWeakReference = null;

        public MainHandler(HandlerActivity activity) {
            // 將Activity 包裝成弱引用,避免內存泄漏
            mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            // 接收處理消息
            HandlerActivity activity = mWeakReference.get();
            if (activity != null) {
                Toast.makeText(activity, "我在UI線程上哦", Toast.LENGTH_SHORT).show();
            }
        }
    }

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

    private void initView() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    // 第一種 post(runnable)用法
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), "我在UI線程上哦", Toast.LENGTH_SHORT).show();
                        }
                    });

                    // 第二種 sendMessage(message)用法
                    Thread.sleep(2000);
                    Message message = Message.obtain();
                    mHandler.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        // activity 銷燬時 Handler 移除所有回調和信息
        if (mHandler != null) {
            mHandler.removeCallbacksAndMessages(null);
            mHandler = null;
        }
    }
}

第2種子線程創建Handler,在子線程發送消息,創建Handler的子線程中接收到消息進行處理:

 1、子線程使用Handler需要自己創建Looper對象;
 2、Looper創建完,在創建Handler對象;
 3、開啓Looper輪詢,Handler就可以發送、處理消息啦;

例子:


public class HandlerActivity2 extends AppCompatActivity {

    private ThreadHandler mHandler;

    // Handler改爲靜態內部類,避免內存泄漏
    public static class ThreadHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            // 接收處理消息
            Log.d("TAG", "接收消息啦----- 當前線程爲:" + Thread.currentThread().getName());
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        newThread1();
        newThread2();
    }

    /**
     * 創建子線程1 創建Handler
     */
    private void newThread1() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("TAG", "當前線程爲:" + Thread.currentThread().getName());
                // 1、創建Looper
                Looper.prepare();
                // 2、創建Handler
                mHandler = new ThreadHandler();
                // 3、Looper開始輪詢
                Looper.loop();
            }
        }).start();
    }

    /**
     * 創建子線程2 用子線程1的handler 發送消息
     */
    private void newThread2() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Log.d("TAG", "當前線程爲:" + Thread.currentThread().getName());
                    Thread.sleep(2000);
                    Message message = Message.obtain();
                    mHandler.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        // activity 銷燬時 Handler 移除所有回調和信息
        if (mHandler != null) {
            mHandler.removeCallbacksAndMessages(null);
            mHandler = null;
        }
    }
}

運行打印結果如下:
我們發現在Thread-2子線程創建了Handler, 在Thread-3子線程發送消息,接收到消息在Thread-2 子線程。

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章