一效览、什么是Handler機(jī)制
定義:
Handler是用來(lái)結(jié)合線程的消息隊(duì)列來(lái)發(fā)送免胃、處理"Message對(duì)象"和"Runnable對(duì)象"的工具。
通常的來(lái)說(shuō),就是我們?cè)诰€程之間處理消息通知及任務(wù)調(diào)度的工具。
二胯杭、Handler機(jī)制原理
先捋一遍源碼,畫(huà)個(gè)時(shí)序圖受啥,然后等下逐一說(shuō)明做个。
-
相關(guān)類(lèi)
Handler機(jī)制的實(shí)現(xiàn)離不開(kāi)與之相關(guān)的其他三個(gè)類(lèi),Message是Handler發(fā)送的消息實(shí)體腔呜,大部分的消息都是通過(guò)Message來(lái)封裝傳遞的叁温;MessageQueue是用來(lái)將消息按順序排隊(duì)的隊(duì)列;Looper本質(zhì)就是一個(gè)循環(huán)核畴,不停的從MessageQueue中取出消息然后處理膝但。
-
執(zhí)行過(guò)程
首先,如上圖所示任務(wù)的開(kāi)始是由創(chuàng)建一個(gè)Message開(kāi)始的谤草,Message創(chuàng)建完畢后交給Handler對(duì)象發(fā)送跟束,
sendMessage
和sendMessageDelay
最終都是在底層調(diào)用了sendMessageAtTime()
方法,將Message對(duì)象放入MessageQueue中的丑孩。之后冀宴,由Looper的
loop()
方法循環(huán)從MessageQueue中取出Message對(duì)象,調(diào)用message.getTarget ()
獲取到發(fā)送消息的Handler對(duì)象温学,然后再調(diào)用handler.dispatchMessage()
方法將信息分發(fā)給對(duì)應(yīng)handler執(zhí)行略贮。最后,Handler在
dispatchMessage()
方法中判斷是否有callback 存在,存在則執(zhí)行callback的onMessageHandler()
逃延,最終交由Message.callback執(zhí)行览妖;否則則執(zhí)行handler的onMessageHandler()
方法。public interface Callback {// callback接口 public boolean handleMessage(Message msg); } public void handleMessage(Message msg) { } public void dispatchMessage(Message msg) { if (msg.callback != null) { /* * callback是msg中的一個(gè)字段揽祥,是一個(gè)Runnable對(duì)象讽膏, * 當(dāng)通過(guò)handler.post方法發(fā)送一個(gè)runnable的時(shí)候就會(huì)被封裝到這個(gè)msg中 */ handleCallback(msg); /* * 此方法是Handler中的一個(gè)靜態(tài)方法,方法體:message.callback.run(); * 只有這一句拄丰,可以看出是直接調(diào)用的run()方法府树,沒(méi)有新建線程, * 否則也不符合這里線程通信了料按。 */ } else { if (mCallback != null) { // mCallback就是上面接口的對(duì)象 if (mCallback.handleMessage(msg)) { /* * 如果返回true直接return結(jié)束方法奄侠, * 不再調(diào)用handler中的handleMessage方法。 */ return; } } handleMessage(msg);// handler中的消息處理方法 } }
三载矿、其他的相關(guān)問(wèn)題
-
一個(gè)線程幾個(gè) Looper遭铺,幾個(gè) Handler,Looper 如何確定是哪個(gè) Handler恢准?
一個(gè)線程只有一個(gè)Looper來(lái)處理MessageQueue中的消息;Handler可以有多個(gè)甫题,用來(lái)發(fā)送及處理消息馁筐。Looper不需要確定是那個(gè)Handler發(fā)送過(guò)來(lái)的消息,因?yàn)镸essage有個(gè)targ字段封裝了發(fā)送他的handler,系統(tǒng)直接通過(guò)message.getTarg().hanldMessage();就可以調(diào)用到handler的處理消息方法坠非。
-
兩個(gè)均不是主線的線程如何用Handler通信
在闡述這個(gè)問(wèn)題時(shí)難免又要回到Handler的機(jī)制說(shuō)明上敏沉;線程間的通信關(guān)鍵是Handler的創(chuàng)建,而Handler的創(chuàng)建本質(zhì)需要一個(gè)Looper對(duì)象的存在炎码。
Android默認(rèn)在主線程中初始化了一個(gè)Looper(詳見(jiàn)
android.app.ActivityThread->Looper.prepareMainLooper()
)盟迟,其他線程都需要自己手動(dòng)創(chuàng)建Looper;一個(gè)線程能夠使用Handler處理消息潦闲,是必須要有Looper和MessageQueue的攒菠。下面回到具體問(wèn)題,兩個(gè)都不是主線程的線程需要通過(guò)Handler通信改如何操作歉闰?根據(jù)上面的分析我們知道:
- 需要在處理邏輯的線程Thread1創(chuàng)建Looper辖众,有了Looper之后才能創(chuàng)建Handler;
- 之后在需要發(fā)消息的線程Thread2使用Thread1的handler對(duì)象發(fā)送Message和敬;
有了理論凹炸,之后擼一把代碼:
public class MainActivity extends ComponentActivity { private static final String TAG = "MainActivity"; private Looper looper2; private Thread1 thread1; private Thread thread2; private Handler child1Handler, child2Handler; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); initView(); } private void initView() { setContentView(R.layout.activity_main); thread1 = new Thread1("ChildThread1"); thread2 = new Thread2("ChildThread2"); thread1.start(); thread2.start(); } private void initHandler() { child1Handler = new Handler(thread1.getLooper()) { @Override public void handleMessage(@NonNull Message msg) { String ms = (String) msg.obj; Log.e(TAG, "當(dāng)前線程:" + Thread.currentThread().getName() + " 消息:" + ms); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } Message message = Message.obtain(); message.obj = "你好,我是" + Thread.currentThread().getName() + "!"; child2Handler.sendMessage(message); } }; child2Handler = new Handler(looper2) { @Override public void handleMessage(@NonNull Message msg) { String ms = (String) msg.obj; Log.w(TAG, "當(dāng)前線程:" + Thread.currentThread().getName() + " 消息:" + ms); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } Message message = Message.obtain(); message.obj = "你好,我是" + Thread.currentThread().getName() + "!"; child1Handler.sendMessage(message); } }; } class Thread1 extends HandlerThread { public Thread1(String name) { super(name); } } class Thread2 extends Thread { public Thread2(String name) { super(name); } @Override public void run() { Looper.prepare(); looper2 = Looper.myLooper(); initHandler(); Message message = Message.obtain(); message.obj = "你好,我是" + Thread.currentThread().getName() + "!"; child2Handler.sendMessage(message); Looper.loop(); } } }
四、Handler的內(nèi)存泄露
Handler是引發(fā)內(nèi)存泄露的大戶昼弟,其根源還是在于Handler機(jī)制的設(shè)計(jì)結(jié)構(gòu)上啤它;
-
原因:
Message
對(duì)象必須持有一個(gè)Handler
的引用才能順利分發(fā)任務(wù)。而如果Message
在MessageQueue
中不能及時(shí)被取出執(zhí)行,則會(huì)導(dǎo)致MessageQueue
長(zhǎng)時(shí)間的持有Message
對(duì)象变骡;一般情況下我們會(huì)在Activity中以內(nèi)部類(lèi)的形式創(chuàng)建Handler
离赫;這就導(dǎo)致了一系列的連鎖反應(yīng):Activity 被 Handler持有,Handler被Message持有锣光,Message被MessageQueue持有笆怠,MessageQueue中任務(wù)積壓,或者是本來(lái)就是個(gè)延時(shí)任務(wù)誊爹;這就導(dǎo)致了內(nèi)存溢出蹬刷。
-
解決辦法:
根據(jù)上述的原因我們只需要能在Handler和Activity之間斷開(kāi)聯(lián)系,既能保證內(nèi)存不再泄露频丘;
1)內(nèi)部類(lèi)是在類(lèi)對(duì)象創(chuàng)建后作為對(duì)象的一個(gè)成員存在的办成,而靜態(tài)內(nèi)部類(lèi)則是在類(lèi)class文件加載時(shí)就創(chuàng)建了,不再與外部類(lèi)對(duì)象關(guān)聯(lián)搂漠;因此我們可以將handler聲明為靜態(tài)內(nèi)部類(lèi)迂卢。
2)我們都知道Hander還有個(gè)內(nèi)部接口
Callback
和與之對(duì)應(yīng)的構(gòu)造函數(shù)Handler(Callback callback)
;/** * 實(shí)現(xiàn)回調(diào)弱引用的Handler * 防止由于內(nèi)部持有導(dǎo)致的內(nèi)存泄露 * 傳入的Callback不能使用匿名實(shí)現(xiàn)的變量桐汤,必須與使用這個(gè)Handle的對(duì)象的生命周期一致而克, * 否則會(huì)被立即釋放掉了 */ public class WeakRefHandler extends Handler { private WeakReference<Callback> mWeakReference; public WeakRefHandler(Callback callback) { mWeakReference = new WeakReference<Handler.Callback>(callback); } public WeakRefHandler(Callback callback, Looper looper) { super(looper); mWeakReference = new WeakReference<Handler.Callback>(callback); } @Override public void handleMessage(Message msg) { if (mWeakReference != null && mWeakReference.get() != null) { Callback callback = mWeakReference.get(); callback.handleMessage(msg); } } }
接下來(lái)就是使用,由于是弱引用怔毛,當(dāng)該類(lèi)需要被回收時(shí)员萍,就可以直接被回收掉:
private Handler.Callback mCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch(msg.what){ return true; } }; private Handler mHandler = new WeakRefHandler(mCallback);