0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

詳解Android Handler機(jī)制和原理

Android編程精選 ? 來源:Android編程精選 ? 作者:Android編程精選 ? 2023-03-26 14:32 ? 次閱讀

Handler的使用

Android開發(fā)中,Handler機(jī)制是一個(gè)很重要的知識(shí)點(diǎn),主要用于消息通信。

Handler使用的三大步驟:

1、Loop.prepare()。

2、new一個(gè)Handler對象,并重寫handleMessage方法。

3、Loop.loop()。

先運(yùn)行實(shí)例代碼觀察現(xiàn)象,再深入分析內(nèi)部原理。

publicclassLooperThreadextendsThread{
privatestaticfinalString TAG = LooperThread.class.getSimpleName();
privateHandler handler;

@Override
publicvoidrun(){
Looper.prepare();
handler = newHandler(Looper.myLooper(), newHandler.Callback() {
@Override
publicbooleanhandleMessage(@NonNull Message msg){
Log.d(TAG, "what: "+ msg.what + ", msg: "+ msg.obj.toString());
returntrue;
}
});
Looper.loop();
}

publicvoidsendMessage(intwhat, Object obj){
Message msg = handler.obtainMessage(what, obj);
handler.sendMessage(msg);
}
}

publicclassFirstActivityextendsAppCompatActivity{
privatestaticfinalString TAG = FirstActivity.class.getSimpleName();

privateLooperThread looperThread;

@Override
protectedvoidonCreate(Bundle savedInstanceState){

looperThread = newLooperThread();
looperThread.start();
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
looperThread.sendMessage(1, "Hello android!");
}

編譯運(yùn)行程序,輸出如下:

2021-10-0623:15:24.32320107-20107/com.example.activitytest D/FirstActivity: Task id is73
2021-10-0623:15:25.32820107-20124/com.example.activitytest D/LooperThread: what:1, msg:Hello android!
2021-10-0623:15:25.39420107-20132/com.example.activitytest I/OpenGLRenderer: Initialized EGL, version1.4
2021-10-0623:15:25.39420107-20132/com.example.activitytest D/OpenGLRenderer: Swap behavior 1

Loop.prepare方法內(nèi)部實(shí)現(xiàn)原理

了解某個(gè)方法具體做了什么,最好的方法就是追蹤下去看源碼。我們跟隨IDE一步一步查看Loop.prepare到底做了什么。

/** 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()}.
*/
publicstaticvoidprepare() {
prepare(true);
}

privatestaticvoidprepare(boolean quitAllowed) {
if(sThreadLocal.get() != null) {
thrownewRuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(newLooper(quitAllowed));
}

sThreadLocal是一個(gè)ThreadLocal類型變量,且ThreadLocal是一個(gè)模板類。Loop.prepare最終創(chuàng)建一個(gè)新的Looper對象,且對象實(shí)例被變量sThreadLocal引用。繼續(xù)追蹤下去,查看Looper構(gòu)造方法做了什么操作。

privateLooper(booleanquitAllowed){
mQueue = newMessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
......
MessageQueue(booleanquitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
到這里我們已經(jīng)很清楚,Looper構(gòu)造方法主要是創(chuàng)建一個(gè)MessageQueue,且MessageQueue構(gòu)造方法調(diào)用native方法獲取底層queue的指針,mQuitAllowed值為true表示允許退出loop,false表示無法退出loop。結(jié)合前面Looper.prepare方法內(nèi)部代碼,表示我們創(chuàng)建的Looper允許退出loop。 new一個(gè)Handler對象實(shí)例,到底做了什么?
/**
* Use the provided {@linkLooper} instead of the default one and take a callback
* interface in which to handle messages.
*
* @paramlooper The looper, must not be null.
* @paramcallback The callback interface in which to handle messages, or null.
*/
publicHandler(@NonNull Looper looper, @Nullable Callback callback){
this(looper, callback, false);
}
......
/**
* Use the provided {@linkLooper} instead of the default one and take a callback
* interface in which to handle messages. Also set whether the handler
* should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by conditions such as display vsync.
*
* @paramlooper The looper, must not be null.
* @paramcallback The callback interface in which to handle messages, or null.
* @paramasync If true, the handler calls {@linkMessage#setAsynchronous(boolean)} for
* each {@linkMessage} that is sent to it or {@linkRunnable} that is posted to it.
*
* @hide
*/
@UnsupportedAppUsage
publicHandler(@NonNull Looper looper, @Nullable Callback callback, booleanasync){
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

Handler還有其他構(gòu)造方法,這里我們調(diào)用其中一種構(gòu)造方法創(chuàng)建一個(gè)Handler對象實(shí)例。該構(gòu)造方法要求傳入一個(gè)Looper對象實(shí)例和CallBack對象實(shí)例?;仡櫼幌伦铋_始的例子代碼,我們傳入的形參,一個(gè)是由Looper.myLooper方法獲取的Looper對象實(shí)例,另外一個(gè)則是Callback匿名類。我們先看看Looper.myLooper到底獲取到了什么。

/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
publicstatic@Nullable Looper myLooper() {
returnsThreadLocal.get();
}
這里獲取到的就是前面Looper.prepare方法新創(chuàng)建的Looper對象實(shí)例,所以Looper.prepare方法必須在創(chuàng)建Handler對象實(shí)例之前調(diào)用。再回到Handler構(gòu)造方法里,有幾個(gè)地方很關(guān)鍵: 1、Handler內(nèi)部保存了Looper對象引用。 2、Handler內(nèi)部保存了Looper內(nèi)部的MessageQueue對象引用。 3、Handler內(nèi)部保存了Callback對象引用。 4、mAsyncchronous值為true表示handleMessage方法異步執(zhí)行,false表示同步執(zhí)行。

Looper.loop方法內(nèi)部實(shí)現(xiàn)原理

/**
* Run the message queue in this thread. Be sure to call
* {@link#quit()} to end the loop.
*/
publicstaticvoidloop(){
finalLooper me = myLooper();
if(me == null) {
thrownewRuntimeException("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;
finalMessageQueue 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();
finallongident = Binder.clearCallingIdentity();

// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
finalintthresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);

booleanslowDeliveryDetected = false;

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
finalPrinter logging = me.mLogging;
if(logging != null) {
logging.println(">>>>> Dispatching to "+ msg.target + " "+
msg.callback + ": "+ msg.what);
}
// Make sure the observer won't change while processing a transaction.
finalObserver observer = sObserver;

finallongtraceTag = me.mTraceTag;
longslowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
longslowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if(thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
finalbooleanlogSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
finalbooleanlogSlowDispatch = (slowDispatchThresholdMs > 0);

finalbooleanneedStartTime = logSlowDelivery || logSlowDispatch;
finalbooleanneedEndTime = logSlowDispatch;

if(traceTag != 0&& Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}

finallongdispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
finallongdispatchEnd;
Object token = null;
if(observer != null) {
token = observer.messageDispatchStarting();
}
longorigWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try{
msg.target.dispatchMessage(msg);
if(observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch(Exception exception) {
if(observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throwexception;
} finally{
ThreadLocalWorkSource.restore(origWorkSource);
if(traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if(logSlowDelivery) {
if(slowDeliveryDetected) {
if((dispatchStart - msg.when) <= 10) {
????????????????????????Slog.w(TAG, "Drained");
????????????????????????slowDeliveryDetected = false;
????????????????????}
????????????????} else?{
????????????????????if?(showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
????????????????????????????msg)) {
????????????????????????// Once we write a slow delivery log, suppress until the queue drains.
????????????????????????slowDeliveryDetected = true;
????????????????????}
????????????????}
????????????}
????????????if?(logSlowDispatch) {
????????????????showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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.recycleUnchecked();
????????}
????}

代碼較長,我們只取關(guān)鍵代碼閱讀。通過myLooper獲取新創(chuàng)建的Looper對象實(shí)例,進(jìn)而獲取Looper內(nèi)部的MessageQueue對象實(shí)例。然后進(jìn)入死循環(huán)中不斷調(diào)用MessageQueue類的next方法獲取MessageQueue里的message,然后調(diào)用dispatchMessage進(jìn)行消息分發(fā),最后由handleMessage進(jìn)行消息處理。到這里L(fēng)ooper、MessageQueue和Handler之間的關(guān)系就建立起來了。介于篇幅,發(fā)送消息和消息處理原理,下篇文章詳細(xì)分析。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • Android
    +關(guān)注

    關(guān)注

    12

    文章

    3936

    瀏覽量

    127417
  • 通信
    +關(guān)注

    關(guān)注

    18

    文章

    6032

    瀏覽量

    135999
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68625
  • handler
    +關(guān)注

    關(guān)注

    0

    文章

    7

    瀏覽量

    3035

原文標(biāo)題:詳解Android Handler機(jī)制和原理(一)

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Trigger Handler

    handler
    橙群微電子
    發(fā)布于 :2023年02月23日 09:04:12

    高保真膽機(jī)制詳解

    http://115.com/file/be3wripk#高保真膽機(jī)制詳解.rar
    發(fā)表于 02-14 09:54

    Android系統(tǒng)原理與開發(fā)要點(diǎn)詳解_培訓(xùn)課件

    Android系統(tǒng)原理與開發(fā)要點(diǎn)詳解_培訓(xùn)課件
    發(fā)表于 08-20 13:01

    android 通信機(jī)制 socket

    socket 作為一種通信機(jī)制,可以實(shí)現(xiàn)單機(jī)或者跨網(wǎng)絡(luò)之間的通信,要有明確的server端和client端。android里面最簡單的socket 通信的demo://1. IP 地址
    發(fā)表于 01-09 22:36

    Android系統(tǒng)下Java編程詳解,Android學(xué)習(xí)者必備

    Android系統(tǒng)下Java編程詳解,從各方面對Android系統(tǒng)的學(xué)習(xí)做出詳解,這些都是在華清遠(yuǎn)見學(xué)習(xí)的一手資料,可以下載學(xué)習(xí)哦,我學(xué)過了,還是不錯(cuò)的
    發(fā)表于 05-30 13:21

    詳解Linux內(nèi)核搶占實(shí)現(xiàn)機(jī)制

    本文詳解了Linux內(nèi)核搶占實(shí)現(xiàn)機(jī)制。首先介紹了內(nèi)核搶占和用戶搶占的概念和區(qū)別,接著分析了不可搶占內(nèi)核的特點(diǎn)及實(shí)時(shí)系統(tǒng)中實(shí)現(xiàn)內(nèi)核搶占的必要性。然后分析了禁止內(nèi)核搶占的情況和內(nèi)核搶占的時(shí)機(jī),最后介紹了實(shí)現(xiàn)搶占內(nèi)核所做的改動(dòng)以及何時(shí)需要重新調(diào)度。
    發(fā)表于 08-06 06:16

    AndroidHandler

    在了解 Handler 前,首先需要知道 Android 中,當(dāng)程序啟動(dòng)時(shí),Android 系統(tǒng)會(huì)啟動(dòng)一條線程,該線程也被稱作 UI 線程,主要用于進(jìn)行界面 UI 的操作。而又因?yàn)槠洳皇蔷€程安全
    發(fā)表于 09-23 09:05

    深入剖析Android消息機(jī)制

    深入剖析Android消息機(jī)制
    發(fā)表于 01-22 21:11 ?11次下載

    Android開發(fā)手冊—API函數(shù)詳解

    Android開發(fā)手冊—API函數(shù)詳解
    發(fā)表于 10-17 09:01 ?13次下載
    <b class='flag-5'>Android</b>開發(fā)手冊—API函數(shù)<b class='flag-5'>詳解</b>

    基于Android開發(fā)手冊—API函數(shù)詳解

    基于Android開發(fā)手冊—API函數(shù)詳解
    發(fā)表于 10-24 09:06 ?18次下載
    基于<b class='flag-5'>Android</b>開發(fā)手冊—API函數(shù)<b class='flag-5'>詳解</b>

    Android 異步通信原理機(jī)制-- handler

    .handler的作用:完成Android中的線程通信(數(shù)據(jù)的異步加載顯示,在子線程中完成耗時(shí)操作,在子線程中加載之后通知UI線程顯示數(shù)據(jù))
    發(fā)表于 06-13 01:10 ?2008次閱讀

    家用風(fēng)力發(fā)電機(jī)制作過程詳解

    家用風(fēng)力發(fā)電機(jī)制作過程詳解
    的頭像 發(fā)表于 08-21 16:11 ?3.6w次閱讀

    詳細(xì)解答Android消息機(jī)制

    Android程序運(yùn)行中,線程之間或者線程內(nèi)部進(jìn)行信息交互時(shí)經(jīng)常會(huì)使用到消息,如果我們熟悉這些基礎(chǔ)的東西及其內(nèi)部的原理,將會(huì)使我們的Android開發(fā)變的容易、可以更好地架構(gòu)系統(tǒng)。在學(xué)習(xí)Android消息
    發(fā)表于 04-24 15:30 ?492次閱讀
    詳細(xì)解答<b class='flag-5'>Android</b>消息<b class='flag-5'>機(jī)制</b>

    Android開發(fā)手冊API函數(shù)詳解資料免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是Android開發(fā)手冊API函數(shù)詳解資料免費(fèi)下載。
    發(fā)表于 02-22 08:00 ?0次下載

    礦石收音機(jī)制詳解

    礦石收音機(jī)制詳解
    發(fā)表于 12-27 17:52 ?63次下載