国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

06.Android之消息機(jī)制問(wèn)題

Aomine / 3065人閱讀

摘要:通過(guò)向消息池發(fā)送各種消息事件通過(guò)處理相應(yīng)的消息事件。子線程往消息隊(duì)列發(fā)送消息,并且往管道文件寫數(shù)據(jù),主線程即被喚醒,從管道文件讀取數(shù)據(jù),主線程被喚醒只是為了讀取消息,當(dāng)消息讀取完畢,再次睡眠。

目錄介紹

6.0.0.1 談?wù)勏C(jī)制Hander作用?有哪些要素?流程是怎樣的?

6.0.0.2 為什么一個(gè)線程只有一個(gè)Looper、只有一個(gè)MessageQueue,可以有多個(gè)Handler?

6.0.0.3 可以在子線程直接new一個(gè)Handler嗎?會(huì)出現(xiàn)什么問(wèn)題,那該怎么做?

6.0.0.4 Looper.prepare()能否調(diào)用兩次或者多次,會(huì)出現(xiàn)什么情況?

6.0.0.5 為什么系統(tǒng)不建議在子線程訪問(wèn)UI,不對(duì)UI控件的訪問(wèn)加上鎖機(jī)制的原因?

6.0.0.6 如何獲取當(dāng)前線程的Looper?是怎么實(shí)現(xiàn)的?(理解ThreadLocal)

6.0.0.7 Looper.loop是一個(gè)死循環(huán),拿不到需要處理的Message就會(huì)阻塞,那在UI線程中為什么不會(huì)導(dǎo)致ANR?

6.0.0.8 Handler.sendMessageDelayed()怎么實(shí)現(xiàn)延遲的?結(jié)合Looper.loop()循環(huán)中,Message=messageQueue.next()和MessageQueue.enqueueMessage()分析。

6.0.0.9 Message可以如何創(chuàng)建?哪種效果更好,為什么?

6.0.1.3 使用Hanlder的postDealy()后消息隊(duì)列會(huì)發(fā)生什么變化?

6.0.1.4 ThreadLocal有什么作用?

好消息

博客筆記大匯總【15年10月到至今】,包括Java基礎(chǔ)及深入知識(shí)點(diǎn),Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時(shí)開(kāi)發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長(zhǎng)期更新維護(hù)并且修正,持續(xù)完善……開(kāi)源的文件是markdown格式的!同時(shí)也開(kāi)源了生活博客,從12年起,積累共計(jì)500篇[近100萬(wàn)字],將會(huì)陸續(xù)發(fā)表到網(wǎng)上,轉(zhuǎn)載請(qǐng)注明出處,謝謝!

鏈接地址:https://github.com/yangchong2...

如果覺(jué)得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬(wàn)事起于忽微,量變引起質(zhì)變!所有的筆記將會(huì)更新到GitHub上,同時(shí)保持更新,歡迎同行提出或者push不同的看法或者筆記!

6.0.0.1 談?wù)勏C(jī)制Hander作用?有哪些要素?流程是怎樣的?

作用:

跨線程通信。當(dāng)子線程中進(jìn)行耗時(shí)操作后需要更新UI時(shí),通過(guò)Handler將有關(guān)UI的操作切換到主線程中執(zhí)行。

四要素:

Message(消息):需要被傳遞的消息,其中包含了消息ID,消息處理對(duì)象以及處理的數(shù)據(jù)等,由MessageQueue統(tǒng)一列隊(duì),最終由Handler處理。技術(shù)博客大總結(jié)

MessageQueue(消息隊(duì)列):用來(lái)存放Handler發(fā)送過(guò)來(lái)的消息,內(nèi)部通過(guò)單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表,等待Looper的抽取。

Handler(處理者):負(fù)責(zé)Message的發(fā)送及處理。通過(guò) Handler.sendMessage() 向消息池發(fā)送各種消息事件;通過(guò) Handler.handleMessage() 處理相應(yīng)的消息事件。

Looper(消息泵):通過(guò)Looper.loop()不斷地從MessageQueue中抽取Message,按分發(fā)機(jī)制將消息分發(fā)給目標(biāo)處理者。

具體流程

Handler.sendMessage()發(fā)送消息時(shí),會(huì)通過(guò)MessageQueue.enqueueMessage()向MessageQueue中添加一條消息;

通過(guò)Looper.loop()開(kāi)啟循環(huán)后,不斷輪詢調(diào)用MessageQueue.next();

調(diào)用目標(biāo)Handler.dispatchMessage()去傳遞消息,目標(biāo)Handler收到消息后調(diào)用Handler.handlerMessage()處理消息。

6.0.0.2 為什么一個(gè)線程只有一個(gè)Looper、只有一個(gè)MessageQueue,可以有多個(gè)Handler?

注意:一個(gè)Thread只能有一個(gè)Looper,可以有多個(gè)Handler

Looper有一個(gè)MessageQueue,可以處理來(lái)自多個(gè)Handler的Message;MessageQueue有一組待處理的Message,這些Message可來(lái)自不同的Handler;Message中記錄了負(fù)責(zé)發(fā)送和處理消息的Handler;Handler中有Looper和MessageQueue。

為什么一個(gè)線程只有一個(gè)Looper?技術(shù)博客大總結(jié)

需使用Looper的prepare方法,Looper.prepare()??梢钥聪略创a,Android中一個(gè)線程最多僅僅能有一個(gè)Looper,若在已有Looper的線程中調(diào)用Looper.prepare()會(huì)拋出RuntimeException(“Only one Looper may be created per thread”)。

所以一個(gè)線程只有一個(gè)Looper,不知道這樣解釋是否合理!更多可以查看我的博客匯總:https://github.com/yangchong2...

public static void prepare() {
    prepare(true);
}

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

6.0.0.3 可以在子線程直接new一個(gè)Handler嗎?會(huì)出現(xiàn)什么問(wèn)題,那該怎么做?

不同于主線程直接new一個(gè)Handler,由于子線程的Looper需要手動(dòng)去創(chuàng)建,在創(chuàng)建Handler時(shí)需要多一些方法:

Handler的工作是依賴于Looper的,而Looper(與消息隊(duì)列)又是屬于某一個(gè)線程(ThreadLocal是線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類,通過(guò)它可以在指定線程中存儲(chǔ)數(shù)據(jù),其他線程則無(wú)法獲取到),其他線程不能訪問(wèn)。因此Handler就是間接跟線程是綁定在一起了。因此要使用Handler必須要保證Handler所創(chuàng)建的線程中有Looper對(duì)象并且啟動(dòng)循環(huán)。因?yàn)樽泳€程中默認(rèn)是沒(méi)有Looper的,所以會(huì)報(bào)錯(cuò)。

正確的使用方法是:技術(shù)博客大總結(jié)

handler = null;
new Thread(new Runnable() {
   private Looper mLooper;
   @Override
   public void run() {
       //必須調(diào)用Looper的prepare方法為當(dāng)前線程創(chuàng)建一個(gè)Looper對(duì)象,然后啟動(dòng)循環(huán)
       //prepare方法中實(shí)質(zhì)是給ThreadLocal對(duì)象創(chuàng)建了一個(gè)Looper對(duì)象
       //如果當(dāng)前線程已經(jīng)創(chuàng)建過(guò)Looper對(duì)象了,那么會(huì)報(bào)錯(cuò)
       Looper.prepare();
       handler = new Handler();
       //獲取Looper對(duì)象
       mLooper = Looper.myLooper();
       //啟動(dòng)消息循環(huán)
       Looper.loop();
       //在適當(dāng)?shù)臅r(shí)候退出Looper的消息循環(huán),防止內(nèi)存泄漏
       mLooper.quit();
   }
}).start();

主線程中默認(rèn)是創(chuàng)建了Looper并且啟動(dòng)了消息的循環(huán)的,因此不會(huì)報(bào)錯(cuò):應(yīng)用程序的入口是ActivityThread的main方法,在這個(gè)方法里面會(huì)創(chuàng)建Looper,并且執(zhí)行Looper的loop方法來(lái)啟動(dòng)消息的循環(huán),使得應(yīng)用程序一直運(yùn)行。

6.0.0.4 Looper.prepare()能否調(diào)用兩次或者多次,會(huì)出現(xiàn)什么情況?

Looper.prepare()方法源碼分析

可以看到Looper中有一個(gè)ThreadLocal成員變量,熟悉JDK的同學(xué)應(yīng)該知道,當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本。

public static void prepare() {
    prepare(true);
}

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

思考:Looper.prepare()能否調(diào)用兩次或者多次

如果運(yùn)行,則會(huì)報(bào)錯(cuò),并提示prepare中的Excetion信息。由此可以得出在每個(gè)線程中Looper.prepare()能且只能調(diào)用一次

技術(shù)博客大總結(jié)

//這里L(fēng)ooper.prepare()方法調(diào)用了兩次
Looper.prepare();
Looper.prepare();
Handler mHandler = new Handler() {
   @Override
   public void handleMessage(Message msg) {
       if (msg.what == 1) {
          Log.i(TAG, "在子線程中定義Handler,并接收到消息。。。");
       }
   }
};
Looper.loop();

6.0.0.5 為什么系統(tǒng)不建議在子線程訪問(wèn)UI,不對(duì)UI控件的訪問(wèn)加上鎖機(jī)制的原因?

為什么系統(tǒng)不建議在子線程訪問(wèn)UI

系統(tǒng)不建議在子線程訪問(wèn)UI的原因是,UI控件非線程安全,在多線程中并發(fā)訪問(wèn)可能會(huì)導(dǎo)致UI控件處于不可預(yù)期的狀態(tài)。

不對(duì)UI控件的訪問(wèn)加上鎖機(jī)制的原因

上鎖會(huì)讓UI控件變得復(fù)雜和低效

上鎖后會(huì)阻塞某些進(jìn)程的執(zhí)行技術(shù)博客大總結(jié)

6.0.0.7 Looper.loop是一個(gè)死循環(huán),拿不到需要處理的Message就會(huì)阻塞,那在UI線程中為什么不會(huì)導(dǎo)致ANR?

問(wèn)題描述

在處理消息的時(shí)候使用了Looper.loop()方法,并且在該方法中進(jìn)入了一個(gè)死循環(huán),同時(shí)Looper.loop()方法是在主線程中調(diào)用的,那么為什么沒(méi)有造成阻塞呢?

ActivityThread中main方法

ActivityThread類的注釋上可以知道這個(gè)類管理著我們平常所說(shuō)的主線程(UI線程)

首先 ActivityThread 并不是一個(gè) Thread,就只是一個(gè) final 類而已。我們常說(shuō)的主線程就是從這個(gè)類的 main 方法開(kāi)始,main 方法很簡(jiǎn)短

public static final void main(String[] args) {
    ...
    //創(chuàng)建Looper和MessageQueue
    Looper.prepareMainLooper();
    ...
    //輪詢器開(kāi)始輪詢
    Looper.loop();
    ...
}

Looper.loop()方法無(wú)限循環(huán)

看看Looper.loop()方法無(wú)限循環(huán)部分的代碼

while (true) {
   //取出消息隊(duì)列的消息,可能會(huì)阻塞
   Message msg = queue.next(); // might block
   ...
   //解析消息,分發(fā)消息
   msg.target.dispatchMessage(msg);
   ...
}

為什么這個(gè)死循環(huán)不會(huì)造成ANR異常呢?

因?yàn)锳ndroid 的是由事件驅(qū)動(dòng)的,looper.loop() 不斷地接收事件、處理事件,每一個(gè)點(diǎn)擊觸摸或者說(shuō)Activity的生命周期都是運(yùn)行在 Looper.loop() 的控制之下,如果它停止了,應(yīng)用也就停止了。只能是某一個(gè)消息或者說(shuō)對(duì)消息的處理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。技術(shù)博客大總結(jié)

處理消息handleMessage方法

如下所示

可以看見(jiàn)Activity的生命周期都是依靠主線程的Looper.loop,當(dāng)收到不同Message時(shí)則采用相應(yīng)措施。

如果某個(gè)消息處理時(shí)間過(guò)長(zhǎng),比如你在onCreate(),onResume()里面處理耗時(shí)操作,那么下一次的消息比如用戶的點(diǎn)擊事件不能處理了,整個(gè)循環(huán)就會(huì)產(chǎn)生卡頓,時(shí)間一長(zhǎng)就成了ANR。

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
            r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        break;
        case RELAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
            ActivityClientRecord r = (ActivityClientRecord) msg.obj;
            handleRelaunchActivity(r);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        break;
        case PAUSE_ACTIVITY:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
            handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0);
            maybeSnapshot();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        case PAUSE_ACTIVITY_FINISHING:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
            handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        ...........
    }
}

loop的循環(huán)消耗性能嗎?

主線程Looper從消息隊(duì)列讀取消息,當(dāng)讀完所有消息時(shí),主線程阻塞。子線程往消息隊(duì)列發(fā)送消息,并且往管道文件寫數(shù)據(jù),主線程即被喚醒,從管道文件讀取數(shù)據(jù),主線程被喚醒只是為了讀取消息,當(dāng)消息讀取完畢,再次睡眠。因此loop的循環(huán)并不會(huì)對(duì)CPU性能有過(guò)多的消耗。

簡(jiǎn)單的來(lái)說(shuō):ActivityThread的main方法主要就是做消息循環(huán),一旦退出消息循環(huán),那么你的程序也就可以退出了。

6.0.0.9 Message可以如何創(chuàng)建?哪種效果更好,為什么?runOnUiThread如何實(shí)現(xiàn)子線程更新UI?

創(chuàng)建Message對(duì)象的幾種方式:技術(shù)博客大總結(jié)

Message msg = new Message();

Message msg = Message.obtain();

Message msg = handler1.obtainMessage();

后兩種方法都是從整個(gè)Messge池中返回一個(gè)新的Message實(shí)例,能有效避免重復(fù)Message創(chuàng)建對(duì)象,因此更鼓勵(lì)這種方式創(chuàng)建Message

runOnUiThread如何實(shí)現(xiàn)子線程更新UI

看看源碼,如下所示

如果msg.callback為空的話,會(huì)直接調(diào)用我們的mCallback.handleMessage(msg),即handler的handlerMessage方法。由于Handler對(duì)象是在主線程中創(chuàng)建的,所以handler的handlerMessage方法的執(zhí)行也會(huì)在主線程中。

在runOnUiThread程序首先會(huì)判斷當(dāng)前線程是否是UI線程,如果是就直接運(yùn)行,如果不是則post,這時(shí)其實(shí)質(zhì)還是使用的Handler機(jī)制來(lái)處理線程與UI通訊。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

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

6.0.1.3 使用Hanlder的postDealy()后消息隊(duì)列會(huì)發(fā)生什么變化?

post delay的Message并不是先等待一定時(shí)間再放入到MessageQueue中,而是直接進(jìn)入并阻塞當(dāng)前線程,然后將其delay的時(shí)間和隊(duì)頭的進(jìn)行比較,按照觸發(fā)時(shí)間進(jìn)行排序,如果觸發(fā)時(shí)間更近則放入隊(duì)頭,保證隊(duì)頭的時(shí)間最小、隊(duì)尾的時(shí)間最大。此時(shí),如果隊(duì)頭的Message正是被delay的,則將當(dāng)前線程堵塞一段時(shí)間,直到等待足夠時(shí)間再喚醒執(zhí)行該Message,否則喚醒后直接執(zhí)行。

6.0.1.4 ThreadLocal有什么作用?

線程本地存儲(chǔ)的功能

ThreadLocal類可實(shí)現(xiàn)線程本地存儲(chǔ)的功能,把共享數(shù)據(jù)的可見(jiàn)范圍限制在同一個(gè)線程之內(nèi),無(wú)須同步就能保證線程之間不出現(xiàn)數(shù)據(jù)爭(zhēng)用的問(wèn)題,這里可理解為ThreadLocal幫助Handler找到本線程的Looper。

技術(shù)博客大總結(jié)

怎么存儲(chǔ)呢?底層數(shù)據(jù)結(jié)構(gòu)是啥?

每個(gè)線程的Thread對(duì)象中都有一個(gè)ThreadLocalMap對(duì)象,它存儲(chǔ)了一組以ThreadLocal.threadLocalHashCode為key、以本地線程變量為value的鍵值對(duì),而ThreadLocal對(duì)象就是當(dāng)前線程的ThreadLocalMap的訪問(wèn)入口,也就包含了一個(gè)獨(dú)一無(wú)二的threadLocalHashCode值,通過(guò)這個(gè)值就可以在線程鍵值值對(duì)中找回對(duì)應(yīng)的本地線程變量。

關(guān)于其他內(nèi)容介紹 01.關(guān)于博客匯總鏈接

1.技術(shù)博客匯總

2.開(kāi)源項(xiàng)目匯總

3.生活博客匯總

4.喜馬拉雅音頻匯總

5.其他匯總

02.關(guān)于我的博客

我的個(gè)人站點(diǎn):www.yczbj.org, www.ycbjie.cn

github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/...

簡(jiǎn)書:http://www.jianshu.com/u/b7b2...

csdn:http://my.csdn.net/m0_37700275

喜馬拉雅聽(tīng)書:http://www.ximalaya.com/zhubo...

開(kāi)源中國(guó):https://my.oschina.net/zbj161...

泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...

郵箱:yangchong211@163.com

阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV

segmentfault頭條:https://segmentfault.com/u/xi...

掘金:https://juejin.im/user/593943...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/72956.html

相關(guān)文章

  • Javascript系列javascript機(jī)制

    摘要:異步任務(wù)必須指定回調(diào)函數(shù),當(dāng)異步任務(wù)從任務(wù)隊(duì)列回到執(zhí)行棧,回調(diào)函數(shù)就會(huì)執(zhí)行。事件循環(huán)主線程從任務(wù)隊(duì)列中讀取事件,這個(gè)過(guò)程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為。事件循環(huán)事件循環(huán)是指主線程重復(fù)從消息隊(duì)列中取消息執(zhí)行的過(guò)程。 參考鏈接:這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制https://zhuanlan.zhihu.com/p/...從瀏覽器多進(jìn)程到JS單線程,JS運(yùn)行機(jī)制...

    13651657101 評(píng)論0 收藏0
  • 180609-Spring事件驅(qū)動(dòng)機(jī)制的簡(jiǎn)單使用

    摘要:文章鏈接之事件驅(qū)動(dòng)機(jī)制的簡(jiǎn)單使用之事件驅(qū)動(dòng)機(jī)制的簡(jiǎn)單使用關(guān)于事件的發(fā)起與相應(yīng),在客戶端的交互中可算是非常頻繁的事情了,關(guān)于事件的發(fā)布訂閱,在生態(tài)中,可謂是非常有名了,而也提供了事件機(jī)制,本文則主要介紹后端如何在的環(huán)境中,使用事件機(jī)制使用姿 showImg(https://segmentfault.com/img/remote/1460000015237252); 文章鏈接:https:...

    Hwg 評(píng)論0 收藏0
  • 分布式服務(wù)框架遠(yuǎn)程通訊技術(shù)及原理分析

    摘要:微軟的雖然引入了事件機(jī)制,可以在隊(duì)列收到消息時(shí)觸發(fā)事件,通知訂閱者。由微軟作為主要貢獻(xiàn)者的,則對(duì)以及做了進(jìn)一層包裝,并能夠很好地實(shí)現(xiàn)這一模式。 在分布式服務(wù)框架中,一個(gè)最基礎(chǔ)的問(wèn)題就是遠(yuǎn)程服務(wù)是怎么通訊的,在Java領(lǐng)域中有很多可實(shí)現(xiàn)遠(yuǎn)程通訊的技術(shù),例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,這些名詞之間到底是些什么關(guān)系呢,它們背后到底是基...

    sorra 評(píng)論0 收藏0
  • 分布式服務(wù)框架遠(yuǎn)程通訊技術(shù)及原理分析

    摘要:微軟的雖然引入了事件機(jī)制,可以在隊(duì)列收到消息時(shí)觸發(fā)事件,通知訂閱者。由微軟作為主要貢獻(xiàn)者的,則對(duì)以及做了進(jìn)一層包裝,并能夠很好地實(shí)現(xiàn)這一模式。 在分布式服務(wù)框架中,一個(gè)最基礎(chǔ)的問(wèn)題就是遠(yuǎn)程服務(wù)是怎么通訊的,在Java領(lǐng)域中有很多可實(shí)現(xiàn)遠(yuǎn)程通訊的技術(shù),例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,這些名詞之間到底是些什么關(guān)系呢,它們背后到底是基...

    0xE7A38A 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<