handler機(jī)制--Handler使用

這節(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)注其他系列文章盘榨。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蟆融,隨后出現(xiàn)的幾起案子草巡,更是在濱河造成了極大的恐慌,老刑警劉巖型酥,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件山憨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡弥喉,警方通過查閱死者的電腦和手機(jī)郁竟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來由境,“玉大人棚亩,你說我怎么就攤上這事÷步埽” “怎么了讥蟆?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)纺阔。 經(jīng)常有香客問我瘸彤,道長(zhǎng),這世上最難降的妖魔是什么笛钝? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任质况,我火速辦了婚禮,結(jié)果婚禮上玻靡,老公的妹妹穿的比我還像新娘结榄。我一直安慰自己,他們只是感情好啃奴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布潭陪。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪依溯。 梳的紋絲不亂的頭發(fā)上老厌,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音黎炉,去河邊找鬼枝秤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛慷嗜,可吹牛的內(nèi)容都是我干的淀弹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼庆械,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼薇溃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缭乘,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤沐序,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后堕绩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體策幼,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年奴紧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了特姐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡黍氮,死狀恐怖唐含,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滤钱,我是刑警寧澤觉壶,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站件缸,受9級(jí)特大地震影響铜靶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜他炊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一争剿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痊末,春花似錦蚕苇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嚼吞。三九已至,卻和暖如春蹬碧,著一層夾襖步出監(jiān)牢的瞬間舱禽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工恩沽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留誊稚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓罗心,卻偏偏與公主長(zhǎng)得像里伯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子渤闷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容