幾個熱身問題
一、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原理架構圖
問題思考:
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
架構師系列文章一覽