日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 消息机制 Handler总结

發布時間:2024/9/30 Android 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 消息机制 Handler总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

老久就想著寫一篇 關于消息機制的文章來總結一下。

Android的消息機制主要是指Handler 的運行機制。我們在開發時有的時候需要在子線程進行耗時的I/o 操作,可能是讀取文件或者 訪問網絡等,有時候耗時工作完成需要在UI上做響應改變,又知道Android開發的規范限制,不能在子線程中訪問更新UI,不然會出現程序異常,這時候Hanlder擔任了這樣的角色,把更新UI的操作挪動到主線程中去操作。當然Handler并不是僅僅這點作用的。
Handler的運行需要底層 MessageQueue 和Looper的支持。
MessageQueue(消息隊列),采用單鏈表的數據結構來存儲的消息列表。不能處理消息。
Looper(消息循環),專門用來處理消息,Looper會以無限循環的形式去查找是否有新消息,有就立刻處理,沒有的話就一直輪詢等待。 Handler創建的時候會采用當前線程的Looper來構造消息循環系統。
ThreadLocal(特殊的Looper),首先他不是線程,他的作用就是在每個線程中存儲數據。ThreadLocal可以在不同的線程中互不干擾的存儲并提供數據,Handler通過Thread Local 獲取到每個線程的Looper,無論是主線程(UI線程(別名:ActivityThread,當主線程創建的時候,就會初始化Looper)),還是子線程。

為什么不允許子線程訪問UI?因為Android的線程是不安全的,如果在多線程中并發訪問極有可能導致UI控件狀態不可控,還有一種狀況。
可不可以加鎖機制呢? 最好不要用啦,但是會使訪問UI的效率降低很多,鎖機制會阻塞一些線程的執行,另外實現起訪問UI的邏輯變得很復雜。所以采用這種Handler單線程來處理,簡單,高效。

消息機制的流程

Handler 創建的時候,就會初始化Looper,不然會報異常:

java.long.RuntimeException:Can't create handler inside thread that has not called Looper.prepare();

Handler創建完之后,這時候內部的Looper和MessageQueue ,三者一起協同工作啦,
通過Handler 的send方法發送一個message,這個message會在Handler內部Looper處理,也可以通過Handler的post的方法將一個Runnable 投遞到Handler內部的Looper中去處理,(post方法的底層實現還是通過send方法實現投遞的)。
Handler的send方法被調用時,也會調用Message Queue 的嗯卻 u 俄 Message方法 將此消息放入消息隊列中,然后那個無限循環的Looper發現有隊列中有消息了,就會處理這個消息,消息中的Runnable 或者Handler的handlerMessage 方法被調用。此時的Looper是運行在Handler所創建的線程中。

Handler

  • Handler是為了解決子線程無法更新UI而引入的。
  • 在主線程中創建Handler對象,復寫handlerMessage方法更新UI。
  • 在子線程中創建Message對象將數據綁定,發送給Handler。
  • 可以通過Message里有個what變量,是int類型的,可以用來作標記,然后在handler里接收msg,通過標記判斷處理不同的msg
  • 代碼如下:
    在子線程中
    Message msg = Message.obtain();
    msg.obj = 要傳遞的數據;
    //msg.what = 1 ; //給msg打標記
    handler.sendMessage(msg);

    在主線程創建handler對象 復寫方法 接收msg 更新UI
    NewsAdapter adapter= (NewsAdapter) msg.obj;
    lv_news.setAdapter(adapter);

    Handler內存泄露

    解決辦法1:
    在Activity的onDestroy中通過removeCallbacks, removeMessages移除指定消息,或用removeCallbacksAndMessages(null)移除消息隊列中所有消息.

    解決辦法2:
    將Handler聲明為靜態

    public class SubActivity extends Activity {static class StaticHandler extends Handler {WeakReference mActivityReference;StaticHandler(Activity activity) {mActivityReference= new WeakReference(activity);}@Overridepublic void handleMessage(Message msg) {if ( mActivityReference!= null){final SubActivity activity = (SubActivity) mActivityReference.get();if (activity != null) {activity.getTextView().setText(測試);}}}} }

    最好是上述兩種辦法一起用

    ThreadLocal的原理

    ThreadLocal是一個線程內部的數據儲存類,通過它可以在指定線程中儲存數據(也只有這個指定線程能獲取到它儲存的數據),也就是說,它可以在不同線程中互不干擾的儲存數據

    通過它可以獲取每個線程的Looper。Handler創建時采用當前線程的Looper來構造消息循環系統,就是通過ThreadLocal來獲取當前線程的Looper

    使用方法

    private ThreadLocal mTL = new ThreadLocal<>();
    然后在不同線程中直接用mTL對象對T變量進行set(value)儲存或get()獲取即可
    不同線程訪問同一個ThreadLocal的get方法時,ThreadLocal內部會從各自線程中取出一個數組,然后再根據索引取值,顯然不同線程中的數組是不一樣的,所以互不干擾。

    使用場景

    * 當某些數據是以線程為作用域,并且不同線程具有不同的數據副本時,就可以采用ThreadLocal。

    例如Looper,它作用域就是線程,并且不同線程具有不同的Looper。

    * 又例如在復雜邏輯下的監聽器,如果需要監聽器貫穿整個線程,就可以用ThreadLocal讓監聽器成為線程的全局對象,在線程內只需通過 get即可拿到監聽器。

    如果不用ThreadLocal,就只有兩種方法:通過參數在方法間傳遞,該方法在邏輯復雜時不可接受;聲明監聽器為靜態,這種辦法只適用于一個線程,如果N個線程執行,那就需要N個靜態監聽器對象,也不好。

    原理

    ThreadLocal有個內部類:Values,它是專門用來儲存線程的ThreadLocal數據
    每個Thread的成員位置都聲明了一個Values
    ThreadLocal有個成員變量reference,它是對ThreadLocal自己的一個弱引用
    Values類內部成員位置有個Object數組table,用于儲存當前線程的數據

    當調用set時,先拿到當前線程對象Thread,然后取出當前線程的Values數據,如果為空就初始化一下,然后調用Values的put方法往table里儲存數據。put儲存數據時總是會把數據存到reference的后一個(例:如果把reference存到了table的index位置,那么就把數據存到index+1位置)

    get時,同樣是先取出當前Thread的values對象,如果為空就返回初始值(初始值由initialValues方法指定)。如果不為空,就查找reference在table中的位置,找到后取出位置+1的數據即是我們儲存的數據

    綜上可知,不同線程操作同一個ThreadLocal對象時,實際上整個操作過程都還是在各自線程內(都是操作各自線程的values對象)

    Handler的原理
    使用Handler時涉及到四個對象:

    * Handler: 主要用于在子線程中將Message發送給主線程(將Message壓入Message壓入MessageQueue中),在主線程中接受Message并處理。* Message:一個消息對象,主要在子線程中綁定數據,攜帶到主線程。arg1和arg2可以攜帶兩個int數據。obj可以攜帶一個任意對象。what用來定義標識* MessageQueue:每個線程最多只可以有一個。它是一個消息隊列,接受其他線程中發來的Message對象,主要操作是插入(enqueueMessage方法)和讀取(next方法),這兩種操作采用單鏈表的數據結構會很高效,所以它是用單鏈表來儲存消息。* Looper: 消息循環器(泵),每個Looper對象和一個線程關聯,不斷的從MessageQueue中取出一個個Message對象,取出后調用Handler.dispatchMessage進行消息分發,handleMessage進行消息處理。子線程默認沒有Looper,使用Handler的話需要手動創建并啟動Looper。可以通過quit()和quitSafely()打上退出循序的標記并退出循環,前者是直接退出,后者是會等到消息全部處理完再退出(這期間不接收新消息)。

    在子線程中開啟Looper后,處理完消息必須調用quit方法終止消息循環,然后線程就會終止,不然該子線程會一直處于等待狀態。

    流程

    * Looper構造時會創建MessageQueue對象,并將當前線程Thread對象儲存。子線程可以通過Looper.prepare()創建Looper,通過Looper.loop()開啟消息循環。* 調用loop()開啟循環后,Looper會循環調用MessageQueue的next方法來取出消息。next方法也是個死循環,沒有消息時會一直阻塞,于是Looper也跟著阻塞在這。當調用了quit或quitSafely標記為退出循環時,next方法會返回null,這時Looper才會終止循環。* 創建Handler時會采用當前線程的Looper來構建消息循環系統,然后就可以通過post/send發送消息* send和post方法最終都會調用sendMessageAtTime方法來發送消息,而post方法其實就是調用getPostMessage方法將Runnable對象儲存到msg的callback成員變量中然后也是send出去。sendMessageAtTime方法就是把當前handler對象存到msg的target中,然后往Queue中插入一條消息。* 然后調用MessageQueue的enqueueMessage方法讓消息入列* Looper取出消息,然后取出msg里的target(這個儲存的就是發送時用的handler),調用handler的dispatchMessage方法來分發消息。handler的dispatchMessage方法是在handler所在線程執行的。dispatchMessage方法如下: public void dispatchMessage(Message msg) {//第一種處理消息方式if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {//第二種處理消息方式if (mCallback.handleMessage(msg)) {return;}}//第三種處理消息方式handleMessage(msg);} }

    我們通過post發送消息時,就會執行第一種處理方法,這里的callback就是我們的runnable;handleCallback方法就是調用runnable的run方法;
    當我們創建Handler對象時,在構造中傳入Handler.Callback接口的實現時,就會采用第二種處理方法;這種構造Handler實例的方式是不會派生Handler子類的。
    我們實現Handler.Callback時,在其handleMessage方法最終返回false;或者構造Handler時用的無參構造,就會調用第三種處理方法,即我們派生Handler子類時重寫的handleMessage方法

    主線程內部也有個Handler,ActivityThread通過ApplicationThread與AMS通信,AMS完成請求后回調ApplicationThread的對應方法,然后ApplicationThread會發送消息到這個Handler,Handler收到消息后會將邏輯切換到ActivityThread中執行,即切換到了主線程。

    總結
    如果一個線程Thread想要使用Handler類來發送消息,就必須先調用Looper類中的prepare成員方法來做一些準備工作,然后調用Looper類中的loop方法啟動該線程的循環機制。Handler類把封裝成Message類的消息發送到Looper類中的消息隊列中,然后Looper類中的loop循環方法中分發消息,最后將相應的消息對象分發到相應的target handler類中去處理消息。
    由此我們知道:一個線程Thread中只能擁有一個Looper對象,且一個Looper對象中只能擁有一個MessageQueue消息隊列,但是一個Thread線程中可以有多個Handler對象,而多個Handler對象共享同一個MessageQueue消息隊列。

    備注:本文作者 經閱讀多本Android 相關書籍, 和代碼實踐 .自己總結.僅供學習參考.謝謝

    總結

    以上是生活随笔為你收集整理的Android 消息机制 Handler总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。