Android Handler機(jī)制

看了多位大神的源碼解析千劈,為了加深自己的理解,遂將自己跟走一遍的流程記錄于此

Handler機(jī)制:一種異步消息處理機(jī)制牌捷,在項(xiàng)目中常用于更新主線程UI墙牌,也可在項(xiàng)目中做一些延時(shí)等操作

我們先看一下項(xiàng)目中如何使用

class SecondActivity :AppCompatActivity(){
    var mTextView:TextView? = null
    private val mHandler = MHandler(WeakReference(this))

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mTextView = findViewById(R.id.tv)
        val btn = findViewById<Button>(R.id.btn)
        btn.setOnClickListener {
            Thread(Runnable {
                val msg = Message()
                msg.what = 1
                msg.obj = "it's time"
                mHandler.sendMessage(msg)
            }).start()
        }
    }

    internal class MHandler(reference: WeakReference<SecondActivity>) : Handler() {

        private var activity: SecondActivity? = null

        init {
            activity = reference.get()
        }

        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            if(msg.what == 1){
                val obj:String = msg.obj as String
                activity?.mTextView?.text = obj
            }
        }
    }
}

Handler機(jī)制的實(shí)現(xiàn)有幾個(gè)重要的組成:

  • Message:消息體,從上面的例子中我們可以看到暗甥,這個(gè)即我們需要傳遞的消息
  • Handler:消息的傳遞者和處理者喜滨,在上方的例子中,通過handler.sendMessage發(fā)送消息撤防,在handlermessage中處理消息
  • Looper:輪詢器虽风,不斷從消息隊(duì)列中取出消息
  • MessageQueue:消息隊(duì)列,采用單鏈表結(jié)構(gòu)存儲消息,便于插入和刪除消息

了解了這幾個(gè)之后焰情,我們從源碼走一遍消息發(fā)送的流程

  1. 首先進(jìn)入到handler的構(gòu)造方法中
 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) {
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

從代碼中我們能看到handler構(gòu)造函數(shù)需要一個(gè)looper對象陌凳,而looper對象需要我們自己調(diào)用Looper.prepare()來構(gòu)建

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

從代碼中我們能看到一個(gè)線程只能有一個(gè)looper對象
我們在主線程創(chuàng)建handler不需要先創(chuàng)建該對象是由于在main函數(shù)中已經(jīng)由系統(tǒng)幫我們創(chuàng)建了


public static void main(String[] args) {
   //
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
 //
    Looper.loop();
}
public static final void prepareMainLooper() {
    prepare();
    setMainLooper(myLooper());
    if (Process.supportsProcesses()) {
        myLooper().mQueue.mQuitAllowed = false;
    }
}
  1. 將消息加入到消息隊(duì)列
 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

可以看到最終會進(jìn)入到sendMessageAtTime方法中,我們再往下走

 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);
    }

其中的queue即是我們在創(chuàng)建looper時(shí)創(chuàng)建的内舟,到了這一步之后就是調(diào)用MessagqQueue的方法將消息加入到消息隊(duì)列中

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

我們可以看到消息隊(duì)列是按照時(shí)間通過指定下一個(gè)next來完成入隊(duì)的操作,當(dāng)我們使用sendMessageAtFrountOfQueue()發(fā)送消息初橘,msg.when = 0验游,就會添加到消息隊(duì)列頭部

  1. 將消息從消息隊(duì)列中取出
  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;
//
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
//
        //
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            msg.recycleUnchecked();
        }
    }

代碼挺多,留下了重要的幾行保檐,我們能看到在loop方法中是一個(gè)死循環(huán)耕蝉,我們調(diào)用next()方法從消息隊(duì)列中取出消息,當(dāng)有消息時(shí)會通過msg.what.dispatchMessage來進(jìn)行分發(fā)夜只,而msg.what我們在之前的handler的enqueueMessage方法中能發(fā)現(xiàn)就是發(fā)送消息的handler垒在,這樣來分發(fā)到對應(yīng)的處理者

  1. 處理消息
    我們再回到handler的方法中
 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

我們能看到有幾個(gè)不同的回調(diào)遭笋,這些之后會說到呈枉。這里我們看到最后一行代碼handleMessage()該方法就是我們在activity中進(jìn)行實(shí)現(xiàn)的方法。至此一套完整的消息傳遞粗粗走完湾揽。
由于Handler是依附于創(chuàng)建的線程旅挤,所以此后我們的handlermessage方法處于主線程中踢关,可以進(jìn)行ui操作
盜取郭神示意圖:

郭神示意圖

再看下其他幾個(gè)子線程更新ui的方法

  • handler的post方法
btn.setOnClickListener {
            Thread(Runnable {
                mHandler.post { mTextView?.text = "it's time"}
            }).start()
        }

往下走一會

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

能看到只是將runnable對象作為msg進(jìn)行消息傳遞,和之前沒有什么區(qū)別

  private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

由此我們聯(lián)想到dispatchMessage中的callback中的來源

  • View的post方法
 btn.setOnClickListener {
            Thread(Runnable {
                mTextView?.post { mTextView?.text = "it's time" }
            }).start()
        }

往里看下

  public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

能看到調(diào)用的是handler的post方法

  • runOnUiThread()
btn.setOnClickListener {
            Thread(Runnable {
                runOnUiThread {
                    mTextView?.text = "it's time"
                }
            }).start()
        }

往里看看

 public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

也是調(diào)用了handler的post方法

粗粗走過粘茄,未求甚解~~~

https://blog.csdn.net/guolin_blog/article/details/9991569

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末签舞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子柒瓣,更是在濱河造成了極大的恐慌儒搭,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芙贫,死亡現(xiàn)場離奇詭異搂鲫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)屹培,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門默穴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人褪秀,你說我怎么就攤上這事蓄诽。” “怎么了媒吗?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵仑氛,是天一觀的道長。 經(jīng)常有香客問我,道長锯岖,這世上最難降的妖魔是什么介袜? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮出吹,結(jié)果婚禮上遇伞,老公的妹妹穿的比我還像新娘。我一直安慰自己捶牢,他們只是感情好鸠珠,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著秋麸,像睡著了一般渐排。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灸蟆,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天驯耻,我揣著相機(jī)與錄音,去河邊找鬼炒考。 笑死可缚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的票腰。 我是一名探鬼主播城看,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼杏慰!你這毒婦竟也來了测柠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤缘滥,失蹤者是張志新(化名)和其女友劉穎轰胁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朝扼,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赃阀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了擎颖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榛斯。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖搂捧,靈堂內(nèi)的尸體忽然破棺而出驮俗,到底是詐尸還是另有隱情,我是刑警寧澤允跑,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布王凑,位于F島的核電站搪柑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏索烹。R本人自食惡果不足惜工碾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望百姓。 院中可真熱鬧渊额,春花似錦、人聲如沸垒拢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽子库。三九已至,卻和暖如春矗晃,著一層夾襖步出監(jiān)牢的瞬間仑嗅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工张症, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仓技,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓俗他,卻偏偏與公主長得像脖捻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子兆衅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容