Handler源碼分析

Handler 已經(jīng)被無數(shù)人分析過了 為什么這里還要寫篇博文分析呢侍匙,因?yàn)槟鞘莿e人的涣狗,自己分析的才是你自己的

handler作用:(下面這段話摘自谷歌文檔)

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own. 大概翻譯一下: Handler有2個(gè)主要的作用:
(1)安排消息或Runnable在某個(gè)主線程中某個(gè)地方執(zhí)行
(2)安排一個(gè)動(dòng)作在不同的線程中執(zhí)行

handler寫法

子線程更新UI數(shù)據(jù),需要使用如下代碼發(fā)送消息給主線程處理:

//子線程中發(fā)送消息操作
Message message = handler.obtainMessage();
message.obj = "子線程更新的內(nèi)容!";
handler.sendMessage(message);
最后會(huì)在主線程的Handler對(duì)象中執(zhí)行handleMessage()方法:

Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Object msgStr = msg.obj;
if (msgStr != null) {
tv_content.setText((String) msgStr);
}
}
};

源碼分析

為什么調(diào)用Handler就能在主線程更新UI呢啡浊?既然是源碼分析就一步步的點(diǎn)進(jìn)去看 首先看這一行代碼做了什么

Message message = handler.obtainMessage();

調(diào)用了Message的obtain()方法

public static Message obtain(Handler h) {
//調(diào)用了Message類中的靜態(tài)方法obtain();
Message m = obtain();
//把上面?zhèn)鬟f進(jìn)來的handler對(duì)象賦值給了m對(duì)象中的target成員變量
m.target = h;
//返回了Message m對(duì)象
return m;
}

繼續(xù)看obtain()方法:

public static Message obtain() {
//同步代碼塊中的內(nèi)容先跳過,第一次發(fā)送消息時(shí)sPool為null
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
//返回了一個(gè)Message對(duì)象
return new Message();

總結(jié):Message message = handler.obtainMessage();調(diào)用該方法時(shí)最后返回了一個(gè)new出來的Message()對(duì)象。

接著看handler.sendMessage(message);

public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
最終會(huì)調(diào)用

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);
}

繼續(xù)往下看enqueueMessage(queue, msg, uptimeMillis);

boolean enqueueMessage(Message msg, long when) {
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) {

//根據(jù)if里打印的異常可以判斷出這個(gè)mQuitting的意義是當(dāng)在死亡線程中發(fā)送message時(shí)就會(huì)失敗
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();//標(biāo)記該消息正在使用
    msg.when = when;
    Message p = mMessages;//將MessageQueue中的mMessage對(duì)象賦值給一個(gè)新變量p
    boolean needWake;
 //第一次進(jìn)入的時(shí)候,mMessage是為null的,也就是說說p是null
    if (p == null || when == 0 || when < p.when) {
        // New head, wake up the event queue if blocked.
        msg.next = p;//把p賦值給了當(dāng)前msg的下一個(gè)節(jié)點(diǎn),看到這里就知道這里使用了鏈表
        mMessages = msg;
        needWake = mBlocked;
    } else {
        // Inserted within the middle of the queue.  Usually we don't have to wake
        // up the event queue unless there is a barrier at the head of the queue
        // and the message is the earliest asynchronous message in the queue.
        needWake = mBlocked && p.target == null && msg.isAsynchronous();
        Message prev;
        for (;;) {
            prev = p;
            p = p.next;
            if (p == null || when < p.when) {
                break;
            }
            if (needWake && p.isAsynchronous()) {
                needWake = false;
            }
        }
        msg.next = p; // invariant: p == prev.next
        prev.next = msg;
    }

    // We can assume mPtr != 0 because mQuitting is false.
    if (needWake) {
        nativeWake(mPtr);
    }
}
return true;

}

從這里可以看出把消息放到了隊(duì)列中,那么只是放到隊(duì)列中而已,再看看Handler構(gòu)造方法

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());
}
}

//從ThreadLocal取出與handler綁定的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//賦值Looper的MessageQueue給Handler
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

依舊沒有看到handleMessage這個(gè)方法的調(diào)用劳景,研究了半天,原來在ActivityThread這個(gè)類中, Android應(yīng)用啟動(dòng)的入口都是從ActivityThread這個(gè)類的main()方法開始的

public static final void main(String[] args) {
SamplingProfilerIntegration.start();

    Process.setArgV0("");

//看到了Looper 劃重點(diǎn)
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

//看到了Looper 劃重點(diǎn)
Looper.loop();

    if (Process.supportsProcesses()) {
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

    thread.detach();
    String name = (thread.mInitialApplication != null)
        ? thread.mInitialApplication.getPackageName()
        : "";
    Slog.i(TAG, "Main thread of " + name + " is now exiting");
}

先分析下Looper.prepareMainLooper();

public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

調(diào)用了prepare(false);以及 sMainLooper = myLooper();先看prepare()方法

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//使用ThreadLocal存儲(chǔ)了一個(gè)新創(chuàng)建的Looper,而且該looper對(duì)象不允許退出
sThreadLocal.set(new Looper(quitAllowed));
}

Looper構(gòu)造函數(shù)
private Looper(boolean quitAllowed) {
//創(chuàng)建一個(gè)MessageQueue對(duì)象并賦值給了自己的成員變量mQueue
mQueue = new MessageQueue(quitAllowed);
//把當(dāng)前線程賦值給了成員變量mThread
mThread = Thread.currentThread();
}
再看看myLooper()

只是把剛剛存到ThreadLocal的Looper對(duì)象取了出來而已!

public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
到此,Looper.prepareMainLooper();方法已經(jīng)全部執(zhí)行完畢了!總結(jié)一下看他做了什么: 1.使用ThreadLocal存儲(chǔ)了一個(gè)新創(chuàng)建的Looper,而且該looper對(duì)象不允許退出 2.創(chuàng)建一個(gè)MessageQueue對(duì)象并賦值給了自己的成員變量mQueue碉就,把當(dāng)前線程賦值給了成員變量mThread 3.取出Looper

還有Looper.loop();方法

public static void loop() {
//執(zhí)行該方法就是獲取了上面創(chuàng)建的Looper對(duì)象
final Looper me = myLooper();
//只要執(zhí)行了上面的prepare方法,這里就不會(huì)取出null
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//拿到了上面創(chuàng)建的MessageQueue對(duì)象
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

//死循環(huán)
for (;;) {
    //執(zhí)行該方法時(shí)會(huì)時(shí)線程進(jìn)入阻塞狀態(tài),一直等待消息的到來
    //恰巧就和上面我們發(fā)送消息的方法對(duì)接上了!
    //我們剛剛就把消息放到隊(duì)列中了,而這里就是在等待隊(duì)列中一旦出現(xiàn)消息馬上就取出來繼續(xù)下面的操作
    Message msg = queue.next(); // might block
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return;
    }

    // This must be in a local variable, in case a UI event sets the logger
    Printer logging = me.mLogging;
    if (logging != null) {
        logging.println(">>>>> Dispatching to " + msg.target + " " +
                msg.callback + ": " + msg.what);
    }

  
        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {

//在這里 msg消息對(duì)應(yīng)的target調(diào)用了dispatchMessage(msg)方法!剛才我們看到的target就是對(duì)應(yīng)的handler對(duì)象,也就是說調(diào)用了每條消息對(duì)應(yīng)的handler對(duì)象的dispatchMessage(msg)方法
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

    if (logging != null) {
        logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
    }

    // Make sure that during the course of dispatching the
    // identity of the thread wasn't corrupted.
    final long newIdent = Binder.clearCallingIdentity();
    if (ident != newIdent) {
        Log.wtf(TAG, "Thread identity changed from 0x"
                + Long.toHexString(ident) + " to 0x"
                + Long.toHexString(newIdent) + " while dispatching to "
                + msg.target.getClass().getName() + " "
                + msg.callback + " what=" + msg.what);
    }

    msg.recycleUnchecked();
}

}

注意盟广,當(dāng)線程loop起來是時(shí),線程就一直在循環(huán)中瓮钥。就是說Looper.loop()后面的代碼就不能被執(zhí)行了筋量。想要執(zhí)行烹吵,需要先退出loop,這也就是為什么Looper.loop();放在最后后面的原因

接下來就要看msg.target.dispatchMessage(msg);

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看出這里進(jìn)行消息的處理

總結(jié)一下(采用面試問答形式)

問題一:Handler原理

1.最初在ActivityThread中,系統(tǒng)調(diào)用了一下Looper.prepareMainLooper();和Looper.loop()方法,所以在主線程中默認(rèn)就會(huì)有一個(gè)Looper對(duì)象和與其綁定的MessageQueue對(duì)象桨武。

2.通過obtain()獲取Message對(duì)象(先從Message對(duì)象池中拿沒有就new一個(gè)),并把Handler賦值給msg.target

3.sendToTarget()最終調(diào)用queue.enqueueMessage將消息插如MessageQueue中

4.Handler的構(gòu)造方法肋拔,會(huì)首先得到當(dāng)前線程中保存的Looper實(shí)例,進(jìn)而與Looper實(shí)例中的MessageQueue相關(guān)聯(lián)呀酸。

5凉蜂、Looper.prepare()會(huì)在本線程中保存一個(gè)Looper對(duì)象,然后該對(duì)象中保存一個(gè)MessageQueue對(duì)象性誉;因?yàn)長(zhǎng)ooper.prepare()在一個(gè)線程中只能調(diào)用一次窿吩,所以MessageQueue在一個(gè)線程中只會(huì)存在一個(gè)。

6错览、Looper.loop()會(huì)讓當(dāng)前線程進(jìn)入一個(gè)無限循環(huán)爆存,不斷從MessageQueue的實(shí)例中讀取消息,然后回調(diào)msg.target.dispatchMessage(msg)方法蝗砾。

問題二:一個(gè)線程中有幾個(gè)Looper幾個(gè)Handler

一個(gè)線程只有一個(gè)Looper可以有多個(gè)Handler

問題三先较;什么時(shí)候會(huì)進(jìn)入阻塞狀態(tài)

1.消息隊(duì)列中有消息,但是消息指定了執(zhí)行的時(shí)間悼粮,而現(xiàn)在還沒有到這個(gè)時(shí)間闲勺,線程會(huì)進(jìn)入等待狀態(tài)。 2.當(dāng)消息隊(duì)列中沒有消息時(shí)扣猫,它會(huì)使線程進(jìn)入等待狀態(tài)菜循;

問題四:MessageQueue是什么時(shí)候創(chuàng)建的

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

在線程主動(dòng)調(diào)用Looper.prepare可以為當(dāng)前線程主動(dòng)創(chuàng)建一個(gè)Looper對(duì)象(主線程會(huì)自動(dòng)生成一個(gè)Looper對(duì)象),MessageQueue在LOOper構(gòu)造函數(shù)中創(chuàng)建

問題五:Handler是否會(huì)引發(fā)內(nèi)存泄露

當(dāng)Activity退出時(shí)消息隊(duì)列中還有未處理的消息或者正在處理的消息 而消息隊(duì)列中的Messager持有handler實(shí)例的引用(當(dāng)使用內(nèi)部類(包括匿名類)來創(chuàng)建Handler的時(shí)候,Handler對(duì)象會(huì)隱式地持有一個(gè)外部類對(duì)象(通常是一個(gè)Activity)的引用), 所以導(dǎo)致Activity的內(nèi)存資源無法及時(shí)回收申尤,引發(fā)內(nèi)存泄露)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末癌幕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子昧穿,更是在濱河造成了極大的恐慌勺远,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件时鸵,死亡現(xiàn)場(chǎng)離奇詭異胶逢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)饰潜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門初坠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人彭雾,你說我怎么就攤上這事碟刺。” “怎么了薯酝?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵半沽,是天一觀的道長(zhǎng)身诺。 經(jīng)常有香客問我,道長(zhǎng)抄囚,這世上最難降的妖魔是什么霉赡? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮幔托,結(jié)果婚禮上穴亏,老公的妹妹穿的比我還像新娘。我一直安慰自己重挑,他們只是感情好嗓化,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谬哀,像睡著了一般刺覆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上史煎,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天谦屑,我揣著相機(jī)與錄音,去河邊找鬼篇梭。 笑死氢橙,一個(gè)胖子當(dāng)著我的面吹牛模燥,可吹牛的內(nèi)容都是我干的栗弟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼悠轩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼袍患!你這毒婦竟也來了坦康?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤诡延,失蹤者是張志新(化名)和其女友劉穎滞欠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孕暇,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仑撞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妖滔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桶良,死狀恐怖座舍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情陨帆,我是刑警寧澤曲秉,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布采蚀,位于F島的核電站,受9級(jí)特大地震影響承二,放射性物質(zhì)發(fā)生泄漏榆鼠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一亥鸠、第九天 我趴在偏房一處隱蔽的房頂上張望妆够。 院中可真熱鬧,春花似錦负蚊、人聲如沸神妹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸵荠。三九已至,卻和暖如春伤极,著一層夾襖步出監(jiān)牢的瞬間蛹找,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工哨坪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留熄赡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓齿税,卻偏偏與公主長(zhǎng)得像彼硫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凌箕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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