這節(jié)介紹Handler類使用相關(guān)的知識(shí)(以下分析都是基于android 12代碼)
1. Handler的使用
1.1 創(chuàng)建Handler實(shí)例
創(chuàng)建Handler實(shí)例直接調(diào)用相應(yīng)的構(gòu)造函數(shù)即可,如下:
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback) {
this(callback, false);
}
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
mCallback = callback;
mAsynchronous = async;
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler類提供了各種重載的構(gòu)造方法质礼,現(xiàn)介紹下它的幾個(gè)屬性:
- mLooper:如果構(gòu)造方法中傳遞的looper為null,則直接調(diào)用Looper.myLooper方法獲取線程對(duì)應(yīng)的Looper,不存在則拋異常
- mQueue:l類型MessageQueue
- mCallback:類型為Callback,設(shè)置了這個(gè)屬性爬范,在dispatchMessage方法中會(huì)先執(zhí)行它的回調(diào)
- mAsynchronous:若它的值為true,代表通過Handler創(chuàng)建的Message都是異步的
1.2 創(chuàng)建Message的方法
Handler提供了創(chuàng)建Message的方法,如下:
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
上面只貼了一部分役耕,主要是為了更方便的創(chuàng)建Message,Handler提供了這些重載方法聪廉。
1.3 執(zhí)行Runnable
Handler提供了執(zhí)行Runnable的方法瞬痘,如下:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
如上面,Handler可以直接post一個(gè)Runnable板熊,還可以delay一段時(shí)間后執(zhí)行Runnable框全,還可以在某個(gè)時(shí)間點(diǎn)執(zhí)行Runnable
1.4 延遲或者在某個(gè)時(shí)間點(diǎn)執(zhí)行Message
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
上面介紹了Handler提供的功能,但是如果在使用的時(shí)候干签,如果使用不當(dāng)有可能會(huì)出現(xiàn)內(nèi)存泄漏的問題津辩,那下面就來介紹下相關(guān)的內(nèi)容
2. Handler使用不當(dāng)與內(nèi)存泄漏
內(nèi)存泄漏
一個(gè)對(duì)象本應(yīng)該在它“生命結(jié)束”后,被垃圾回收器回收掉容劳。但是由于一個(gè)生命周期更長(zhǎng)的對(duì)象引用了它喘沿,導(dǎo)致垃圾回收器無法完成回收。它作為一個(gè)“毫無用處”的對(duì)象鸭蛙,占用著“無辜”的內(nèi)存摹恨。
造成內(nèi)存泄漏原因分析
下面是一段Handler使用不當(dāng),有可能會(huì)造成內(nèi)存泄漏的代碼
public class MyActivity extends Activity{
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 1 ){
}
}
}
protected void onResume() {
super.onResume();
handler.postDelayed(new Runnable(){
省略業(yè)務(wù)代碼
},10);
}
}
上面的代碼在Activity的onResume方法中娶视,在10s后去執(zhí)行一個(gè)Runnable晒哄,這段代碼是比較容易出現(xiàn)內(nèi)存泄漏的,但是并不是說一定會(huì)出現(xiàn)肪获。
在這樣的場(chǎng)景下會(huì)出現(xiàn):Activity的onResume方法被執(zhí)行后寝凌,這時(shí)候用戶剛好要退出Activity,那這種場(chǎng)景下必出現(xiàn)內(nèi)存泄漏孝赫。
來分析下為啥上面的場(chǎng)景會(huì)出現(xiàn)內(nèi)存泄漏:
- Handler handler的實(shí)例是一個(gè)匿名內(nèi)部類较木,匿名內(nèi)部類編譯器會(huì)為它增加一個(gè)Activity類型的屬性,同時(shí)它的構(gòu)造函數(shù)增加一個(gè)參數(shù)青柄,這個(gè)參數(shù)指向了當(dāng)前的Activity對(duì)象伐债,這樣handler就持有了當(dāng)前Activity對(duì)象。
- new Runnable的時(shí)候也是一個(gè)匿名內(nèi)部類致开,它同樣也持有當(dāng)前的Activity對(duì)象
- 在調(diào)用handler.postDelayed這個(gè)方法的時(shí)候峰锁,最終會(huì)創(chuàng)建一個(gè)Message對(duì)象,這個(gè)Message的callback屬性指向了上面創(chuàng)建的Runnable双戳,同時(shí)它的target屬性指向了handler虹蒋。創(chuàng)建的Message對(duì)象會(huì)被放入MessageQueue的鏈表中,在10s后才能執(zhí)行。
- 因?yàn)楫?dāng)前Activity被用戶銷毀了變成“無用”對(duì)象了魄衅,但是MessageQueue的鏈表卻還間接的引用著當(dāng)前的Activity峭竣,MessageQueue的生命周期與app進(jìn)程是一致的,最終導(dǎo)致當(dāng)前的Activity對(duì)象不能被即時(shí)回收
解決內(nèi)存泄漏
如下代碼:
public class MyActivity extends Activity{
private Handler handler = new MyHandler(this);
private static class MyHandler extends Handler{
private WeakReference<Activity> activity;
MyHandler(Activity activity) {
this.activity = new WeakReference<>(activity);
}
}
private Runnable runnable = new Runnable(){
省略業(yè)務(wù)代碼
};
protected void onResume() {
super.onResume();
handler.postDelayed(runnable,10);
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(runnable);
}
}
如上代碼可以解決Handler使用不當(dāng)晃虫,導(dǎo)致有可能出現(xiàn)內(nèi)存泄漏問題:
- 把Handler類的子類定義為靜態(tài)內(nèi)部類皆撩,使用弱引用來引用Activity對(duì)象
- 在onDestory的方法中,調(diào)用handler的removeCallbacks方法傲茄,把runnable從MessageQueue的鏈表中移除掉
總結(jié)
到此關(guān)于Handler的介紹就結(jié)束了毅访,關(guān)于handler機(jī)制系列的文章也介紹完了,歡迎關(guān)注其他系列文章盘榨。