Android源碼——Handler機制(一)

Android源碼——Handler機制(一)

    • Handler機制概述
      • 介紹
      • Handler機制模型
      • Handler機制架構
    • Handler機制源碼解析
      • ActivityThread
      • Looper
      • Handler

Handler機制概述

介紹

Handler是Android消息機制的上層接口。Handler可以將一個任務切換到Handler所在的線程中去執行。通常情況下,Handler的使用場景就是更新UI。

如下就是使用消息機制的一個簡單實例:

public class BasicActivity extends AppCompatActivity {private static final String TAG = "BasicActivity";private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Log.i(TAG, msg.what);}};@Overridepublic void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {super.onCreate(savedInstanceState, persistentState);setContentView(R.layout.activity_main);new Thread(new Runnable() {@Overridepublic void run() {// 耗時操作.....Message message = Message.obtain();message.what = 1;mHandler.sendMessage(message);}}).start();}
}

在子線程中,進行耗時操作,執行完操作后,發送消息,通知主線程更新UI。這便是消息機制的典型應用場景。我們通常只會接觸到Handler和Message來完成消息機制,其實內部還有兩大助手來共同完成消息傳遞。

Handler機制模型

消息機制主要包含:MessageQueueHandlerLooper這三大部分,以及Message

  • Message: 需要傳遞的消息,可以傳遞數據;
  • MessageQueue: 消息隊列,但是它的內部實現并不是用的隊列,實際上是通過一個單鏈表的數據結構來維護消息列表,因為單鏈表在插入和刪除上比較有優勢。主要功能向消息池投遞消息MessageQueue.enqueueMessage和取走消息池的消息MessageQueue.next
  • Handler: 消息輔助類,主要功能向消息池發送各種消息事件Handler.sendMessage和處理相應消息事件Handler.handleMessage
  • Looper: 不斷循環執行Looper.loop,從MessageQueue中讀取消息,按分發機制將消息分發給目標處理者。

Handler機制架構

運行流程:在子線程執行完耗時操作,當Handler發送Message時,會調用MessageQueue.enqueueMessage,向消息隊列中添加Message。當通過Looper.loop開啟循環后,會不斷從線程池中讀取消息,即調用MessageQueue.next,然后調用目標Handler(即發送該消息的Handler)的dispatchMessage方法傳遞消息,然后返回到Handler所在線程,目標Handler收到消息,調用handleMessage方法,接收處理消息。
在這里插入圖片描述

每個線程只能存在一個LooperLooper保存在ThreadLocal線程中。主線程(UI線程)已經創建了一個Looper,所以在主線程中不需要再創建Looper,但是在其他線程中需要創建Looper。每個線程可以有多個Handler,即一個Looper可以處理來自多個Handler的消息。Looper中維護一個MessageQueue,來維護消息隊列,消息隊列中的Message可以來自不同的Handler

在這里插入圖片描述
Looper有一個MessageQueue消息隊列,MessageQueue有一組待處理的MessageMessage中記錄發送和處理消息的HandlerHandler中有LooperMessageQueue

Handler機制源碼解析

ActivityThread

ActivityThread類是用來啟動Android的,在main()函數中準備相關環境,創建并啟動Looper循環。

主線程中不需要自己創建Looper,這是由于在程序啟動的時候,系統已經幫我們自動調用了Looper.prepare()方法。

public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");// Install selective syscall interceptionAndroidOs.install();// CloseGuard defaults to true and can be quite spammy.  We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);Environment.initForCurrentUser();// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);// Call per-process mainline module initialization.initializeMainlineModules();Process.setArgV0("<pre-initialized>");// 1. 把主線程和消息隊列放進Looper中,同時把Looper放進ThreadLocal(每個線程綁定一個Looper)Looper.prepareMainLooper();// ....// 啟動Looper循環,不斷循環取出消息Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper

Looper不斷循環執行loop(),從MessageQueue中讀取消息,按分發機制將消息分發給目標處理者。

  1. 初始化Looper
public final class Looper {// sThreadLocal.get() will return null unless you've called prepare().@UnsupportedAppUsagestatic final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();@UnsupportedAppUsageprivate static Looper sMainLooper;  // guarded by Looper.class@UnsupportedAppUsagefinal MessageQueue mQueue;final Thread mThread;/** Initialize the current thread as a looper.* This gives you a chance to create handlers that then reference* this looper, before actually starting the loop. Be sure to call* {@link #loop()} after calling this method, and end it by calling* {@link #quit()}.*/public static void prepare() {prepare(true);}/*** 創建Looper,并保存在ThreadLocal,不能重復創建Looper,只能創建一個。 */private static void prepare(boolean quitAllowed) {// 不能重復創建否則報錯if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}// 4. 實例化自己,封裝mQueue和主線程,把自己放入ThreadLocal中sThreadLocal.set(new Looper(quitAllowed));}/*** Initialize the current thread as a looper, marking it as an* application's main looper. See also: {@link #prepare()}** @deprecated The main looper for your application is created by the Android environment,*   so you should never need to call this function yourself.*/@Deprecatedpublic static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}/*** Return the Looper object associated with the current thread.  Returns* null if the calling thread is not associated with a Looper.*/public static @Nullable Looper myLooper() {return sThreadLocal.get();}private Looper(boolean quitAllowed) {// 2. 實例化MessageQueuemQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}}
  1. 開啟Looper
    /*** 5. 調用Looper.loop()方法* Run the message queue in this thread. Be sure to call* {@link #quit()} to end the loop.*/public static void loop() {// 獲取TLS存儲的Looper對象final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}if (me.mInLoop) {Slog.w(TAG, "Loop again would have the queued messages be executed"+ " before this one completed.");}me.mInLoop = true;// 獲取Looper對象中的消息隊列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();// 進入loop()主循環for (;;) {// 消息隊列取出一條消息Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// 默認為null,可通過setMessageLogging()方法來指定輸出,用于debug功能// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}// .....try {// 獲取msg的目標Handler,然后用于分發Messagemsg.target.dispatchMessage(msg);// ....} catch (Exception exception) {// ...} finally {// ...}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();// 回收可能正在使用的消息msg.recycleUnchecked();}}

loop()進入循環模式,不斷重復下面的操作,直到消息為空時退出循環:讀取MessageQueue的下一條Message;把Message分發給相應的target

next()取出下一條消息時,隊列中已經沒有消息時,next()會無限循環,產生阻塞。等待MessageQueue中加入消息,然后重新喚醒。

Handler

消息輔助類,主要功能向消息池發送各種消息事件(Handler.sendMessage)和處理相應消息事件(Handler.handleMessage)。

  1. 創建Handler
public class Handler {@UnsupportedAppUsagefinal Looper mLooper;// 這里并沒有實例化,指向Lopper的消息隊列final MessageQueue mQueue;@UnsupportedAppUsagefinal Callback mCallback;final boolean mAsynchronous;@UnsupportedAppUsageIMessenger mMessenger;/*** Callback interface you can use when instantiating a Handler to avoid* having to implement your own subclass of Handler.*/public interface Callback {/*** @param msg A {@link android.os.Message Message} object* @return True if no further handling is desired*/boolean handleMessage(@NonNull Message msg);}/*** Subclasses must implement this to receive messages.*/public void handleMessage(@NonNull Message msg) {}}

構造函數:

    @Deprecatedpublic Handler() {this(null, false);}@Deprecatedpublic Handler(@Nullable Callback callback) {this(callback, false);}@UnsupportedAppUsagepublic Handler(boolean async) {this(null, async);}/*** 第一種構造方法:給主線程調用,因為主線程已經調用Looper.prepareMainLooper()*/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());}}// 獲取主線程的LoopermLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}// Handler的mQueue指向Looper的mQueuemQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}public Handler(@NonNull Looper looper) {this(looper, null, false);}public Handler(@NonNull Looper looper, @Nullable Callback callback) {this(looper, callback, false);}/*** 第二種構造方法,給在子線程中創建Handler時使用,子線程要自主調用Looper.prepare()*/@UnsupportedAppUsagepublic Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;}
  1. 發送消息

發送消息有幾種方式,但是歸根結底都是調用了sendMessageAtTime()方法。在子線程中通過Handlerpost()方式或send()方式發送消息,最終都是調用了sendMessageAtTime()方法。

post方法:

    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 postAtTime(@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);}public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {return sendMessageDelayed(getPostMessage(r), delayMillis);}public final boolean postDelayed(Runnable r, int what, long delayMillis) {return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);}public final boolean postDelayed(@NonNull Runnable r, @Nullable Object token, long delayMillis) {return sendMessageDelayed(getPostMessage(r, token), delayMillis);}

send方法:

    public final boolean sendMessage(@NonNull Message msg) {return sendMessageDelayed(msg, 0);}public final boolean sendEmptyMessage(int what){return sendEmptyMessageDelayed(what, 0);}public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageDelayed(msg, delayMillis);}public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageAtTime(msg, uptimeMillis);}public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

就連子線程中調用Activity中的runOnUiThread()更新UI,其實也是發送消息通知主線程更新UI,最終也會調用sendMessageAtTime()方法。

/*** 如果當前的線程不等于UI線程(主線程),就去調用Handler的post()方法,最終會調用sendMessageAtTime()方法。* 否則就直接調用Runnable對象的run()方法。*/
public final void runOnUiThread(Runnable action) {if (Thread.currentThread() != mUiThread) {mHandler.post(action);} else {action.run();}
}

sendMessageAtTime()

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {// 其中mQueue是消息隊列,從Looper中獲取的MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}// 調用enqueueMessage方法return enqueueMessage(queue, msg, uptimeMillis);}private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {// 把自己(Handler)傳入消息一并發送,因為Looper需要用Handler來執行dispatchMessage()msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}// 調用MessageQueue的enqueueMessage方法return queue.enqueueMessage(msg, uptimeMillis);}

可以看到sendMessageAtTime()方法的作用很簡單,就是調用MessageQueueenqueueMessage()方法,往消息隊列中添加一個消息。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/36980.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/36980.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/36980.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

趕緊收藏!2024 年最常見的操作系統面試題(八)

上一篇地址&#xff1a;趕緊收藏&#xff01;2024 年最常見的操作系統面試題&#xff08;七&#xff09;-CSDN博客 十五、什么是進程同步&#xff1f;請舉例說明幾種進程同步的方法。 進程同步是操作系統中用于控制多個進程或線程對共享資源的訪問的一種機制。它確保在任何給…

網絡物理隔離后 可以用保密U盤進行數據安全交換嗎?

企業用的保密U盤通常被設計用于存儲和傳輸敏感信息&#xff0c;以確保數據的安全和保密性。 在網絡之間實現了物理隔離后&#xff0c;使用保密U盤進行數據安全交換是一種常見的做法。物理隔離確保了兩個網絡之間的完全分離&#xff0c;因此使用保密U盤可以作為一種安全的手段來…

android view 設置過 transalationY/X 后 marginTop/marginStart/Left 不變

在 Android 開發中&#xff0c;當你對一個視圖(View)設置了 translationY 屬性后&#xff0c;這個視圖的 marginTop 屬性實際上并不會改變。這是因為 translationY 只會影響視圖的繪制位置&#xff0c;而不會改變視圖的布局參數。換句話說&#xff0c;translationY 是一個運行時…

第1章 物聯網模式簡介---物聯網概述

物聯網模式簡介 物聯網&#xff08;IoT&#xff09;在最近幾年獲得了巨大的吸引力&#xff0c;該領域在未來幾年將呈指數級增長。這一增長將跨越所有主要領域/垂直行業&#xff0c;包括消費者、家庭、制造業、健康、旅游和運輸。這本書將為那些想了解基本物聯網模式以及如何混…

UNIAPP_在js文件中使用i18n國際化

導入 import { initVueI18n } from dcloudio/uni-i18n import messages from /locale/index const { t } initVueI18n(messages) 使用 t(config.request.i001).

【大模型】大模型微調方法總結(四)

1. P-Tuning v1 1.背景 大模型的Prompt構造方式嚴重影響下游任務的效果。比如&#xff1a;GPT-3采用人工構造的模版來做上下文學習&#xff08;in context learning&#xff09;&#xff0c;但人工設計的模版的變化特別敏感&#xff0c;加一個詞或者少一個詞&#xff0c;或者變…

MySQL存儲引擎 INNODB和MYISAM

存儲引擎概述 什么是存儲引擎 是數據庫底層軟件組件&#xff0c;數據庫管理系統使用數據索引進行創建、查詢、更新和刪除數據操作。不同的存儲引擎提供不同的存儲機制、索引技巧】鎖定水平等功能&#xff0c;使用不同的存儲引擎可以獲得特定的功能 MySQL5.7支持的存儲引擎 …

大數據面試之Hadoop

目錄 介紹下Hadoop Hadoop的特點 說下Hadoop生態圈組件及其作用 Hadoop主要分哪幾個部分?他們有什么作用? Hadoop 1.x&#xff0c;2x&#xff0c;3.x的區別 Hadoop集群工作時啟動哪些進程?它們有什么作用? 在集群計算的時候&#xff0c;什么是集群的主要瓶頸 搭建Ha…

用英文介紹美國總統Trump: Donald J. Trump Twice Impeached (2017 – 2021)

Donald J. Trump: Twice Impeached (2017 – 2021) Link: https://www.youtube.com/watch?vJ7RC2DKf6rs&listPLybg94GvOJ9E-ZM1U6PAjgPUmz-V4-Yja&index45 Summary Summary of Donald Trump’s Rise and Presidency Donald John Trump, originally from Queens, Ne…

網頁中如何接入高德地圖【靜態地圖篇】

接入高德地圖 登錄高德開放平臺創建應用添加key創建靜態地圖文檔說明markers 網頁應用總結 登錄高德開放平臺 高德開放平臺 創建應用 點擊我的應用 -> 創建應用 添加key 調相關接口都需要用到這個key&#xff01; 創建靜態地圖 靜態地圖API文檔 文檔說明 服務地址…

基于上一篇博客,用阻塞隊列實現異步下單

在上一篇博客中&#xff0c;我們介紹了如何利用 Redis 和 Lua 腳本來高效處理秒殺活動中的高并發請求&#xff0c;保證用戶體驗。本文將進一步優化秒殺系統&#xff0c;通過引入阻塞隊列實現異步下單&#xff0c;從而提高系統的整體性能和穩定性。 引言 秒殺活動往往伴隨著極…

ArmSoM-Sige7/5/1 和樹莓派5規格比較

引言 在當今快速發展的嵌入式系統領域&#xff0c;選擇一款性能強大、功能豐富的開發板對于項目的成功至關重要。本文將介紹并比較 Sige7、Sige5、Raspberry Pi 5 和 Sige1 這四款開發板的關鍵規格和特性&#xff0c;幫助開發者和愛好者選擇最適合其需求的平臺。 ArmSoM-Sige…

使用模板方法設計模式封裝 socket 套接字并實現Tcp服務器和客戶端 簡單工廠模式設計

文章目錄 使用模板方法設計模式封裝套接字使用封裝后的套接字實現Tcp服務器和客戶端實現Tcp服務器實現Tcp客戶端 工廠模式 使用模板方法設計模式封裝套接字 可以使用模塊方法設計模式來設計套接字 socket 的封裝 模板方法&#xff08;Template Method&#xff09;設計模式是一…

【深度學習】深度學習基礎

李宏毅深度學習筆記 局部極小值與鞍點 鞍點其實就是梯度是零且區別于局部極小值和局部極大值的點。 鞍點的叫法是因為其形狀像馬鞍。鞍點的梯度為零&#xff0c;但它不是局部極小值。我們把梯度為零的點統稱為臨界點&#xff08;critical point&#xff09;。損失沒有辦法再下…

使用Flink CDC實現 Oracle數據庫數據同步(非SQL)

文章目錄 前言一、開啟歸檔日志二、創建flinkcdc專屬用戶2.1 對于Oracle 非CDB數據庫&#xff0c;執行如下sql2.2 對于Oracle CDB數據庫&#xff0c;執行如下sql 三、指定oracle表、庫級啟用四、使用flink-connector-oracle-cdc實現數據庫同步4.1 引入pom依賴4.1 Java主代碼4.1…

Docker Desktop 簡易操作指南 (Windows, macOS, Linux)

1. 下載最新版本 Docker Desktop https://www.docker.com/products/docker-desktop/ 2.啟動 Docker Desktop 3.常用命令&#xff08;在 cmd 或 Terminal 中執行&#xff09; #列出所有鏡像&#xff08;Images&#xff09; docker images #列出所有容器&#xff08;Containers&…

OpenSSL/3.3.0: error:0A00018A:SSL routines::dh key too small

php curl解決辦法: curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, ‘DEFAULTSECLEVEL1’); python 解決辦法: from twisted.internet.ssl import AcceptableCiphers from scrapy.core.downloader import contextfactory contextfactory.DEFAULT_CIPHERS AcceptableCiphers.from…

CSS 核心知識點 - grid

思維導圖 參考網址: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_grid_layout 一、什么是 grid&#xff1f; CSS Grid布局是在CSS3規范中引入的一種新的布局方式&#xff0c;旨在解決傳統布局方法&#xff08;如浮動、定位、表格布局&#xff09;存在的許多問題。C…

Spring Boot 集成 MyBatis-Plus 總結

Spring Boot 集成 MyBatis-Plus 總結 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01; 在Java開發中&#xff0c;Spring Boot以其簡潔和高效的特點&#xff0c;…

Oh My Zsh Git 插件

以下是一些常見的別名和它們對應的 Git 命令&#xff1a; g: gitga: git addgaa: git add --allgapa: git add --patchgau: git add --updategb: git branchgba: git branch -agbd: git branch -dgbda: git branch --no-color --merged | command grep -vE “^(||*|\s*(main|m…