文章目錄
- Hander 簡單使用
- 原始碼分析
- Looper
- Message
- Handler
- MessageQueue
- Handler的記憶體泄露
Hander 簡單使用
handler機制是執行緒間進行通信的一種簡單方式,在Android中常用于在子執行緒中請求網路資料,然后利用handler通知ui執行緒進行ui渲染,它的通信是單向的,只能一個執行緒進行接收,另外一個執行緒進行發送,下面的2個例子分別是從子執行緒給主執行緒發送訊息和從主執行緒給子執行緒發訊息
/**
* 子執行緒往主執行緒發訊息
*/
private fun work2UiHandler()
{
//注意 Handler的無參建構式已經在 SDK 30版本已經被廢棄了
//用戶需要顯性的提供當前執行緒的Looper,這里我們提供的主執行緒的Looper
val handler = object: Handler(Looper.getMainLooper())
{
override fun handleMessage(msg: Message)
{
when(msg.what)
{
10001->Log.i("mainActivity","主執行緒更新 ---${msg.obj}")
}
}
}
Thread{
//子執行緒進行耗時操作
//這里得Message建議不要用new的方式去獲取,因為Message內部自己維護了一個快取的訊息池
//當message 被處理完畢后,系統會自動回收,
// 如果是自己new的Message,每次使用后,系統會放入到訊息池中,
//沒有進行一個回收的程序,會占用記憶體,后面會有原始碼分析該訊息快取機制
val message = Message.obtain()
message.what = 10001
message.obj = "這是子執行緒耗時獲取的資料"
//通知主執行緒執行緒進行UI更新
handler.sendMessage(message)
}.start()
}
/**
* 從主執行緒給子執行緒發訊息
*/
private fun ui2WorkHandler()
{
val workThread = WorkThread()
workThread.start()
//等待作業的執行緒的run方法執行結束,即handler初始化完成
Thread.sleep(1000)
//通過handler獲取message 其內部最后還是呼叫的Message.obtainMessage()
val message = workThread.mHandler.obtainMessage()
message.what = 10002
message.obj = "這是主執行緒發送的資料"
workThread.mHandler.sendMessage(message)
}
class WorkThread:Thread()
{
lateinit var mHandler:Handler
override fun run() {
//為當前的作業執行緒初始化looper
Looper.prepare()
//初始化作業執行緒的的handler Looper.myLooper獲取的即為與當前執行緒系結的looper
mHandler = object : Handler(Looper.myLooper()!!)
{
override fun handleMessage(msg: Message) {
when(msg.what)
{
10002->Log.i("mainActivity","子執行緒收到訊息 ---${msg.obj}")
}
}
}
//looper的任務訊息佇列開始輪詢執行
//后續原始碼會詳細分析
Looper.loop()
}
}
原始碼分析
關于下面四個類之間的關系
從上面的關系可以看出,Looper是Handler和MessageQueue中間橋梁,資料載體是Message, MessageQueue存盤Handler發送過來的Message,Handler會持有Looper,Looper會持有MessageQueue
Looper
public class Looper
{
//每一個執行緒 都會關聯一個ThreadLocalMap<ThreadLocal,Looper> 該map中存盤的是ThreadLocal-->Looper的鍵值對
//即 一個thread 對應 一個 ThreadLocalMap,但是map中存盤的ThreadLocal均為同一個,Looper就不同
//總結即 一個thread 至多只能系結一個Looper(也可為null,即當前執行緒未系結Looper,需呼叫Looper.perpare()進行執行緒系結)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//主執行緒系結的Looper
private static Looper sMainLooper; // guarded by Looper.class
//Looper與之關聯的訊息佇列
final MessageQueue mQueue;
//針對訊息分發的前后回呼通知
private static Observer sObserver;
//這里可以進行一個全域的設定,去回呼監聽訊息分發的全程序
//但是這個方法被影藏了,如果有需要可以進行Hook二次代理即可
@hide
public static void setObserver(@Nullable Observer observer) {
sObserver = observer;
}
private static void prepare(boolean quitAllowed) {
//呼叫該方法時,會判斷當前的執行緒是否已經系結了Looper,
//sThreadLocal.get()不為null則為當前執行緒是已經系結了一個Looper,此時會拋出例外
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//實體化Looper并將其與當前執行緒進行系結,即將looper存入與執行緒想關聯的ThreadLocalMap中
//具體原始碼本章節不做過多決議
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
//獲取當前執行緒系結的looper,可能為null,為null則為當前執行緒未系結looper
return sThreadLocal.get();
}
//剛方法會在ActivityThread的main方法中被呼叫,即為主執行緒系結了Looper,開發日常所有回呼到Ui執行緒的looper
//都是 sMainLooper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//開始輪詢訊息佇列,處理訊息
public static void loop() {
//獲取當前的執行緒的looper
final Looper me = myLooper();
...
//獲取looper所關聯的messageQueue
final MessageQueue queue = me.mQueue;
...
//輪詢佇列
for (;;) {
//取出訊息,當無訊息時,該執行緒會處于一個阻塞的狀態
Message msg = queue.next(); // might block
...
if (observer != null) {
//分發訊息開始
token = observer.messageDispatchStarting();
}
...
try {
//msg.target所代表的目標是 handler,分析Message時會講到
//這里則是handler分發處理訊息起始點
msg.target.dispatchMessage(msg);
//分發訊息結束
observer.messageDispatched(token, msg);
} catch (Exception exception) {
//分發的當次訊息拋出了例外
observer.dispatchingThrewException(token, msg, exception);
}
...
//回收訊息
msg.recycleUnchecked();
}
}
}
Message
public class Message
{
//代表Message的標識
public int what;
public int arg1;
public int arg2;
public Object obj;
//handler發送訊息時,會與message進行一個系結,該值即為當前的handler
//這個target很重要
Handler target;
//可賦值,代表Message被分發處理時,會被執行的一個任務
Runnable callback;
//Message在鏈表結構中的的下一個節點
//該快取池是以單鏈表的方式存盤的
Message next;
//Message快取池(單鏈表的頭指標)
private static Message sPool;
//快取池的初始大小
private static int sPoolSize = 0;
//快取池的最大容量
private static final int MAX_POOL_SIZE = 50;
public static Message obtain() {
synchronized (sPoolSync) {
//初始快取池為null,會直接new Message回傳實體物件
if (sPool != null) {
//當該鏈表的頭指標不為null,即當前快取池中已經有了可以重新回收的message物件
//這里取出頭指標
Message m = sPool;
//將當前的頭指標指向下一個節點
sPool = m.next;
//取出的頭指標的next置null
m.next = null;
//size-1
sPoolSize--;
//將頭指標回傳出去,即從快取池中取出了一個已經回收的Message物件
return m;
}
}
return new Message();
}
void recycleUnchecked() {
//清除Message攜帶的所有資料
...
synchronized (sPoolSync) {
//如果當前的快取池大小 未達到最大容量,會將該回收的message置于快取池中(單鏈表)
//頭插法
if (sPoolSize < MAX_POOL_SIZE) {
//尾指標指向下個節點
next = sPool;
//當前節點為頭指標
sPool = this;
//size+1
sPoolSize++;
//當下一個Message(這里指 msgB物件)又被回收后,msgB的next指向上一個節點,頭指標指向當前的msgB物件
//以此類推,后面每一個被回收的message都會被插入到鏈表的最前端
}
}
}
//利用message系結的handler直接去發送訊息
public void sendToTarget() {
target.sendMessage(this);
}
//這里也是獲取一個快取池中的Message,只不多了handler和callback
//這么做的目的是直接將兩者系結到message中去,往往在獲取到該Message后可以直接去呼叫sendToTarget()發送訊息
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
}
Handler
public class Handler
{
//與handler系結的looper,一一對應
final Looper mLooper;
//looper中的訊息佇列
final MessageQueue mQueue;
//回呼函式
final Callback mCallback;
//沒有傳入looper時,會呼叫Looper.myLooper()獲取與當前執行緒系結的looper
public Handler(@Nullable Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
...
//將looper中訊息佇列賦值
mQueue = mLooper.mQueue;
//handler的回呼函式,在dispatchMessage()分發訊息方法中會被呼叫
mCallback = callback;
mAsynchronous = async;
}
//通過Runnable 獲取message 將runnable任務與message系結,最后也會在dispatchMessage()分發訊息方法中會被呼叫
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
//所有的post的方法 最后都會走到sendMessageAtTime
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
//SystemClock.uptimeMillis() + delayMillis 是否延遲執行 即在定點的時間上去執行
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
...
//將訊息放入訊息佇列中
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//將msg的target與當前handler進行一個一對一系結
msg.target = this;
...
//入佇列
return queue.enqueueMessage(msg, uptimeMillis);
}
//handler的回呼函式
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
//handler的處理訊息邏輯,用戶需要自己重寫
public void handleMessage(@NonNull Message msg) {
}
//該方法很重要
public void dispatchMessage(@NonNull Message msg) {
//該方法在Looper.loop()中輪詢佇列時,取出訊息后
// message.target.dispatchMessage(message) 執行
//這里就是Message的callback不為null時會執行處理訊息
if (msg.callback != null) {
handleCallback(msg);
} else {
//當Message的callback不去消費處理訊息
//handler自身的回呼函式會首先去處理訊息
if (mCallback != null) {
//如果handler的mCallBack.handleMessage方法去消費處理訊息時
//回傳的是true則handler自身的handleMessage方法就不會被執行
//回傳的是false是,就還會去執行handleMessage
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
//總結即:Message的callback最先被執行,然后才是handler自身的CallBack回呼函式執行
//而handler的handleMessage方法則需要看CallBack中函式回傳值來決定是否執行
}
}
}
MessageQueue
/**
* 負責管理訊息佇列,實際上Message類有一個next欄位,
* 會將Message物件串在一起成為一個訊息佇列,
* 所以并不需要LinkedList之類的資料結構將Message物件組在一起成為佇列,
*/
public class MessageQueue
{
//native層需要用的句柄
private long mPtr;
//native層去執行一個訊息佇列的初始化
private native static long nativeInit();
//該程序是一個阻塞的操作
private native void nativePollOnce(long ptr, int timeoutMillis);
MessageQueue(boolean quitAllowed) {
//允許退出
mQuitAllowed = quitAllowed;
//初始化獲取句柄
mPtr = nativeInit();
}
/**
*
* 用于獲取下一個Message物件,如果沒有需要處理的Message物件,該方法將阻塞,
* MessageQueue用本地方法做同步互斥,因為這樣時間更精準,
* 每個Message物件都有一個什么時刻處理該Message物件的屬性when,
* 沒到時間都不會處理該Message物件,如果時間不精準的話,會導致系統訊息不能及時處理,
*/
Message next() {
//具體代碼不做過多分析,后續會出一個新章節來專門分析 MessageQueue的實作原理
...
}
/**
* 佇列中的Message觸發時間是有先后順序的,
* 當訊息加入訊息佇列時,會從佇列頭開始遍歷,直到找到訊息應該插入的合適位置,
* 以保證所有訊息的時間順序(內部遍歷佇列中Message,找到when比當前Message的when大的Message,
* 將Message插入到該Message之前,如果沒找到則將Message插入到佇列最后),
* 一般是當前佇列為空的情況下,
* next那邊會進入睡眠,需要的時候MessageQueue這邊會喚醒next方法,
*/
boolean enqueueMessage(Message msg, long when)
{
//具體代碼不做過多分析,后續會出一個新章節來專門分析 MessageQueue的實作原理
...
}
}
Handler的記憶體泄露
private val mHandler = object :Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
Log.i("HandlerLinkActivity","處理訊息")
}
}
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//延時2分鐘發送一個空訊息
mHandler.sendEmptyMessageDelayed(100086,2*60*1000)
}
這里在Activity中延遲2分鐘去發送一個訊息,然后再這兩分鐘內,按回傳鍵去關閉Activity此時就會存在一個記憶體泄露的現象,造成記憶體泄露的原因時,mHandler是一個匿名的內部類,它會持有一個外部內的參考即當前Activity的參考,當gc時,發現Activity還在被參考時,就無法去回收記憶體
解決方法:
/**
* 避免handle記憶體泄露的辦法
* 1.使用static 修飾的handler,但是一般會弱參考activity物件,因為要使用activity物件中的成員
* 2.單獨定義handler,同樣可以弱參考activity
* 3.使用內部類的handler,在onDestroy方法中removeCallbacksAndMessages
*/
class StaticHandler(activity: HandlerLinkActivity) :Handler(Looper.getMainLooper())
{
var mWeakReference: WeakReference<HandlerLinkActivity> = WeakReference(activity)
override fun handleMessage(msg: Message) {
mWeakReference.get().run {
//執行更新ui
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/236624.html
標籤:其他
上一篇:Harmony OS 打包
下一篇:親測有效,關于Android出現the emulator process for Android was killed.
