主頁 > 移動端開發 > Android Handler深度決議原始碼(一)

Android Handler深度決議原始碼(一)

2020-12-18 11:50:24 移動端開發

文章目錄

  • Hander 簡單使用
  • 原始碼分析
    • Looper
    • Message
    • Handler
    • MessageQueue
  • Handler的記憶體泄露

Hander 簡單使用

handler機制是執行緒間進行通信的一種簡單方式,在Android中常用于在子執行緒中請求網路資料,然后利用handler通知ui執行緒進行ui渲染,它的通信是單向的,只能一個執行緒進行接收,另外一個執行緒進行發送,下面的2個例子分別是從子執行緒給主執行緒發送訊息和從主執行緒給子執行緒發訊息

	 /**
     * 子執行緒往主執行緒發訊息
     */
    private fun work2UiHandler()
    {
        //注意 Handler的無參建構式已經在 SDK 30版本已經被廢棄了
        //用戶需要顯性的提供當前執行緒的Looper,這里我們提供的主執行緒的Looper
        val handler = object: Handler(Looper.getMainLooper())
        {
            override fun handleMessage(msg: Message)
            {
                when(msg.what)
                {
                    10001->Log.i("mainActivity","主執行緒更新 ---${msg.obj}")
                }
            }
        }
        Thread{
            //子執行緒進行耗時操作
            //這里得Message建議不要用new的方式去獲取,因為Message內部自己維護了一個快取的訊息池
            //當message 被處理完畢后,系統會自動回收,
            // 如果是自己new的Message,每次使用后,系統會放入到訊息池中,
            //沒有進行一個回收的程序,會占用記憶體,后面會有原始碼分析該訊息快取機制
            val message = Message.obtain()
            message.what = 10001
            message.obj = "這是子執行緒耗時獲取的資料"
            //通知主執行緒執行緒進行UI更新
            handler.sendMessage(message)
        }.start()
    }
 /**
     * 從主執行緒給子執行緒發訊息
     */
    private fun ui2WorkHandler()
    {
        val workThread = WorkThread()
        workThread.start()
        //等待作業的執行緒的run方法執行結束,即handler初始化完成
        Thread.sleep(1000)
        //通過handler獲取message 其內部最后還是呼叫的Message.obtainMessage()
        val message = workThread.mHandler.obtainMessage()
        message.what = 10002
        message.obj = "這是主執行緒發送的資料"
        workThread.mHandler.sendMessage(message)
    }

    class WorkThread:Thread()
    {
        lateinit var mHandler:Handler

        override fun run() {

            //為當前的作業執行緒初始化looper
            Looper.prepare()
            //初始化作業執行緒的的handler Looper.myLooper獲取的即為與當前執行緒系結的looper
            mHandler = object : Handler(Looper.myLooper()!!)
            {
                override fun handleMessage(msg: Message) {
                    when(msg.what)
                    {
                        10002->Log.i("mainActivity","子執行緒收到訊息 ---${msg.obj}")
                    }
                }
            }
            //looper的任務訊息佇列開始輪詢執行
            //后續原始碼會詳細分析
            Looper.loop()
        }
    }

原始碼分析

關于下面四個類之間的關系

<style>#mermaid-svg-NMDCnIx4e80jq0oz .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-NMDCnIx4e80jq0oz .label text{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz .node rect,#mermaid-svg-NMDCnIx4e80jq0oz .node circle,#mermaid-svg-NMDCnIx4e80jq0oz .node ellipse,#mermaid-svg-NMDCnIx4e80jq0oz .node polygon,#mermaid-svg-NMDCnIx4e80jq0oz .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-NMDCnIx4e80jq0oz .node .label{text-align:center;fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz .node.clickable{cursor:pointer}#mermaid-svg-NMDCnIx4e80jq0oz .arrowheadPath{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-NMDCnIx4e80jq0oz .flowchart-link{stroke:#333;fill:none}#mermaid-svg-NMDCnIx4e80jq0oz .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-NMDCnIx4e80jq0oz .edgeLabel rect{opacity:0.9}#mermaid-svg-NMDCnIx4e80jq0oz .edgeLabel span{color:#333}#mermaid-svg-NMDCnIx4e80jq0oz .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-NMDCnIx4e80jq0oz .cluster text{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-NMDCnIx4e80jq0oz .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-NMDCnIx4e80jq0oz text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-NMDCnIx4e80jq0oz .actor-line{stroke:grey}#mermaid-svg-NMDCnIx4e80jq0oz .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-NMDCnIx4e80jq0oz .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-NMDCnIx4e80jq0oz #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-NMDCnIx4e80jq0oz .sequenceNumber{fill:#fff}#mermaid-svg-NMDCnIx4e80jq0oz #sequencenumber{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz #crosshead path{fill:#333;stroke:#333}#mermaid-svg-NMDCnIx4e80jq0oz .messageText{fill:#333;stroke:#333}#mermaid-svg-NMDCnIx4e80jq0oz .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-NMDCnIx4e80jq0oz .labelText,#mermaid-svg-NMDCnIx4e80jq0oz .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-NMDCnIx4e80jq0oz .loopText,#mermaid-svg-NMDCnIx4e80jq0oz .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-NMDCnIx4e80jq0oz .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-NMDCnIx4e80jq0oz .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-NMDCnIx4e80jq0oz .noteText,#mermaid-svg-NMDCnIx4e80jq0oz .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-NMDCnIx4e80jq0oz .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-NMDCnIx4e80jq0oz .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-NMDCnIx4e80jq0oz .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-NMDCnIx4e80jq0oz .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz .section{stroke:none;opacity:0.2}#mermaid-svg-NMDCnIx4e80jq0oz .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-NMDCnIx4e80jq0oz .section2{fill:#fff400}#mermaid-svg-NMDCnIx4e80jq0oz .section1,#mermaid-svg-NMDCnIx4e80jq0oz .section3{fill:#fff;opacity:0.2}#mermaid-svg-NMDCnIx4e80jq0oz .sectionTitle0{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz .sectionTitle1{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz .sectionTitle2{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz .sectionTitle3{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-NMDCnIx4e80jq0oz .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz .grid path{stroke-width:0}#mermaid-svg-NMDCnIx4e80jq0oz .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-NMDCnIx4e80jq0oz .task{stroke-width:2}#mermaid-svg-NMDCnIx4e80jq0oz .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz .taskText:not([font-size]){font-size:11px}#mermaid-svg-NMDCnIx4e80jq0oz .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-NMDCnIx4e80jq0oz .task.clickable{cursor:pointer}#mermaid-svg-NMDCnIx4e80jq0oz .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-NMDCnIx4e80jq0oz .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-NMDCnIx4e80jq0oz .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-NMDCnIx4e80jq0oz .taskText0,#mermaid-svg-NMDCnIx4e80jq0oz .taskText1,#mermaid-svg-NMDCnIx4e80jq0oz .taskText2,#mermaid-svg-NMDCnIx4e80jq0oz .taskText3{fill:#fff}#mermaid-svg-NMDCnIx4e80jq0oz .task0,#mermaid-svg-NMDCnIx4e80jq0oz .task1,#mermaid-svg-NMDCnIx4e80jq0oz .task2,#mermaid-svg-NMDCnIx4e80jq0oz .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-NMDCnIx4e80jq0oz .taskTextOutside0,#mermaid-svg-NMDCnIx4e80jq0oz .taskTextOutside2{fill:#000}#mermaid-svg-NMDCnIx4e80jq0oz .taskTextOutside1,#mermaid-svg-NMDCnIx4e80jq0oz .taskTextOutside3{fill:#000}#mermaid-svg-NMDCnIx4e80jq0oz .active0,#mermaid-svg-NMDCnIx4e80jq0oz .active1,#mermaid-svg-NMDCnIx4e80jq0oz .active2,#mermaid-svg-NMDCnIx4e80jq0oz .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-NMDCnIx4e80jq0oz .activeText0,#mermaid-svg-NMDCnIx4e80jq0oz .activeText1,#mermaid-svg-NMDCnIx4e80jq0oz .activeText2,#mermaid-svg-NMDCnIx4e80jq0oz .activeText3{fill:#000 !important}#mermaid-svg-NMDCnIx4e80jq0oz .done0,#mermaid-svg-NMDCnIx4e80jq0oz .done1,#mermaid-svg-NMDCnIx4e80jq0oz .done2,#mermaid-svg-NMDCnIx4e80jq0oz .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-NMDCnIx4e80jq0oz .doneText0,#mermaid-svg-NMDCnIx4e80jq0oz .doneText1,#mermaid-svg-NMDCnIx4e80jq0oz .doneText2,#mermaid-svg-NMDCnIx4e80jq0oz .doneText3{fill:#000 !important}#mermaid-svg-NMDCnIx4e80jq0oz .crit0,#mermaid-svg-NMDCnIx4e80jq0oz .crit1,#mermaid-svg-NMDCnIx4e80jq0oz .crit2,#mermaid-svg-NMDCnIx4e80jq0oz .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-NMDCnIx4e80jq0oz .activeCrit0,#mermaid-svg-NMDCnIx4e80jq0oz .activeCrit1,#mermaid-svg-NMDCnIx4e80jq0oz .activeCrit2,#mermaid-svg-NMDCnIx4e80jq0oz .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-NMDCnIx4e80jq0oz .doneCrit0,#mermaid-svg-NMDCnIx4e80jq0oz .doneCrit1,#mermaid-svg-NMDCnIx4e80jq0oz .doneCrit2,#mermaid-svg-NMDCnIx4e80jq0oz .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-NMDCnIx4e80jq0oz .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-NMDCnIx4e80jq0oz .milestoneText{font-style:italic}#mermaid-svg-NMDCnIx4e80jq0oz .doneCritText0,#mermaid-svg-NMDCnIx4e80jq0oz .doneCritText1,#mermaid-svg-NMDCnIx4e80jq0oz .doneCritText2,#mermaid-svg-NMDCnIx4e80jq0oz .doneCritText3{fill:#000 !important}#mermaid-svg-NMDCnIx4e80jq0oz .activeCritText0,#mermaid-svg-NMDCnIx4e80jq0oz .activeCritText1,#mermaid-svg-NMDCnIx4e80jq0oz .activeCritText2,#mermaid-svg-NMDCnIx4e80jq0oz .activeCritText3{fill:#000 !important}#mermaid-svg-NMDCnIx4e80jq0oz .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-NMDCnIx4e80jq0oz g.classGroup text .title{font-weight:bolder}#mermaid-svg-NMDCnIx4e80jq0oz g.clickable{cursor:pointer}#mermaid-svg-NMDCnIx4e80jq0oz g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-NMDCnIx4e80jq0oz g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-NMDCnIx4e80jq0oz .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-NMDCnIx4e80jq0oz .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-NMDCnIx4e80jq0oz .dashed-line{stroke-dasharray:3}#mermaid-svg-NMDCnIx4e80jq0oz #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz .commit-id,#mermaid-svg-NMDCnIx4e80jq0oz .commit-msg,#mermaid-svg-NMDCnIx4e80jq0oz .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-NMDCnIx4e80jq0oz g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-NMDCnIx4e80jq0oz g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-NMDCnIx4e80jq0oz g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-NMDCnIx4e80jq0oz .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-NMDCnIx4e80jq0oz .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-NMDCnIx4e80jq0oz .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-NMDCnIx4e80jq0oz .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-NMDCnIx4e80jq0oz .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-NMDCnIx4e80jq0oz .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-NMDCnIx4e80jq0oz .edgeLabel text{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-NMDCnIx4e80jq0oz .node circle.state-start{fill:black;stroke:black}#mermaid-svg-NMDCnIx4e80jq0oz .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-NMDCnIx4e80jq0oz #statediagram-barbEnd{fill:#9370db}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-state .divider{stroke:#9370db}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-NMDCnIx4e80jq0oz .note-edge{stroke-dasharray:5}#mermaid-svg-NMDCnIx4e80jq0oz .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-NMDCnIx4e80jq0oz .error-icon{fill:#522}#mermaid-svg-NMDCnIx4e80jq0oz .error-text{fill:#522;stroke:#522}#mermaid-svg-NMDCnIx4e80jq0oz .edge-thickness-normal{stroke-width:2px}#mermaid-svg-NMDCnIx4e80jq0oz .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-NMDCnIx4e80jq0oz .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-NMDCnIx4e80jq0oz .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-NMDCnIx4e80jq0oz .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-NMDCnIx4e80jq0oz .marker{fill:#333}#mermaid-svg-NMDCnIx4e80jq0oz .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;}</style> <style>#mermaid-svg-NMDCnIx4e80jq0oz { color: rgba(0, 0, 0, 0.75); font: ; }</style> Handler Looper MessageQueue Message.obtainMessage() sendMesssage() message存入佇列 Looper.loop() 輪詢佇列 取出message handlerMessager() Handler Looper MessageQueue

從上面的關系可以看出,Looper是Handler和MessageQueue中間橋梁,資料載體是Message, MessageQueue存盤Handler發送過來的Message,Handler會持有Looper,Looper會持有MessageQueue

Looper

public class Looper
{
    //每一個執行緒 都會關聯一個ThreadLocalMap<ThreadLocal,Looper> 該map中存盤的是ThreadLocal-->Looper的鍵值對
    //即 一個thread 對應 一個 ThreadLocalMap,但是map中存盤的ThreadLocal均為同一個,Looper就不同
    //總結即 一個thread 至多只能系結一個Looper(也可為null,即當前執行緒未系結Looper,需呼叫Looper.perpare()進行執行緒系結)
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    //主執行緒系結的Looper
    private static Looper sMainLooper;  // guarded by Looper.class

    //Looper與之關聯的訊息佇列
    final MessageQueue mQueue;

    //針對訊息分發的前后回呼通知
    private static Observer sObserver;

    //這里可以進行一個全域的設定,去回呼監聽訊息分發的全程序
    //但是這個方法被影藏了,如果有需要可以進行Hook二次代理即可
    @hide
    public static void setObserver(@Nullable Observer observer) {
        sObserver = observer;
    }

    private static void prepare(boolean quitAllowed) {
        //呼叫該方法時,會判斷當前的執行緒是否已經系結了Looper,
        //sThreadLocal.get()不為null則為當前執行緒是已經系結了一個Looper,此時會拋出例外
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //實體化Looper并將其與當前執行緒進行系結,即將looper存入與執行緒想關聯的ThreadLocalMap中
        //具體原始碼本章節不做過多決議
        sThreadLocal.set(new Looper(quitAllowed));
    }

    public static @Nullable Looper myLooper() {
        //獲取當前執行緒系結的looper,可能為null,為null則為當前執行緒未系結looper
        return sThreadLocal.get();
    }

    //剛方法會在ActivityThread的main方法中被呼叫,即為主執行緒系結了Looper,開發日常所有回呼到Ui執行緒的looper
    //都是 sMainLooper
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

     //開始輪詢訊息佇列,處理訊息
     public static void loop() {
        //獲取當前的執行緒的looper
        final Looper me = myLooper();
        ...
        //獲取looper所關聯的messageQueue
        final MessageQueue queue = me.mQueue;
        ...
        //輪詢佇列
        for (;;) {
            //取出訊息,當無訊息時,該執行緒會處于一個阻塞的狀態
            Message msg = queue.next(); // might block
            ...
             if (observer != null) {
                //分發訊息開始
                token = observer.messageDispatchStarting();
            }
            ...
            try {
                //msg.target所代表的目標是 handler,分析Message時會講到
                //這里則是handler分發處理訊息起始點
                msg.target.dispatchMessage(msg);
                //分發訊息結束
                observer.messageDispatched(token, msg);
            } catch (Exception exception) {
                //分發的當次訊息拋出了例外
                observer.dispatchingThrewException(token, msg, exception);
            }
            ...
            //回收訊息
            msg.recycleUnchecked();
        }
    }
}

Message

public class Message
{
    //代表Message的標識
    public int what;

    public int arg1;

    public int arg2;

    public Object obj;

    //handler發送訊息時,會與message進行一個系結,該值即為當前的handler
    //這個target很重要
    Handler target;

    //可賦值,代表Message被分發處理時,會被執行的一個任務
    Runnable callback;

    //Message在鏈表結構中的的下一個節點
    //該快取池是以單鏈表的方式存盤的
    Message next;

    //Message快取池(單鏈表的頭指標)
    private static Message sPool;

    //快取池的初始大小
    private static int sPoolSize = 0;

    //快取池的最大容量
    private static final int MAX_POOL_SIZE = 50;

    public static Message obtain() {
        synchronized (sPoolSync) {
            //初始快取池為null,會直接new Message回傳實體物件
            if (sPool != null) {
                //當該鏈表的頭指標不為null,即當前快取池中已經有了可以重新回收的message物件
                //這里取出頭指標
                Message m = sPool;
                //將當前的頭指標指向下一個節點
                sPool = m.next;
                //取出的頭指標的next置null
                m.next = null;
                //size-1
                sPoolSize--;
                //將頭指標回傳出去,即從快取池中取出了一個已經回收的Message物件
                return m;
            }
        }
        return new Message();
    }

    void recycleUnchecked() {
        //清除Message攜帶的所有資料
        ...
        synchronized (sPoolSync) {
            //如果當前的快取池大小 未達到最大容量,會將該回收的message置于快取池中(單鏈表)
            //頭插法
            if (sPoolSize < MAX_POOL_SIZE) {
                //尾指標指向下個節點
                next = sPool;
                //當前節點為頭指標
                sPool = this;
                //size+1
                sPoolSize++;
                //當下一個Message(這里指 msgB物件)又被回收后,msgB的next指向上一個節點,頭指標指向當前的msgB物件
                //以此類推,后面每一個被回收的message都會被插入到鏈表的最前端
            }
        }
    }

    //利用message系結的handler直接去發送訊息
    public void sendToTarget() {
        target.sendMessage(this);
    }

    //這里也是獲取一個快取池中的Message,只不多了handler和callback
    //這么做的目的是直接將兩者系結到message中去,往往在獲取到該Message后可以直接去呼叫sendToTarget()發送訊息
    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

}

Handler

public class Handler
{
    //與handler系結的looper,一一對應
    final Looper mLooper;

    //looper中的訊息佇列
    final MessageQueue mQueue;

    //回呼函式
    final Callback mCallback;


    //沒有傳入looper時,會呼叫Looper.myLooper()獲取與當前執行緒系結的looper
    public Handler(@Nullable Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();
        ...
        //將looper中訊息佇列賦值
        mQueue = mLooper.mQueue;
        //handler的回呼函式,在dispatchMessage()分發訊息方法中會被呼叫
        mCallback = callback;
        mAsynchronous = async;
    }


    //通過Runnable 獲取message 將runnable任務與message系結,最后也會在dispatchMessage()分發訊息方法中會被呼叫
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    //所有的post的方法 最后都會走到sendMessageAtTime
    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //SystemClock.uptimeMillis() + delayMillis 是否延遲執行 即在定點的時間上去執行
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        ...
        //將訊息放入訊息佇列中
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        //將msg的target與當前handler進行一個一對一系結
        msg.target = this;
        ...
        //入佇列
        return queue.enqueueMessage(msg, uptimeMillis);
    }

    //handler的回呼函式
    public interface Callback {
        boolean handleMessage(@NonNull Message msg);
    }

    //handler的處理訊息邏輯,用戶需要自己重寫
    public void handleMessage(@NonNull Message msg) {
    }

    //該方法很重要
    public void dispatchMessage(@NonNull Message msg) {
        //該方法在Looper.loop()中輪詢佇列時,取出訊息后
        // message.target.dispatchMessage(message) 執行
        //這里就是Message的callback不為null時會執行處理訊息
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //當Message的callback不去消費處理訊息
            //handler自身的回呼函式會首先去處理訊息
            if (mCallback != null) {
                //如果handler的mCallBack.handleMessage方法去消費處理訊息時
                //回傳的是true則handler自身的handleMessage方法就不會被執行
                //回傳的是false是,就還會去執行handleMessage
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
            //總結即:Message的callback最先被執行,然后才是handler自身的CallBack回呼函式執行
            //而handler的handleMessage方法則需要看CallBack中函式回傳值來決定是否執行
        }
    }
}

MessageQueue

/**
* 負責管理訊息佇列,實際上Message類有一個next欄位,
* 會將Message物件串在一起成為一個訊息佇列,
* 所以并不需要LinkedList之類的資料結構將Message物件組在一起成為佇列,
*/
public class MessageQueue
{
    //native層需要用的句柄
    private long mPtr;

   //native層去執行一個訊息佇列的初始化
    private native static long nativeInit();

    //該程序是一個阻塞的操作
    private native void nativePollOnce(long ptr, int timeoutMillis);

    MessageQueue(boolean quitAllowed) {
        //允許退出
        mQuitAllowed = quitAllowed;
        //初始化獲取句柄
        mPtr = nativeInit();
    }

    /**
    *
    * 用于獲取下一個Message物件,如果沒有需要處理的Message物件,該方法將阻塞,
    * MessageQueue用本地方法做同步互斥,因為這樣時間更精準,
    * 每個Message物件都有一個什么時刻處理該Message物件的屬性when,
    * 沒到時間都不會處理該Message物件,如果時間不精準的話,會導致系統訊息不能及時處理,
    */
    Message next() {
        //具體代碼不做過多分析,后續會出一個新章節來專門分析 MessageQueue的實作原理
       ...
    }

    /**
    * 佇列中的Message觸發時間是有先后順序的,
    * 當訊息加入訊息佇列時,會從佇列頭開始遍歷,直到找到訊息應該插入的合適位置,
    * 以保證所有訊息的時間順序(內部遍歷佇列中Message,找到when比當前Message的when大的Message,
    * 將Message插入到該Message之前,如果沒找到則將Message插入到佇列最后),
    * 一般是當前佇列為空的情況下,
    * next那邊會進入睡眠,需要的時候MessageQueue這邊會喚醒next方法,
    */
    boolean enqueueMessage(Message msg, long when)
    {
      //具體代碼不做過多分析,后續會出一個新章節來專門分析 MessageQueue的實作原理
        ...
    }
}

Handler的記憶體泄露

  private val mHandler = object :Handler(Looper.getMainLooper()){
        override fun handleMessage(msg: Message) {
            Log.i("HandlerLinkActivity","處理訊息")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //延時2分鐘發送一個空訊息
        mHandler.sendEmptyMessageDelayed(100086,2*60*1000)
    }

這里在Activity中延遲2分鐘去發送一個訊息,然后再這兩分鐘內,按回傳鍵去關閉Activity此時就會存在一個記憶體泄露的現象,造成記憶體泄露的原因時,mHandler是一個匿名的內部類,它會持有一個外部內的參考即當前Activity的參考,當gc時,發現Activity還在被參考時,就無法去回收記憶體

解決方法:

 /**
     * 避免handle記憶體泄露的辦法
     * 1.使用static 修飾的handler,但是一般會弱參考activity物件,因為要使用activity物件中的成員
     * 2.單獨定義handler,同樣可以弱參考activity
     * 3.使用內部類的handler,在onDestroy方法中removeCallbacksAndMessages
     */
    class StaticHandler(activity: HandlerLinkActivity) :Handler(Looper.getMainLooper())
    {
        var mWeakReference: WeakReference<HandlerLinkActivity> = WeakReference(activity)


        override fun handleMessage(msg: Message) {
            mWeakReference.get().run {
                //執行更新ui
            }
        }
    }

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/236624.html

標籤:其他

上一篇:Harmony OS 打包

下一篇:親測有效,關于Android出現the emulator process for Android was killed.

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more