天天看點

一次對Handler的回憶~

幾個熱身問題

一、handler記憶體洩漏測試

1、handler的基本使用

2、handler發送的過程中(休眠2s),關閉activity,消息仍然可以接收到。

3、使用removeMessage的方式測試,發現沒用。原因是還沒有壓入隊列。

4、destory的時候直接置空handler,發現有用。

5、message.recycle方式:如果已經進消息隊列了,還好說,如果還沒進入消息隊列,則會報異常。

二、不能在子線程中建立handler

1、理由是子線程中的looper還是空的

三、TextView.setText()真的隻能在子線程中執行嗎

1、定位到TextView的requestLayout和invalidate方法,requestlayout方法中有檢查代碼的邏輯。是以invalidate在檢查代碼前先執行,就能很神奇的發現setText()能正常在子線程中執行。

四、new Handler的兩種寫法有何差別

五、ThreadLocal的用法和原理

測試代碼片段:

/**
 * 1、Handler記憶體洩露測試
 * 2、為什麼不能在子線程建立Handler
 * 3、textView.setText()隻能在主線程執行,這句話是錯誤!
 * 4、new Handler()兩種寫法有什麼差別?
 * 5、ThreadLocal用法和原理
 */
public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private Message message;

    // 4、new Handler()兩種寫法有什麼差別?
    private Handler handler1 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            startActivity(new Intent(MainActivity.this, PersonalActivity.class));
            return false;
        }
    });

    // 這是谷歌備胎的api,不推薦使用
    private Handler handler2 = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            textView.setText(msg.obj.toString());
        }
    };

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

        textView = findViewById(R.id.tv);
        message = new Message();

        test();
    }

    private void test() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 正常的寫法
//                message.obj = "Net163";
//                message.what = 163;
//                handler2.sendMessage(message);


                // 1、Handler記憶體洩露測試(假象)
                SystemClock.sleep(1000); // 銷毀Activity
//                message.what = 3;
                // This message is already in use.
//                if (handler1 != null) handler1.sendMessage(message); // 跳轉到第二個界面
                // handler1.sendMessageDelayed(message, 3000);

                // 2、為什麼不能在子線程建立Handler
                // new Handler();

                // 3、textView.setText()隻能在主線程執行,這句話是錯誤!
                // textView.setText("彭老師");
                Toast.makeText(MainActivity.this, "孫老師", Toast.LENGTH_SHORT).show();
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("moon >>> ", "onDestroy");

        // 不推薦寫法
        message.recycle();

        handler1.removeMessages(3);
        handler1 = null;
    }
}
           

Demo

随意查閱了幾篇高流量部落格

android Handler機制原了解析

安卓 handler解析

Handler原理架構圖

一次對Handler的回憶~

問題思考:

1.為什麼主線程用_ooper死循環不會引發ANR異常?

答:因為在Looper.next()開啟死循環的時候,一旦 需要等待時或還沒有執行到執行的時候,

會調用NDK裡面的JNI方法,釋放目前時間片,這樣就不會引發ANR異常了

2.為什麼Handler構造方法裡面的ooper不是直接new?

答:如果在Handler構造方法裡面new Looper,怕是無法保證保證Looper唯一-, 隻有用

Looper.prepare()才能保證唯一性, 具體去看prepare方法

3.MessageQueue為什麼要放在L ooper私有構造方法初始化?

答:因為一個線程隻綁定一個L ooper,是以在l ooper構造方法裡面初始化就可以保證mQueue也是

唯一的Thread對應一個Looper對應一個mQueue

4.主線程裡面的ooper.prepare/L ooper.loop,是一直在無限循環裡面的嗎?

答:是的

手寫一個簡化版的handler

Handler.java

public class Handler {

    private Looper mLooper;
    private MessageQueue mQueue;

    public Handler() {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }

    public void sendMessage(Message message) {
        // 将消息放入消息隊列中
        enqueueMessage(message);
    }

    private void enqueueMessage(Message message) {
        // 指派目前handler
        message.target = this;

        // 使用mQueue,将消息放入
        mQueue.enqueueMessage(message);
    }

    public void dispatchMessage(Message msg) {
        handleMessage(msg);
    }

    // 給開發者提供的開放API,用于重寫和回調監聽
    public void handleMessage(Message msg) {
    }
}
           

Looper.java

public class Looper {

    public MessageQueue mQueue;
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    private Looper() {
        mQueue = new MessageQueue();
    }

    public static void prepare() {
        // 主線程隻有唯一一個Looper對象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }

        // 應用啟動時,初始化指派
        sThreadLocal.set(new Looper());
    }

    //
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

    // 輪詢,提取消息
    public static void loop() {
        // 從全局ThreadLocalMap中擷取唯一:Looper對象
        Looper me = myLooper();
        // 從Looper對象中擷取全局唯一消息隊列MessageQueue對象
        final MessageQueue queue = me.mQueue;

        Message resultMessage;
        // 從消息隊列中取消息
        while (true) {
            Message msg = queue.next();

            if (msg != null) {
                if (msg.target != null) msg.target.dispatchMessage(msg);
            }
        }
    }
}
           

Message.java

// 消息對象
public class Message {

    // 辨別
    public int what;
    // Handler對象
    public Handler target;
    // 消息内容
    public Object obj;

    public Message() {
    }

    public Message(Object obj) {
        this.obj = obj;
    }

    // 模拟
    @Override
    public String toString() {
        return obj.toString();
    }
}
           

MessageQueue.java

// 消息隊列
public class MessageQueue {

    // 阻塞隊列
    BlockingQueue<Message> blockingQueue = new ArrayBlockingQueue<>(50);

    // 将Message消息對象存入阻塞隊列中
    public void enqueueMessage(Message message) {
        try {
            blockingQueue.put(message);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 從消息隊列中取出消息
    public Message next() {
        try {
            return blockingQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
           

手寫handlerDemo

架構師系列文章一覽