Handler流程分析

Handler概念
Handler主要用于發(fā)送和處理消息和Runable對(duì)象稍途。處理異步操作
源碼解析
  • Handler的創(chuàng)建

Handler共有7個(gè)構(gòu)造函數(shù),如下:

1砚婆、無參構(gòu)造函數(shù)械拍,內(nèi)部調(diào)用【6】構(gòu)造函數(shù)

  public Handler() {
        this(null, false);
    }

2、參數(shù):只有Callback的構(gòu)造函數(shù),內(nèi)部調(diào)用【6】構(gòu)造函數(shù)

 public Handler(Callback callback) {
        this(callback, false);
    }

3坷虑、參數(shù):只有async的構(gòu)造函數(shù)甲馋,被系統(tǒng)隱藏,我們不能調(diào)用迄损。內(nèi)部調(diào)用【6】構(gòu)造函數(shù)

  public Handler(boolean async) {
        this(null, async);
    }

4定躏、參數(shù):只有一個(gè)Looper的構(gòu)造函數(shù),內(nèi)部調(diào)用

 public Handler(Looper looper) {
        this(looper, null, false);
    }

5芹敌、參數(shù):有Looper和Callback的構(gòu)造函數(shù)痊远,內(nèi)部調(diào)用

public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

6、參數(shù):Callback和async的構(gòu)造函數(shù)党窜,被系統(tǒng)隱藏拗引,我們不能調(diào)用

Callback:用于處理消息的接口。
async:是否是異步處理消息幌衣,默認(rèn)為false矾削,我創(chuàng)建Handler時(shí),系統(tǒng)為我們指定為false

執(zhí)行步驟:
1豁护、調(diào)用 Looper.myLooper()哼凯,獲取Looper。myLooper()方法實(shí)際使用ThreadLocal.get()方法楚里,獲取當(dāng)前線程指定的Looper断部。

2、從獲取的Looper中獲取消息隊(duì)列班缎,MessageQueue

3蝴光、初始化賦值 mCallback和 mAsynchronous
  public Handler(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 that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Callback接口源碼

public interface Callback {
        public boolean handleMessage(Message msg);
    }

7、參數(shù):Looper达址、Callback蔑祟、async 構(gòu)造函數(shù)。
主要就是初始化賦值

public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
  • ThreadLocal
  1. 簡(jiǎn)介

ThreadLocal只有用于線程內(nèi)的數(shù)據(jù)存儲(chǔ)沉唠,通過他可以在指定的線程內(nèi)存儲(chǔ)數(shù)據(jù)疆虚,數(shù)據(jù)存儲(chǔ)以后,其他線程無法獲取到該數(shù)據(jù)满葛,只有該線程可以獲取到存儲(chǔ)的數(shù)據(jù)

在系統(tǒng)內(nèi)径簿,雖然使用的是同一個(gè)ThreadLocal對(duì)象,但是通過TheadLocal獲取的數(shù)據(jù)不一樣嘀韧,例如Looper

一般線程篇亭,某些數(shù)據(jù)是以線程為作用于,并且不同線程用于不同的數(shù)據(jù)副本的時(shí)候锄贷,就可以考慮使用ThreadLocal

2.ThreadLocal的get()方法

執(zhí)行流程

1暗赶、獲取當(dāng)前線程
2鄙币、獲取當(dāng)前線程的TheadLocalMap
3、如果TheadLocalMap不為空蹂随,則以ThreaLocal當(dāng)前實(shí)例作為Key去取值十嘿,然后返回
4、如果ThreaLocalMap為空岳锁,或者取值為空绩衷,創(chuàng)建ThreadLocal或者設(shè)置默認(rèn)值
public T get() {
    //1. 獲取當(dāng)前的線程
    Thread t = Thread.currentThread();
    //2. 以當(dāng)前線程為參數(shù),獲取一個(gè) ThreadLocalMap 對(duì)象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //3. map 不為空激率,則以當(dāng)前 ThreadLocal 對(duì)象實(shí)例作為key值咳燕,去map中取值,有找到直接返回
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    //4. map 為空或者在map中取不到值乒躺,那么走這里招盲,返回默認(rèn)初始值
    return setInitialValue();
}
  • Looper初始化/Looper.perpare()
1、實(shí)例化MessageQueue
2嘉冒、獲取當(dāng)前線程
 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
1曹货、Handler初始化獲取,當(dāng)在主線程定義Handler時(shí)讳推,通過Looper.myLooper()獲取主線程的Looper顶籽。
2、在子線程內(nèi)調(diào)用Looper.prepare()方法初始化银觅,
Looper.prepare()實(shí)際是new一個(gè)Looper之后通過ThreadLoca的set方法礼饱,以ThreadLoca當(dāng)前實(shí)例為key放入到線程內(nèi)的ThreadLocalMap中
如果Looper已經(jīng)被初始化,則拋出異常
RuntimeException("Only one Looper may be created per thread")

總結(jié):

1究驴、所以在主線程創(chuàng)建Handler時(shí)镊绪,我們不需要實(shí)例Looper,因?yàn)樵贏ctivityThread主線程中已經(jīng)初始化了Looper洒忧,而之后我們?cè)谥骶€程使用的Looper都是同一個(gè)Looper

2蝴韭、在子線程實(shí)例Handler時(shí),需要我們?cè)谧泳€程內(nèi)調(diào)用Looper的prepare()方法實(shí)例化Looper

  • Handler發(fā)送消息
實(shí)際發(fā)送一個(gè)Message主要是調(diào)用MessageQueue的enqueueMessage方法將消息放入隊(duì)列
 //調(diào)用sendMessageDelayed(Message msg, long delayMillis)
 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
 // 調(diào)用sendMessageAtTime(Message msg, long uptimeMillis)  
 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    //調(diào)用enqueueMessage(queue, msg, uptimeMillis)
public boolean sendMessageAtTime(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);
    }
    //調(diào)用MessageQueue的enqueueMessage(msg, uptimeMillis);將消息放入隊(duì)列
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //設(shè)置target為當(dāng)前Handler實(shí)例
        msg.target = this;
        //如果是異步處理,則設(shè)置Message為同步跑慕,這個(gè) mAsynchronous 標(biāo)識(shí)是我們構(gòu)造 Handler 的時(shí)候傳遞的參數(shù),默認(rèn)為  false
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        最后就是真正的進(jìn)隊(duì)方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }    

MessageQueue將消息放入隊(duì)列

消息隊(duì)列是使用鏈表作為數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)摧找。是可以插隊(duì)的核行,即不存在發(fā)送了延時(shí)消息不會(huì)阻塞消息隊(duì)列。
MessageQueue中存儲(chǔ)一個(gè)頭結(jié)點(diǎn)的Message 蹬耘,而每個(gè)Message中都存儲(chǔ)一個(gè)Message芝雪,這樣根據(jù)消息的執(zhí)行順序設(shè)置Message的next形成了消息隊(duì)列

  boolean enqueueMessage(Message msg, long when) {
        //如果target為空則拋出異常。target實(shí)際是發(fā)送消息的Handler實(shí)例综苔,之后被用來處理消息
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        //同步鎖
        synchronized (this) {
            //如果MessageQueue退出惩系,則釋放消息
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
            
            msg.markInUse();
            ///賦值調(diào)用時(shí)間
            msg.when = when;
            //獲取頭結(jié)點(diǎn)
            Message p = mMessages;
            boolean needWake;
            //  //隊(duì)列中沒有消息 或者 時(shí)間為0 或者 比頭結(jié)點(diǎn)的時(shí)間早
            //插入到頭結(jié)點(diǎn)中
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                /類似插入排序位岔,找到合適的位置,找到一個(gè)Messgae的next為空或者next的執(zhí)行實(shí)際大于當(dāng)前message的Message
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // 將上一個(gè)Message的next設(shè)置個(gè)當(dāng)前Message。將上一個(gè)Message的next設(shè)置為當(dāng)前Message
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

         
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
  • Looper循環(huán)消息
1堡牡、loop首先判斷Looper是否為空抒抬,如果為空則拋出異常   RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 
這就是我們創(chuàng)建非主線程的 Handler為什么要調(diào)用Looper.prepare()的原因。
而主線程中會(huì)在ActivityThread.main() 方法里面調(diào)用了 prepare 方法晤柄,
所以我們創(chuàng)建默認(rèn)(主線程)的 Handler 不需要額外創(chuàng)建 Looper 

2擦剑、loop內(nèi)是一個(gè)死循環(huán),取出消息如果為空則退出循環(huán)芥颈,如果不為空則執(zhí)行msg的響應(yīng)的Handler的dispatchMessage(msg)處理消息惠勒。for循環(huán)內(nèi)通過queue.next()取出消息。
// Looper.java 爬坑,省略部分代碼
loop(){
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // might block  , 從隊(duì)列取出一個(gè)msg
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        msg.target.dispatchMessage(msg); //Handler處理消息
        msg.recycleUnchecked();  //回收msg
    }
}
MessageQueue的next()如下
//MessageQueue.java ,刪減部分代碼
Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        //如果隊(duì)列已經(jīng)停止了(quit or dispose)
        return null;
    }
    for (;;) {
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();   //獲取當(dāng)前時(shí)間
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                 //msg == target 的情況只能是屏障消息纠屋,即調(diào)用postSyncBarrier()方法
                //如果存在屏障,停止同步消息盾计,異步消息還可以執(zhí)行
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());  //找出異步消息售担,如果有的話
            }
            if (msg != null) {
                if (now < msg.when) {
                    //當(dāng)前消息還沒準(zhǔn)備好(時(shí)間沒到)
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 消息已準(zhǔn)備,可以取出
                    if (prevMsg != null) {
                        //有屏障闯估,prevMsg 為異步消息 msg 的前一節(jié)點(diǎn)灼舍,相當(dāng)于拿出 msg ,鏈接前后節(jié)點(diǎn)
                        prevMsg.next = msg.next;
                    } else {
                        //沒有屏障,msg 即頭節(jié)點(diǎn),將 mMessages 設(shè)為新的頭結(jié)點(diǎn)
                        mMessages = msg.next;
                    }
                    msg.next = null;  //斷開即將執(zhí)行的 msg
                    msg.markInUse(); //標(biāo)記為使用狀態(tài)
                    return msg;  //返回取出的消息涨薪,交給Looper處理
                }
            } 
            // Process the quit message now that all pending messages have been ha
            if (mQuitting) {
                //隊(duì)列已經(jīng)退出
                dispose();
                return null;  //返回null后Looper.loop()方法也會(huì)結(jié)束循環(huán)
            }
    }
}

異步消息并不會(huì)立刻執(zhí)行骑素,而是根據(jù)時(shí)間,完全跟同步消息一樣的順序插入隊(duì)列中刚夺。異步消息與同步消息唯一的區(qū)別就是當(dāng)有消息屏障時(shí)献丑,異步消息還可以執(zhí)行,而同步消息則不行

總結(jié):發(fā)送消息的所在的線程侠姑,不處理消息创橄,只有Looper將消息取出后處理,所以在無論Handler發(fā)送消息所在在哪個(gè)線程莽红,最終處理消息是在Looper.loop()所在的線程

  • Handler處理消息
Looper.loop()取出消息后妥畏,調(diào)用msg.target.dispatchMessage(msg)方法處理消息,msg.target就是發(fā)送消息的Handler

消息處理:
1安吁、消息的Runable不為空時(shí)醉蚁,使用Runable處理消息
2、Handler的CallBack不為空時(shí)鬼店,使用CallBack處理消息
3网棍、Handler的handleMessage()處理消息
public void dispatchMessage(Message msg) {
        //如果消息的CallBack不為空,則調(diào)用 handleCallback(msg);.Callback就是Runable
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            // 如果Handler的CallBack不為空妇智,則調(diào)用Callback處理下消息滥玷,如果mCallback.handleMessage(msg)返回true氏身,則處理結(jié)束,否則惑畴,將繼續(xù)執(zhí)行Handler的handleMessage(msg);
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //最后上面情況都不滿足時(shí)蛋欣,調(diào)用Handler的handleMessage(msg)處理消息
            handleMessage(msg);
        }
    }
    
    
private static void handleCallback(Message message) {
        message.callback.run();
}

總結(jié):

如果 msg 沒有 callback 的話,那么將會(huì)判斷 mCallback 是否為空桨菜,
這個(gè) mCallback 就是構(gòu)造方法種傳遞的那個(gè) Callback ,如果 
mCallback為空,那么就調(diào)用 Handler 的 handleMessage(msg) 
方法豁状,否則就調(diào)用 mCallback.handleMessage(msg) 方法,然后根據(jù) 
mCallback.handleMessage(msg)的返回值判斷是否攔截消息倒得,如果攔截(返回 
true)泻红,則結(jié)束,否則還會(huì)調(diào)用 Handler#handleMessage(msg)方法霞掺。
也就是說:Callback.handleMessage() 
的優(yōu)先級(jí)比Handler.handleMessage()要高 
谊路。如果存在Callback,并且Callback#handleMessage() 返回了 true 
,那么Handler#handleMessage()將不會(huì)調(diào)用。
  • Handler.post(Runnable)

無論消息發(fā)送Runable還是msg菩彬,最終消息的載體都是Message缠劝,Runable只是用來處理消息,而最終使用Runable的處理消息使用handleCallback(Message message)骗灶,而該方法內(nèi)部調(diào)用Runable的run方法惨恭,并沒有新建線程

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    //創(chuàng)建消息,將Runable放入現(xiàn)在Message內(nèi)耙旦,用來處理消息
 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
  • 消息創(chuàng)建
消息的創(chuàng)建分為:
New消息
Message.obtain()
Handler.obtainMessage() 
而Handler.obtainMessage() 最終也是調(diào)用的 Message.obtain(Handler)脱羡,所傳參數(shù)會(huì)將Handler本身賦值給Message的Target用來處理消息
接下來分析Message.obtain()
//Message.java

private static Message sPool;

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}
obtain() 方法會(huì)在以 sPool作為頭結(jié)點(diǎn)的消息池(鏈表)中遍歷,如果找到,那么取出來免都,并置為非使用狀態(tài)锉罐,然后返回,如果消息池為空绕娘,則新建一個(gè)消息
  • 消息池的獲取

這是一個(gè)回收的方法脓规,方法體內(nèi)將 Message 對(duì)象的各種參數(shù)清空,如果消息池的數(shù)量小于最大數(shù)量(50)的話险领,就當(dāng)前消息插入緩存池的頭結(jié)點(diǎn)中

//Message.java

private static final int MAX_POOL_SIZE = 50;

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

上方法的其中一個(gè)調(diào)用在 Looper.looperzhong 使用侨舆,調(diào)用時(shí)機(jī)是在 Handler 處理事件之后,既然是 Handler 處理后就會(huì)回收:源碼如下

// Looper.java 绢陌,省略部分代碼
loop(){
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // might block  , 從隊(duì)列取出一個(gè)msg
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        msg.target.dispatchMessage(msg); //Handler處理消息
        ...
        msg.recycleUnchecked();  //回收msg
    }
}
總結(jié):
    所以在消息處理方中:
    Handler的Callback接口中
    Runable的run方法中
    Handler的handlerMessage
    消息處理完成后系統(tǒng)就會(huì)回收消息挨下,所以不要在消息處理方法中異步的處理消息,以為在loop方法中執(zhí)行了處理方法后下面,就執(zhí)行回收复颈。如果真要在異步中使用绩聘,那么可以創(chuàng)建一個(gè)新的 Message 對(duì)象沥割,并將值賦值過去

總而言之耗啦,因?yàn)?Handler 機(jī)制在整個(gè) Android 系統(tǒng)中使用太頻繁,所以 Android 就采用了一個(gè)緩存策略机杜。就是 Message 里面會(huì)緩存一個(gè)靜態(tài)的消息池帜讲,當(dāng)消息被處理或者移除的時(shí)候就會(huì)被回收到消息池,所以推薦使用 Message.obtain()來獲取消息對(duì)象椒拗。

  • 總結(jié)流程
    image

把整個(gè)Handler機(jī)制比作一個(gè)流水線的話似将,那么 Handler 就是工人,可以在不同線程傳遞 Message到傳送帶(MessageQueue)蚀苛,而傳送帶是被馬達(dá)(Looper)運(yùn)輸?shù)脑谘椋R達(dá)又是一開始就運(yùn)行了(Looper.loop()),并且只會(huì)在一開始的線程堵未,所以無論哪個(gè)工人(Handler)在哪里(任意線程)傳遞產(chǎn)品(Message)腋舌,都只會(huì)在一條傳送帶(MessageQueue)上被唯一的馬達(dá)(Looper)運(yùn)送到終點(diǎn)處理,即 Message 只會(huì)在調(diào)用 Looper.loop() 的線程被處理渗蟹。


問題總結(jié)
  • Handler為什么能造成內(nèi)存泄漏
Handler本身并不會(huì)造成內(nèi)存泄漏块饺,而造成內(nèi)存泄漏的主要原因是匿名內(nèi)部類的引用,匿名內(nèi)部類的隱形的持有外部類的引用    (如果不持有引用怎么可以使用外部類的變量方法呢雌芽?)
所以當(dāng)匿名內(nèi)部類的生命周期較長(zhǎng)授艰,例如正在跑一個(gè)耗時(shí)的線程,而碰巧Activity不外類此時(shí)退出世落,需要回收淮腾,而內(nèi)部類讓然引用外部類,導(dǎo)致內(nèi)存不能回收岛心。

Handler發(fā)生內(nèi)存泄漏的原因分析
Handler持有Activity来破,Message持有Handler、Message在MessageQueue中忘古、
MessageQueue在Looper中徘禁、而ThreadLoca靜態(tài)持有Looper。因?yàn)?sThreadLocal 是方法區(qū)常量髓堪,所以不會(huì)被回收送朱,即 sThreadLocal 間接的持有了 Activity 的引用,當(dāng) Handler 發(fā)送的消息還沒有被處理完畢時(shí)干旁,比如延時(shí)消息驶沼,而 Activity 又被用戶返回了,即 onDestroy() 后争群,系統(tǒng)想要對(duì) Activity 對(duì)象進(jìn)行回收回怜,但是發(fā)現(xiàn)還有引用鏈存在,回收不了换薄,就造成了內(nèi)存泄漏
image
怎么防止 Handler 內(nèi)存泄漏
1玉雾、把ThreadLocal與Activity的引用鏈斷開

    最簡(jiǎn)單的方法就是在 onPause()中使用 Handler 的 removeCallbacksAndMessages(null)方法清除所有消息及回調(diào)翔试。就可以把引用鏈斷開了。
    Android 源碼中這種方式也很常見复旬,不在 onDestroy()里面調(diào)用主要是 onDestroy() 方法不能保證每次都能執(zhí)行到

2垦缅、第二種方法就是使用靜態(tài)類加弱引用的方式
    因?yàn)殪o態(tài)類不會(huì)持有外部類的引用,所以需要傳一個(gè) Activity 過來,并且使用一個(gè)弱引用來引用 Activity 的實(shí)例驹碍,
    弱引用在 gc的時(shí)候會(huì)被回收壁涎,所以也就相當(dāng)于把強(qiáng)引用鏈給斷了,自然也就沒有內(nèi)存泄漏了志秃。
Loop.loop() 為什么不會(huì)造成應(yīng)用卡死怔球?
    loop() 方法是一個(gè)死循環(huán),那么肯定會(huì)占用大量的 cpu 而導(dǎo)致應(yīng)用卡頓浮还,甚至說 ANR 庞溜。
    但是 Android 中即使使用大量的 Looper,也不會(huì)造成這種問題碑定,問什么呢流码?
    由于這個(gè)問題涉及到的知識(shí)比較深,主要是通過 Linux 的 epoll 機(jī)制實(shí)現(xiàn)的延刘,這里需要 Linux 漫试、 jni 等知識(shí),我等菜鳥就不分析了碘赖,推薦一些相關(guān)文章:

Android中為什么主線程不會(huì)因?yàn)長(zhǎng)ooper.loop()里的死循環(huán)卡死驾荣?
https://www.zhihu.com/question/34652589

總結(jié)
1. Handler 的回調(diào)方法是在 Looper.loop()所調(diào)用的線程進(jìn)行的;
2. Handler 的創(chuàng)建需要先調(diào)用 Looper.prepare() 普泡,然后再手動(dòng)調(diào)用 loop()方法開啟循環(huán)播掷;
3. App 啟動(dòng)時(shí)會(huì)在ActivityThread.main()方法中創(chuàng)建主線程的 Looper ,并開啟循環(huán),所以主線程使用 Handler 不用調(diào)用第2點(diǎn)的邏輯撼班;
4. 延時(shí)消息并不會(huì)阻塞消息隊(duì)列歧匈;
5. 異步消息不會(huì)馬上執(zhí)行,插入隊(duì)列的方式跟同步消息一樣砰嘁,唯一的區(qū)別是當(dāng)有消息屏障時(shí)件炉,異步消息可以繼續(xù)執(zhí)行,同步消息則不行矮湘;
6. Callback.handleMessage() 的優(yōu)先級(jí)比 Handler.handleMessage()要高*
7. Handler.post(Runnable)傳遞的 Runnale 對(duì)象并不會(huì)在新的線程執(zhí)行斟冕;
8. Message 的創(chuàng)建推薦使用 Message.obtain() 來獲取,內(nèi)部采用緩存消息池實(shí)現(xiàn)缅阳;
9. 不要在 handleMessage()中對(duì)消息進(jìn)行異步處理磕蛇;
10. 可以通過removeCallbacksAndMessages(null)或者靜態(tài)類加弱引用的方式防止內(nèi)存泄漏;
11. Looper.loop()不會(huì)造成應(yīng)用卡死,里面使用了 Linux 的 epoll 機(jī)制秀撇。

參考資料

Handler全家桶之 —— Handler 源碼解析

深入理解 ThreadLocal (這些細(xì)節(jié)不應(yīng)忽略)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末伏伯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子捌袜,更是在濱河造成了極大的恐慌,老刑警劉巖炸枣,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虏等,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡适肠,警方通過查閱死者的電腦和手機(jī)霍衫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侯养,“玉大人敦跌,你說我怎么就攤上這事」淇” “怎么了柠傍?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辩稽。 經(jīng)常有香客問我惧笛,道長(zhǎng),這世上最難降的妖魔是什么逞泄? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任患整,我火速辦了婚禮,結(jié)果婚禮上喷众,老公的妹妹穿的比我還像新娘各谚。我一直安慰自己,他們只是感情好到千,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布昌渤。 她就那樣靜靜地躺著挟鸠,像睡著了一般娃弓。 火紅的嫁衣襯著肌膚如雪爽丹。 梳的紋絲不亂的頭發(fā)上奴潘,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天淆九,我揣著相機(jī)與錄音冬耿,去河邊找鬼蚀腿。 笑死宣羊,一個(gè)胖子當(dāng)著我的面吹牛斟览,可吹牛的內(nèi)容都是我干的毁腿。 我是一名探鬼主播,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼已烤!你這毒婦竟也來了鸠窗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤胯究,失蹤者是張志新(化名)和其女友劉穎稍计,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裕循,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡臣嚣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了剥哑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硅则。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖株婴,靈堂內(nèi)的尸體忽然破棺而出怎虫,到底是詐尸還是另有隱情,我是刑警寧澤困介,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布大审,位于F島的核電站,受9級(jí)特大地震影響座哩,放射性物質(zhì)發(fā)生泄漏饥努。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一八回、第九天 我趴在偏房一處隱蔽的房頂上張望酷愧。 院中可真熱鬧,春花似錦缠诅、人聲如沸溶浴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽士败。三九已至,卻和暖如春褥伴,著一層夾襖步出監(jiān)牢的瞬間谅将,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來泰國打工重慢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留饥臂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓似踱,卻偏偏與公主長(zhǎng)得像隅熙,于是被迫代替她去往敵國和親稽煤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

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