1. 程式人生 > >深入理解android訊息機制(一)——handler Looper原始碼

深入理解android訊息機制(一)——handler Looper原始碼

android 重要核心知識點,怎麼深刻理解都不為過,本篇部落格從常用api ,Looper Hanldery以及HanlderTread原始碼角度解讀

一 常用api,主執行緒接收處理訊息

 private Handler handler = new Handler() {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 1 :
                bt.setText("正在下載...");
                break;
            case
2 : Bitmap bm = (Bitmap) msg.obj; iv.setImageBitmap(bm); break; case 3 : Bundle bundle = msg.getData(); String data = bundle.getString("text"); bt.setText(data); break; } } };

二 子執行緒傳送訊息

send傳送

Message msg=new Message();
msg.what=1;//魔鬼數字,避免
handler.sendMessage(msg);
//或者
sendMessageDelay(what);
//或者
sendEmptyMessage(what);
//如果要帶一個obj
Message msg = new Message(); 
msg.what =2; 
msg.obj = bm; 
handler.sendMessage(msg);
//帶一個bundle
Message msg = new Message(); 
Bundle data = new
Bundle(); data.putString("text", "正在下載..."); msg.what = 3; msg.setData(data); handler.sendMessage(msg); //通過obtainMessage能避免重複Message建立物件 handler.sendEmptyMessage(1); handler.sendEmptyMessageDelayed(1,1000); handler.sendEmptyMessageAtTime(1,1000); handler.sendMessage(Message.obtain()); handler.sendMessageAtTime(Message.obtain(),100); handler.sendMessageDelayed(Message.obtain(),100);

post傳送

mHandler.post(new Runnable()  
        {  
            @Override  
            public void run()  
            {  
                Log.e("TAG", Thread.currentThread().getName());  
                mTxt.setText("yoxi");  
            }  
        }); //並未建立執行緒,與handler同一個執行緒當然還有postDelayed方法

三 handler looper message 三者之間關係,逐個分析

3.1 Looper

Looper負責建立一個MessageQueue,然後進入一個無限迴圈體,不斷從MessageQueue讀取訊息,訊息的建立者就是Handler
Looper主要是prepare()和loop方法,

public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper(true));  
} 

sThreadLocal是一個ThreadLocal物件,可以在一個執行緒中儲存變數。可以看到,在第5行,將一個Looper的例項放入了ThreadLocal,並且2-4行判斷了sThreadLocal是否為null,否則丟擲異常。這也就說明了Looper.prepare()方法不能被呼叫兩次,同時也保證了一個執行緒中只有一個Looper例項.ActivityThread的main函式中呼叫prepare方法
Looper構造器

private Looper(boolean quitAllowed) {  
        mQueue = new MessageQueue(quitAllowed);  
        mRun = true;  
        mThread = Thread.currentThread();  
} 

在構造方法中,建立了一個MessageQueue(訊息佇列)。
loop()方法:

    public static void 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;  

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

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

                msg.target.dispatchMessage(msg);  

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

第2行:
public static Looper myLooper() {
return sThreadLocal.get();
}

方法直接返回了sThreadLocal儲存的Looper例項,如果me為null則丟擲異常,也就是說looper方法必須在prepare方法之後執行。

第6行:拿到該looper例項中的mQueue(訊息佇列)
13到45行:就進入了我們所說的無限迴圈。
14行:取出一條訊息,如果沒有訊息則阻塞。
27行:使用呼叫 msg.target.dispatchMessage(msg);把訊息交給msg的target的dispatchMessage方法去處理。Msg的target是什麼呢?其實就是handler物件,下面會進行分析。
44行:釋放訊息佔據的資源。

Looper主要作用:
1、 與當前執行緒繫結,保證一個執行緒只會有一個Looper例項,同時一個Looper例項也只有一個MessageQueue。
2、 loop()方法,不斷從MessageQueue中去取訊息,交給訊息的target屬性的dispatchMessage去處理。

3.2 handler

好了,我們的非同步訊息處理執行緒已經有了訊息佇列(MessageQueue),也有了在無限迴圈體中取出訊息的哥們,現在缺的就是傳送訊息的物件了,於是乎:Handler登場了。看原始碼

public Handler() {  
        this(null, false);  
}  
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;  
    } 

構造方法中: 14行:通過Looper.myLooper()獲取了當前執行緒儲存的Looper例項,然後在19行又獲取了這個Looper例項中儲存的MessageQueue(訊息佇列),這樣就保證了handler的例項與我們Looper例項中MessageQueue關聯上了。

send訊息時: 所有的message的send方法 sendMessage(Message msg) ,sendEmptyMessageDelayed(int what, long delayMillis)
sendMessageDelayed(Message msg, long delayMillis)
都會走到sendMessageAtTime,在此方法內部有直接獲取MessageQueue然後呼叫了enqueueMessage方法,我們再來看看此方法:

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);  
   }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
       msg.target = this;  
       if (mAsynchronous) {  
           msg.setAsynchronous(true);  
       }  
       return queue.enqueueMessage(msg, uptimeMillis);  
   } 

enqueueMessage中首先為msg.target賦值為this,【如果大家還記得Looper的loop方法會取出每個msg然後交給msg,target.dispatchMessage(msg)去處理訊息】,也就是把當前的handler作為msg的target屬性。最終會呼叫queue的enqueueMessage的方法,也就是說handler發出的訊息,最終會儲存到訊息佇列中去
保證了handler的例項與我們Looper例項中MessageQueue關聯上了。

現在已經很清楚了Looper會呼叫prepare()和loop()方法,在當前執行的執行緒中儲存一個Looper例項,這個例項會儲存一個MessageQueue物件,然後當前執行緒進入一個無限迴圈中去,不斷從MessageQueue中讀取Handler發來的訊息。然後再回調建立這個訊息的handler中的dispathMessage方法,下面我們趕快去看一看這個方法:

public void dispatchMessage(Message msg) {  
        if (msg.callback != null) {  
            handleCallback(msg);  
        } else {  
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            handleMessage(msg);  
        }  
    }

這樣就到了handlerMessge方法,回撥 ok

總結

1、Looper.prepare()呼叫Looper構造器,Looper 構造器new一個MessageQueue,例項化處一個執行緒。prepare方法只能呼叫一次,然後該例項中儲存一個MessageQueue物件;因為Looper.prepare()在一個執行緒中只能呼叫一次,所以MessageQueue在一個執行緒中只會存在一個。

2、Looper.loop()會讓當前執行緒進入一個無限迴圈,不斷從MessageQueue的例項中讀取訊息,然後回撥msg.target.dispatchMessage(msg)方法。

3、Handler的構造方法中,Loop.myLooper()會首先得到當前執行緒中儲存的Looper例項,進而與Looper例項中的MessageQueue相關聯。

4、Handler的send方法,會給msg的target賦值為handler自身,然後將message加入MessageQueue中。

5、在構造Handler例項時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終呼叫的方法。

好了,總結完成,大家可能還會問,那麼在Activity中,我們並沒有顯示的呼叫Looper.prepare()和Looper.loop()方法,為啥Handler可以成功建立呢,這是因為在Activity的啟動程式碼中,已經在當前UI執行緒ActiveityThread呼叫了Looper.prepare()和Looper.loop()方法。
簡化如下:

Looper,prepare方法threadLocal保證一個執行緒只有一個looper例項,構造方法中建立msgqueue,loop無限迴圈方法,msgqueue.next 取出訊息,msg.target.dispatchMessage ,把訊息交給target的dispatchmessage方法處理,hanlder構造方法中Looper靜態方法獲取當前執行緒儲存的Looper例項,(訊息輪詢和處理都在主執行緒), sendMessage方法無論是否延遲都會走到sendMesageAtTime –>enqueueMessage方法 將this賦值給msg.target 。handler傳送的訊息儲存到訊息佇列中,dispatchMessage中呼叫handleMessage

相關推薦

深入理解android訊息機制——handler Looper原始碼

android 重要核心知識點,怎麼深刻理解都不為過,本篇部落格從常用api ,Looper Hanldery以及HanlderTread原始碼角度解讀 一 常用api,主執行緒接收處理訊息 private Handler handler = ne

深入理解HTTP訊息

初識HTTP訊息頭     但凡搞 WEB 開發的人都離不開HTTP(超文字傳輸協議),而要了解HTTP,除了HTML本身以外,還有一部分不可忽視的就是HTTP訊息頭。 做 過Socket程式設計的人都知道,當我們設計一個通訊協議時,“訊息頭/訊息

Android訊息機制之 ThreadLocal

前言   今天重溫了Android開發藝術探索上的訊息機制,花了一些時間,書上寫很好,但是可能文章一些先後順序問題,不是特別好理解,這篇文章博主用了自己的理解,看原始碼,結合書上的知識,希望大家能更容易理解。(可能會寫的不太好。。。不正確的地方歡迎指出)  

再次深入理解類載入機制

一、類的載入方法1、ClassLoader的的基本概念:       與c與c++編寫的程式不同,Java程式並不是可執行檔案,而是有許多的類檔案組成,每個檔案對應一個Java類。而且這些類並不是全部裝進記憶體,而是根據程式執行的需要逐步裝載。ClassLoader是JVM的

深入理解OkHttp源碼——提交請求

mat esp 屬於 idt set ref setname 失敗 class 本篇文章主要介紹OkHttp執行同步和異步請求的大體流程。主要流程如下圖: 主要分析到getResponseWidthInterceptorChain方法,該方法為具體的根據請求獲取響應

WPF的訊息機制- 讓應用程式動起來

原文: WPF的訊息機制(一)- 讓應用程式動起來 前言 談起“訊息機制”這個詞,我們都會想到Windows的訊息機制,系統將鍵盤滑鼠的行為包裝成一個Windows Message,然後系統主動將這些Windows Message派發給特定的視窗,實際上訊息是被Post到特定視窗所線上程

深入理解java虛擬機器java虛擬機器的記憶體區域

一、 java虛擬機器記憶體區域主要有:方法區、堆、虛擬機器棧、本地方方法棧、程式計數器     按照執行緒私有和共有來分:執行緒私有的有--程式計數器,虛擬機器棧,本地方法棧。共有的有--本地方法區,堆     1、程式計數器:主要功能是控制程式

深入理解java虛擬機器java的記憶體區域

程式計數器:可以看作當前執行緒所執行的位元組碼的行號指示器,位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條 需要執行的位元組碼指令,分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來實現。每一個執行緒都有一個獨立的程式計數器,各個執行緒之間的計數器互不影響,獨立

深入理解Java虛擬機器--- Java 與 JVM

Java 特性 1.結構嚴謹,面向物件程式語言 2.跨平臺性 3.較安全的記憶體管理和訪問機制(避免了絕大部分記憶體洩漏和指標越界的問題) 4.實現熱點程式碼檢測和執行時編譯優化 5.擁有一套完整的API介面 6.擁有豐富的第三方庫 JVM 特性 基於棧

深入理解Java虛擬機器——JVM整體結構與垃圾回收演算法介紹

JVM整體架構 •JVM(虛擬機器):指以軟體的方式模擬具有完整硬體系統功能、執行在一個完全隔離環境中的完整計算機系統 ,是物理機的軟體實現。常用的虛擬機器有VMWare,Virtual Box,Ja

深入理解java虛擬機器

前言  本篇主要講述java記憶體區域的劃分。下面直接進入正題。 概述 java虛擬機器就是在真實物理機上虛擬出來的一臺計算機,java語言有一個特點就是可以跨平臺,其中java起著關鍵作用。這是因為它遮蔽與平臺相關的資訊,java原始檔經過編譯程式編譯後生成位元組碼檔

深入理解線性迴歸演算法

前言 線性迴歸演算法是公眾號介紹的第一個機器學習演算法,原理比較簡單,相信大部分人對線性迴歸演算法的理解多於其他演算法。本文介紹的線性迴歸演算法包括最小二乘法和最大似然法,進而討論這兩種演算法蘊含的一些小知識,然後分析演算法的偏差和方差問題,最後總結全文。        

深入理解多執行緒——Synchronized的實現原理

synchronized,是Java中用於解決併發情況下資料同步訪問的一個很重要的關鍵字。當我們想要保證一個共享資源在同一時間只會被一個執行緒訪問到時,我們可以在程式碼中使用synchronized關鍵字對類或者物件加鎖。那麼,本文來介紹一下synchronized關鍵字的實

深入理解Java記憶體模型——基礎

併發程式設計模型的分類 在併發程式設計中,我們需要處理兩個關鍵問題:執行緒之間如何通訊及執行緒之間如何同步(這裡的執行緒是指併發執行的活動實體)。通訊是指執行緒之間以何種機制來交換資訊。在指令式程式設計中,執行緒之間的通訊機制有兩種:共享記憶體和訊息傳遞。 在共享記憶體的併發模型裡,執行緒之

深入理解java虛擬機器----jvm記憶體模型

最近大致的學習了一下jvm的相關技術,發現深入理解java虛擬機器這本書很不錯,所以想將這本書的內容的重難點在blog總結一下,一是為了鞏固這些知識,二是為了把這些重點單獨寫出來,讓初學者在學習的時候有一個大致的框架以至於學起來不至於那麼迷茫 學習java虛擬

Windows程式和訊息機制:視窗程式的建立

Windows視窗程式的實現 上面介紹了Windows下的訊息機制,系統傳送訊息到程式,程式接收到訊息後的處理統稱為視窗過程。 要實現視窗過程當然需要先建立一個視窗程式了。視窗程式的建立很簡單,主要分為以下幾個步驟: 註冊視窗類建立視窗及顯示視窗建立訊

深入理解java虛擬機器:java記憶體區域記憶體結構劃分

圖一:java記憶體結構劃分 由上圖可知,java記憶體主要分為6部分,分別是程式計數器,虛擬機器棧,本地方法棧,堆,方法區和直接記憶體,下面將逐一詳細描述。 1、程式計數器 執行緒私有,即每個執行緒都會有一個,執行緒之間互不影響,獨立儲存。 代表著當前執行緒所執行

WIN32學習——Windows訊息機制

1、Win32視窗程式採用的是事件驅動方式執行,也就是訊息機制,當系統通知視窗工作時,就是採用訊息的方式派發給視窗,通過呼叫視窗處理函式進行對訊息對處理。 2、訊息MessageBox結構體: int MessageBox( HWND hWnd, //父視窗

Android視窗機制初識Android的視窗結構

Android視窗機制系列 Android視窗機制(一)初識Android的視

深入理解Java類載入機制

1 前言: 在上一篇文章一文讓你明白 Java 位元組碼中, 我們瞭解了java位元組碼的解析過程,那麼在接下來的內容中,我們來了解一下類的載入機制。 2 題外話 Java的核心是什麼?當然是JVM了,所以說了解並熟悉JVM對於我們理解Java語言非常重要,不管你是做Java還是Andr