日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Handler的内功心法,值得拥有!

發(fā)布時(shí)間:2024/1/18 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Handler的内功心法,值得拥有! 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


/? ?今日科技快訊? ?/

12月16日,華為今日宣布正式推出鴻蒙OS的手機(jī)開(kāi)發(fā)者Beta版。相關(guān)測(cè)試視頻顯示,鴻蒙OS在系統(tǒng)UI方面與現(xiàn)有的EMUI11基本保持一致。此前,華為曾宣布,鴻蒙系統(tǒng)在2021年4月將面向內(nèi)存128MB-4GB終端設(shè)備開(kāi)源,2021年10月以后將面向4GB以上所有設(shè)備開(kāi)源。

/? ?作者簡(jiǎn)介? ?/

本篇文章來(lái)自傷心的豬大腸的投稿,整理并分享了Android開(kāi)發(fā)中Handler的相關(guān)內(nèi)容,相信會(huì)對(duì)大家有所幫助!同時(shí)也感謝作者貢獻(xiàn)的精彩文章!

傷心的豬大腸的博客地址:

https://juejin.cn/user/4441682709326958

/? ?正文? ?/

Handler是Android中的消息處理機(jī)制,是一種線程間通信的解決方案,同時(shí)你也可以理解為它天然的為我們?cè)谥骶€程創(chuàng)建一個(gè)隊(duì)列,隊(duì)列中的消息順序就是我們?cè)O(shè)置的延遲的時(shí)間,如果你想在Android中實(shí)現(xiàn)一個(gè)隊(duì)列的功能,不妨第一時(shí)間考慮一下它。本文分為三部分:

Handler的源碼和常見(jiàn)問(wèn)題的解答

  • 一個(gè)線程中最多有多少個(gè)Handler,Looper,MessageQueue?

  • Looper死循環(huán)為什么不會(huì)導(dǎo)致應(yīng)用卡死,會(huì)耗費(fèi)大量資源嗎?

  • 子線程的如何更新UI,比如Dialog,Toast等?系統(tǒng)為什么不建議子線程中更新UI?

  • 主線程如何訪問(wèn)網(wǎng)絡(luò)?

  • 如何處理Handler使用不當(dāng)造成的內(nèi)存泄漏?

  • Handler的消息優(yōu)先級(jí),有什么應(yīng)用場(chǎng)景?

  • 主線程的Looper何時(shí)退出?能否手動(dòng)退出?

  • 如何判斷當(dāng)前線程是安卓主線程?

  • 正確創(chuàng)建Message實(shí)例的方式?

  • Handler深層次問(wèn)題解答

  • ThreadLocal

  • epoll機(jī)制

  • Handle同步屏障機(jī)制

  • Handler的鎖相關(guān)問(wèn)題

  • Handler中的同步方法

  • Handler在系統(tǒng)以及第三方框架的一些應(yīng)用

  • HandlerThread

  • IntentService

  • 如何打造一個(gè)不崩潰的APP

  • Glide中的運(yùn)用

  • Handler的源碼和常見(jiàn)問(wèn)題的解答

    下面來(lái)看一下官方對(duì)其的定義:

    A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.

    大意就是Handler允許你發(fā)送Message/Runnable到線程的消息隊(duì)列(MessageQueue)中,每個(gè)Handler實(shí)例和一個(gè)線程以及那個(gè)線程的消息隊(duì)列相關(guān)聯(lián)。當(dāng)你創(chuàng)建一個(gè)Handler時(shí)應(yīng)該和一個(gè)Looper進(jìn)行綁定(主線程默認(rèn)已經(jīng)創(chuàng)建Looper了,子線程需要自己創(chuàng)建Looper),它向Looper的對(duì)應(yīng)的消息隊(duì)列傳送Message/Runnable同時(shí)在那個(gè)Looper所在線程處理對(duì)應(yīng)的Message/Runnable。下面這張圖就是Handler的工作流程。

    Handler工作流程圖

    可以看到在Thread中,Looper的這個(gè)傳送帶其實(shí)就一個(gè)死循環(huán),它不斷的從消息隊(duì)列MessageQueue中不斷的取消息,最后交給Handler.dispatchMessage進(jìn)行消息的分發(fā),而Handler.sendXXX,Handler.postXXX這些方法把消息發(fā)送到消息隊(duì)列中MessageQueue,整個(gè)模式其實(shí)就是一個(gè)生產(chǎn)者-消費(fèi)者模式,源源不斷的生產(chǎn)消息,處理消息,沒(méi)有消息時(shí)進(jìn)行休眠。MessageQueue是一個(gè)由單鏈表構(gòu)成的優(yōu)先級(jí)隊(duì)列(取的都是頭部,所以說(shuō)是隊(duì)列)。

    前面說(shuō)過(guò),當(dāng)你創(chuàng)建一個(gè)Handler時(shí)應(yīng)該和一個(gè)Looper進(jìn)行綁定(綁定也可以理解為創(chuàng)建,主線程默認(rèn)已經(jīng)創(chuàng)建Looper了,子線程需要自己創(chuàng)建Looper),因此我們先來(lái)看看主線程中是如何處理的:

    //ActivityThread.java public?static?void?main(String[]?args)?{···Looper.prepareMainLooper();···ActivityThread?thread?=?new?ActivityThread();thread.attach(false,?startSeq);if?(sMainThreadHandler?==?null)?{sMainThreadHandler?=?thread.getHandler();}if?(false)?{Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG,?"ActivityThread"));}//?End?of?event?ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();throw?new?RuntimeException("Main?thread?loop?unexpectedly?exited"); }

    可以看到在ActivityThread中的main方法中,我們先調(diào)用了Looper.prepareMainLooper()方法,然后獲取當(dāng)前線程的Handler,最后調(diào)用Looper.loop()。先來(lái)看一下Looper.prepareMainLooper()方法。

    //Looper.java?? /** *?Initialize?the?current?thread?as?a?looper,?marking?it?as?an *?application's?main?looper.?The?main?looper?for?your?application *?is?created?by?the?Android?environment,?so?you?should?never?need *?to?call?this?function?yourself.??See?also:?{@link?#prepare()} */ public?static?void?prepareMainLooper()?{prepare(false);synchronized?(Looper.class)?{if?(sMainLooper?!=?null)?{throw?new?IllegalStateException("The?main?Looper?has?already?been?prepared.");}sMainLooper?=?myLooper();} } //prepare 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.prepareMainLooper()方法中創(chuàng)建了當(dāng)前線程的Looper,同時(shí)將Looper實(shí)例存放到線程局部變量sThreadLocal(ThreadLocal)中,也就是每個(gè)線程有自己的Looper。在創(chuàng)建Looper的時(shí)候也創(chuàng)建了該線程的消息隊(duì)列,可以看到prepareMainLooper會(huì)判斷sMainLooper是否有值,如果調(diào)用多次,就會(huì)拋出異常,所以也就是說(shuō)主線程的Looper和MessageQueue只會(huì)有一個(gè)。同理子線程中調(diào)用Looper.prepare()時(shí),會(huì)調(diào)用prepare(true)方法,如果多次調(diào)用,也會(huì)拋出每個(gè)線程只能由一個(gè)Looper的異常,總結(jié)起來(lái)就是每個(gè)線程中只有一個(gè)Looper和MessageQueue。

    //Looper.java private?Looper(boolean?quitAllowed)?{mQueue?=?new?MessageQueue(quitAllowed);mThread?=?Thread.currentThread(); }

    再來(lái)看看主線程sMainThreadHandler = thread.getHandler(),getHandler獲取到的實(shí)際上就是mH這個(gè)Handler。

    //ActivityThread.java final?H?mH?=?new?H();? @UnsupportedAppUsagefinal?Handler?getHandler()?{return?mH; }

    mH這個(gè)Handler是ActivityThread的內(nèi)部類,通過(guò)查看handMessage方法,可以看到這個(gè)Handler處理四大組件,Application等的一些消息,比如創(chuàng)建Service,綁定Service的一些消息。

    //ActivityThread.java class?H?extends?Handler?{···public?void?handleMessage(Message?msg)?{if?(DEBUG_MESSAGES)?Slog.v(TAG,?">>>?handling:?"?+?codeToString(msg.what));switch?(msg.what)?{case?BIND_APPLICATION:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,?"bindApplication");AppBindData?data?=?(AppBindData)msg.obj;handleBindApplication(data);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;case?EXIT_APPLICATION:if?(mInitialApplication?!=?null)?{mInitialApplication.onTerminate();}Looper.myLooper().quit();break;case?RECEIVER:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,?"broadcastReceiveComp");handleReceiver((ReceiverData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;case?CREATE_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,?("serviceCreate:?"?+?String.valueOf(msg.obj)));handleCreateService((CreateServiceData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;case?BIND_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,?"serviceBind");handleBindService((BindServiceData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;case?UNBIND_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,?"serviceUnbind");handleUnbindService((BindServiceData)msg.obj);schedulePurgeIdler();Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;case?SERVICE_ARGS:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,?("serviceStart:?"?+?String.valueOf(msg.obj)));handleServiceArgs((ServiceArgsData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;case?STOP_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,?"serviceStop");handleStopService((IBinder)msg.obj);schedulePurgeIdler();Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;···case?APPLICATION_INFO_CHANGED:mUpdatingSystemConfig?=?true;try?{handleApplicationInfoChanged((ApplicationInfo)?msg.obj);}?finally?{mUpdatingSystemConfig?=?false;}break;case?RUN_ISOLATED_ENTRY_POINT:handleRunIsolatedEntryPoint((String)?((SomeArgs)?msg.obj).arg1,(String[])?((SomeArgs)?msg.obj).arg2);break;case?EXECUTE_TRANSACTION:final?ClientTransaction?transaction?=?(ClientTransaction)?msg.obj;mTransactionExecutor.execute(transaction);if?(isSystem())?{//?Client?transactions?inside?system?process?are?recycled?on?the?client?side//?instead?of?ClientLifecycleManager?to?avoid?being?cleared?before?this//?message?is?handled.transaction.recycle();}//?TODO(lifecycler):?Recycle?locally?scheduled?transactions.break;case?RELAUNCH_ACTIVITY:handleRelaunchActivityLocally((IBinder)?msg.obj);break;case?PURGE_RESOURCES:schedulePurgeIdler();break;}Object?obj?=?msg.obj;if?(obj?instanceof?SomeArgs)?{((SomeArgs)?obj).recycle();}if?(DEBUG_MESSAGES)?Slog.v(TAG,?"<<<?done:?"?+?codeToString(msg.what));} }

    最后我們查看Looper.loop()方法:

    //Looper.java public?static?void?loop()?{//獲取ThreadLocal中的Looperfinal?Looper?me?=?myLooper();···final?MessageQueue?queue?=?me.mQueue;···for?(;;)?{?//死循環(huán)//獲取消息Message?msg?=?queue.next();?//?might?blockif?(msg?==?null)?{//?No?message?indicates?that?the?message?queue?is?quitting.return;}···msg.target.dispatchMessage(msg);···//回收復(fù)用??msg.recycleUnchecked();} }

    在loop方法中是一個(gè)死循環(huán),在這里從消息隊(duì)列中不斷的獲取消息queue.next(),然后通過(guò)Handler(msg.target)進(jìn)行消息的分發(fā),其實(shí)并沒(méi)有什么具體的綁定,因?yàn)镠andler在每個(gè)線程中對(duì)應(yīng)只有一個(gè)Looper和消息隊(duì)列MessageQueue,自然要靠它來(lái)處理,也就是是調(diào)用Looper.loop()方法。在Looper.loop()的死循環(huán)中不斷的取消息,最后回收復(fù)用。

    這里要強(qiáng)調(diào)一下Message中的參數(shù)target(Handler),正是這個(gè)變量,每個(gè)Message才能找到對(duì)應(yīng)的Handler進(jìn)行消息分發(fā),讓多個(gè)Handler同時(shí)工作。

    再來(lái)看看子線程中是如何處理的,首先在子線程中創(chuàng)建一個(gè)Handler并發(fā)送Runnable。

    @Overrideprotected?void?onCreate(@Nullable?Bundle?savedInstanceState)?{super.onCreate(savedInstanceState);setContentView(R.layout.activity_three);new?Thread(new?Runnable()?{@Overridepublic?void?run()?{new?Handler().post(new?Runnable()?{@Overridepublic?void?run()?{Toast.makeText(HandlerActivity.this,"toast",Toast.LENGTH_LONG).show();}});}}).start();}

    運(yùn)行后可以看到錯(cuò)誤日志,可以看到提示我們需要在子線程中調(diào)用Looper.prepare()方法,實(shí)際上就是要?jiǎng)?chuàng)建一個(gè)Looper和你的Handler進(jìn)行“關(guān)聯(lián)”。

    ???---------?beginning?of?crash 2020-11-09?15:51:03.938?21122-21181/com.jackie.testdialog?E/AndroidRuntime:?FATAL?EXCEPTION:?Thread-2Process:?com.jackie.testdialog,?PID:?21122java.lang.RuntimeException:?Can't?create?handler?inside?thread?Thread[Thread-2,5,main]?that?has?not?called?Looper.prepare()at?android.os.Handler.<init>(Handler.java:207)at?android.os.Handler.<init>(Handler.java:119)at?com.jackie.testdialog.HandlerActivity$1.run(HandlerActivity.java:31)at?java.lang.Thread.run(Thread.java:919)

    添加Looper.prepare()創(chuàng)建Looper,同時(shí)調(diào)用Looper.loop()方法開(kāi)始處理消息。

    ?@Overrideprotected?void?onCreate(@Nullable?Bundle?savedInstanceState)?{super.onCreate(savedInstanceState);setContentView(R.layout.activity_three);new?Thread(new?Runnable()?{@Overridepublic?void?run()?{//創(chuàng)建Looper,MessageQueueLooper.prepare();new?Handler().post(new?Runnable()?{@Overridepublic?void?run()?{Toast.makeText(HandlerActivity.this,"toast",Toast.LENGTH_LONG).show();}});//開(kāi)始處理消息Looper.loop();}}).start();}

    這里需要注意在所有事情處理完成后應(yīng)該調(diào)用quit方法來(lái)終止消息循環(huán),否則這個(gè)子線程就會(huì)一直處于循環(huán)等待的狀態(tài),因此不需要的時(shí)候終止Looper,調(diào)用Looper.myLooper().quit()。

    看完上面的代碼可能你會(huì)有一個(gè)疑問(wèn),在子線程中更新UI(進(jìn)行Toast)不會(huì)有問(wèn)題嗎,我們Android不是不允許在子線程更新UI嗎,實(shí)際上并不是這樣的,在ViewRootImpl中的checkThread方法會(huì)校驗(yàn)mThread != Thread.currentThread(),mThread的初始化是在ViewRootImpl的的構(gòu)造器中,也就是說(shuō)一個(gè)創(chuàng)建ViewRootImpl線程必須和調(diào)用checkThread所在的線程一致,UI的更新并非只能在主線程才能進(jìn)行。

    void?checkThread()?{if?(mThread?!=?Thread.currentThread())?{throw?new?CalledFromWrongThreadException("Only?the?original?thread?that?created?a?view?hierarchy?can?touch?its?views.");} }

    這里需要引入一些概念,Window是Android中的窗口,每個(gè)Activity和Dialog,Toast分別對(duì)應(yīng)一個(gè)具體的Window,Window是一個(gè)抽象的概念,每一個(gè)Window都對(duì)應(yīng)著一個(gè)View和一個(gè)ViewRootImpl,Window和View通過(guò)ViewRootImpl來(lái)建立聯(lián)系,因此,它是以View的形式存在的。我們來(lái)看一下Toast中的ViewRootImpl的創(chuàng)建過(guò)程,調(diào)用toast的show方法最終會(huì)調(diào)用到其handleShow方法。

    //Toast.java public?void?handleShow(IBinder?windowToken)?{···if?(mView?!=?mNextView)?{//?Since?the?notification?manager?service?cancels?the?token?right//?after?it?notifies?us?to?cancel?the?toast?there?is?an?inherent//?race?and?we?may?attempt?to?add?a?window?after?the?token?has?been//?invalidated.?Let?us?hedge?against?that.try?{mWM.addView(mView,?mParams);?//進(jìn)行ViewRootImpl的創(chuàng)建trySendAccessibilityEvent();}?catch?(WindowManager.BadTokenException?e)?{/*?ignore?*/}} }

    這個(gè)mWM(WindowManager)的最終實(shí)現(xiàn)者是WindowManagerGlobal,其的addView方法中會(huì)創(chuàng)建ViewRootImpl,然后進(jìn)行root.setView(view, wparams, panelParentView),通過(guò)ViewRootImpl來(lái)更新界面并完成Window的添加過(guò)程。

    //WindowManagerGlobal.java root?=?new?ViewRootImpl(view.getContext(),?display);?//創(chuàng)建ViewRootImplview.setLayoutParams(wparams);mViews.add(view);mRoots.add(root);mParams.add(wparams);//?do?this?last?because?it?fires?off?messages?to?start?doing?thingstry?{//ViewRootImplroot.setView(view,?wparams,?panelParentView);}?catch?(RuntimeException?e)?{//?BadTokenException?or?InvalidDisplayException,?clean?up.if?(index?>=?0)?{removeViewLocked(index,?true);}throw?e;} }

    setView內(nèi)部會(huì)通過(guò)requestLayout來(lái)完成異步刷新請(qǐng)求,同時(shí)也會(huì)調(diào)用checkThread方法來(lái)檢驗(yàn)線程的合法性。

    @Override public?void?requestLayout()?{if?(!mHandlingLayoutInLayoutRequest)?{checkThread();mLayoutRequested?=?true;scheduleTraversals();} }

    因此,我們的ViewRootImpl的創(chuàng)建是在子線程,所以mThread的值也是子線程,同時(shí)我們的更新也是在子線程,所以不會(huì)產(chǎn)生異常,同時(shí)也可以參考這篇文章分析,寫的非常詳細(xì)。同理下面的代碼也可以驗(yàn)證這個(gè)情況。

    //子線程中調(diào)用???? public?void?showDialog(){new?Thread(new?Runnable()?{@Overridepublic?void?run()?{//創(chuàng)建Looper,MessageQueueLooper.prepare();new?Handler().post(new?Runnable()?{@Overridepublic?void?run()?{builder?=?new?AlertDialog.Builder(HandlerActivity.this);builder.setTitle("jackie");alertDialog?=?builder.create();alertDialog.show();alertDialog.hide();}});//開(kāi)始處理消息Looper.loop();}}).start();}

    在子線程中調(diào)用showDialog方法,先調(diào)用alertDialog.show()方法,再調(diào)用alertDialog.hide()方法,hide方法只是將Dialog隱藏,并沒(méi)有做其他任何操作(沒(méi)有移除Window),然后再在主線程調(diào)用alertDialog.show();便會(huì)拋出Only the original thread that created a view hierarchy can touch its views異常了。

    2020-11-09?18:35:39.874?24819-24819/com.jackie.testdialog?E/AndroidRuntime:?FATAL?EXCEPTION:?mainProcess:?com.jackie.testdialog,?PID:?24819android.view.ViewRootImpl$CalledFromWrongThreadException:?Only?the?original?thread?that?created?a?view?hierarchy?can?touch?its?views.at?android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8191)at?android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1420)at?android.view.View.requestLayout(View.java:24454)at?android.view.View.setFlags(View.java:15187)at?android.view.View.setVisibility(View.java:10836)at?android.app.Dialog.show(Dialog.java:307)at?com.jackie.testdialog.HandlerActivity$2.onClick(HandlerActivity.java:41)at?android.view.View.performClick(View.java:7125)at?android.view.View.performClickInternal(View.java:7102)

    所以在線程中更新UI的重點(diǎn)是創(chuàng)建它的ViewRootImpl和checkThread所在的線程是否一致。

    如何在主線程中訪問(wèn)網(wǎng)絡(luò)

    在網(wǎng)絡(luò)請(qǐng)求之前添加如下代碼:

    StrictMode.ThreadPolicy?policy?=?new?StrictMode.ThreadPolicy.Builder().permitNetwork().build(); StrictMode.setThreadPolicy(policy);

    StrictMode(嚴(yán)苛模式)Android2.3引入,用于檢測(cè)兩大問(wèn)題:ThreadPolicy(線程策略)和VmPolicy(VM策略),這里把嚴(yán)苛模式的網(wǎng)絡(luò)檢測(cè)關(guān)了,就可以在主線程中執(zhí)行網(wǎng)絡(luò)操作了,一般是不建議這么做的。

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

    這是因?yàn)?Android 的UI控件不是線程安全的,如果在多線程中并發(fā)訪問(wèn)可能會(huì)導(dǎo)致UI控件處于不可預(yù)期的狀態(tài),那么為什么系統(tǒng)不對(duì)UI控件的訪問(wèn)加上鎖機(jī)制呢?缺點(diǎn)有兩個(gè):

  • 首先加上鎖機(jī)制會(huì)讓UI訪問(wèn)的邏輯變得復(fù)雜

  • 鎖機(jī)制會(huì)降低UI訪問(wèn)的效率,因?yàn)殒i機(jī)制會(huì)阻塞某些線程的執(zhí)行。

  • 所以最簡(jiǎn)單且高效的方法就是采用單線程模型來(lái)處理UI操作。(安卓開(kāi)發(fā)藝術(shù)探索)

    子線程如何通知主線程更新UI(都是通過(guò)Handle發(fā)送消息到主線程操作UI的)

  • 主線程中定義 Handler,子線程通過(guò) mHandler 發(fā)送消息,主線程 Handler 的 handleMessage 更新UI。

  • 用 Activity 對(duì)象的 runOnUiThread 方法。

  • 創(chuàng)建 Handler,傳入 getMainLooper。

  • View.post(Runnable r) 。

  • Looper死循環(huán)為什么不會(huì)導(dǎo)致應(yīng)用卡死,會(huì)耗費(fèi)大量資源嗎?

    從前面的主線程、子線程的分析可以看出,Looper會(huì)在線程中不斷的檢索消息,如果是子線程的Looper死循環(huán),一旦任務(wù)完成,用戶應(yīng)該手動(dòng)退出,而不是讓其一直休眠等待。(引用自Gityuan)線程其實(shí)就是一段可執(zhí)行的代碼,當(dāng)可執(zhí)行的代碼執(zhí)行完成后,線程的生命周期便該終止了,線程退出。而對(duì)于主線程,我們是絕不希望會(huì)被運(yùn)行一段時(shí)間,自己就退出,那么如何保證能一直存活呢?簡(jiǎn)單做法就是可執(zhí)行代碼是能一直執(zhí)行下去的,死循環(huán)便能保證不會(huì)被退出,例如,binder 線程也是采用死循環(huán)的方法,通過(guò)循環(huán)方式不同與 Binder 驅(qū)動(dòng)進(jìn)行讀寫操作,當(dāng)然并非簡(jiǎn)單地死循環(huán),無(wú)消息時(shí)會(huì)休眠。Android是基于消息處理機(jī)制的,用戶的行為都在這個(gè)Looper循環(huán)中,我們?cè)谛菝邥r(shí)點(diǎn)擊屏幕,便喚醒主線程繼續(xù)進(jìn)行工作。

    主線程的死循環(huán)一直運(yùn)行是不是特別消耗 CPU 資源呢?其實(shí)不然,這里就涉及到 Linux ?pipe/epoll機(jī)制,簡(jiǎn)單說(shuō)就是在主線程的 MessageQueue 沒(méi)有消息時(shí),便阻塞在 loop 的 queue.next() 中的 nativePollOnce() 方法里,此時(shí)主線程會(huì)釋放 CPU 資源進(jìn)入休眠狀態(tài),直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生,通過(guò)往 pipe 管道寫端寫入數(shù)據(jù)來(lái)喚醒主線程工作。這里采用的 epoll 機(jī)制,是一種IO多路復(fù)用機(jī)制,可以同時(shí)監(jiān)控多個(gè)描述符,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮?#xff0c;本質(zhì)同步I/O,即讀寫是阻塞的。所以說(shuō),主線程大多數(shù)時(shí)候都是處于休眠狀態(tài),并不會(huì)消耗大量CPU資源。

    主線程的Looper何時(shí)退出

    在App退出時(shí),ActivityThread中的mH(Handler)收到消息后,執(zhí)行退出。

    //ActivityThread.java case?EXIT_APPLICATION:if?(mInitialApplication?!=?null)?{mInitialApplication.onTerminate();}Looper.myLooper().quit();break;

    如果你嘗試手動(dòng)退出主線程Looper,便會(huì)拋出如下異常。

    Caused?by:?java.lang.IllegalStateException:?Main?thread?not?allowed?to?quit.at?android.os.MessageQueue.quit(MessageQueue.java:428)at?android.os.Looper.quit(Looper.java:354)at?com.jackie.testdialog.Test2Activity.onCreate(Test2Activity.java:29)at?android.app.Activity.performCreate(Activity.java:7802)at?android.app.Activity.performCreate(Activity.java:7791)at?android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)at?android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)at?android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)?at?android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)?at?android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)?at?android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)?at?android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)?at?android.os.Handler.dispatchMessage(Handler.java:107)?at?android.os.Looper.loop(Looper.java:214)?at?android.app.ActivityThread.main(ActivityThread.java:7356)?at?java.lang.reflect.Method.invoke(Native?Method)?at?com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)?at?com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

    為什么不允許退出呢,因?yàn)橹骶€程不允許退出,一旦退出就意味著程序掛了,退出也不應(yīng)該用這種方式退出。

    Handler的消息處理順序

    在Looper執(zhí)行消息循環(huán)loop()時(shí)會(huì)執(zhí)行下面這行代碼,msg.targe就是這個(gè)Handler對(duì)象。

    msg.target.dispatchMessage(msg);

    我們來(lái)看看dispatchMessage的源碼:

    ??public?void?dispatchMessage(@NonNull?Message?msg)?{if?(msg.callback?!=?null)?{handleCallback(msg);}?else?{//如果?callback?處理了該?msg?并且返回?true,?就不會(huì)再回調(diào)?handleMessageif?(mCallback?!=?null)?{if?(mCallback.handleMessage(msg))?{return;}}handleMessage(msg);}}

    1.如果Message這個(gè)對(duì)象有CallBack回調(diào)的話,這個(gè)CallBack實(shí)際上是個(gè)Runnable,就只執(zhí)行這個(gè)回調(diào),然后就結(jié)束了,創(chuàng)建該Message的CallBack代碼如下:

    Message?msgCallBack?=?Message.obtain(handler,?new?Runnable()?{@Overridepublic?void?run()?{} });

    而handleCallback方法中調(diào)用的是Runnable的run方法。

    private?static?void?handleCallback(Message?message)?{message.callback.run(); }

    2.如果Message對(duì)象沒(méi)有CallBack回調(diào),進(jìn)入else分支判斷Handler的CallBack是否為空,不為空?qǐng)?zhí)行CallBack的handleMessage方法,然后return,構(gòu)建Handler的CallBack代碼如下:

    Handler.Callback?callback?=?new?Handler.Callback()?{@Overridepublic?boolean?handleMessage(@NonNull?Message?msg)?{//retrun?true,就不執(zhí)行下面的邏輯了,可以用于做優(yōu)先級(jí)的處理return?false;} };

    3.最后才調(diào)用到Handler的handleMessage()函數(shù),也就是我們經(jīng)常去重寫的函數(shù),在該方法中做消息的處理。

    使用場(chǎng)景

    可以看到Handler.Callback 有優(yōu)先處理消息的權(quán)利 ,當(dāng)一條消息被 Callback 處理并攔截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不會(huì)被調(diào)用了;如果 Callback 處理了消息,但是并沒(méi)有攔截,那么就意味著一個(gè)消息可以同時(shí)被 Callback 以及 Handler 處理。我們可以利用CallBack這個(gè)攔截來(lái)攔截Handler的消息。

    場(chǎng)景:Hook ActivityThread.mH , 在 ActivityThread 中有個(gè)成員變量 mH ,它是個(gè) Handler,又是個(gè)極其重要的類,幾乎所有的插件化框架都使用了這個(gè)方法。

    Handler.post(Runnable r)方法的執(zhí)行邏輯

    我們需要分析平時(shí)常用的Handler.post(Runnable r)方法是如何執(zhí)行的,是否新創(chuàng)建了一個(gè)線程了呢,實(shí)際上并沒(méi)有,這個(gè)Runnable對(duì)象只是被調(diào)用了它的run方法,根本并沒(méi)有啟動(dòng)一個(gè)線程,源碼如下:

    //Handler.java public?final?boolean?post(@NonNull?Runnable?r)?{return??sendMessageDelayed(getPostMessage(r),?0); }private?static?Message?getPostMessage(Runnable?r)?{Message?m?=?Message.obtain();m.callback?=?r;return?m;}

    最終該Runnable對(duì)象被包裝成一個(gè)Message對(duì)象,也就是這個(gè)Runnable對(duì)象就是該Message的CallBack對(duì)象了,有優(yōu)先執(zhí)行的權(quán)利了。

    Handler是如何進(jìn)行線程切換的

    原理很簡(jiǎn)單,線程間是共享資源的,子線程通過(guò)handler.sendXXX,handler.postXXX等方法發(fā)送消息,然后通過(guò)Looper.loop()在消息隊(duì)列中不斷的循環(huán)檢索消息,最后交給handle.dispatchMessage方法進(jìn)行消息的分發(fā)處理。

    如何處理Handler使用不當(dāng)造成的內(nèi)存泄漏?

  • 有延時(shí)消息,在界面關(guān)閉后及時(shí)移除Message/Runnable,調(diào)用handler.removeCallbacksAndMessages(null)

  • 內(nèi)部類導(dǎo)致的內(nèi)存泄漏改為靜態(tài)內(nèi)部類,并對(duì)上下文或者Activity/Fragment使用弱引用。

  • 同時(shí)還有一個(gè)很關(guān)鍵的點(diǎn),如果有個(gè)延時(shí)消息,當(dāng)界面關(guān)閉時(shí),該Handler中的消息還沒(méi)有處理完畢,那么最終這個(gè)消息是怎么處理的?經(jīng)過(guò)測(cè)試,比如我打開(kāi)界面后延遲10s發(fā)送消息,關(guān)閉界面,最終在Handler(匿名內(nèi)部類創(chuàng)建的)的handMessage方法中還是會(huì)收到消息(打印日志)。因?yàn)闀?huì)有一條MessageQueue -> Message -> Handler -> Activity的引用鏈,所以Handler不會(huì)被銷毀,Activity也不會(huì)被銷毀。

    正確創(chuàng)建Message實(shí)例

  • 通過(guò) Message 的靜態(tài)方法 Message.obtain() 獲取;

  • 通過(guò) Handler 的公有方法 handler.obtainMessage()

  • 所有的消息會(huì)被回收,放入sPool中,使用享元設(shè)計(jì)模式。

    Handler深層次問(wèn)題解答

    ThreadLocal

    ThreadLocal為每個(gè)線程都提供了變量的副本,使得每個(gè)線程在某一時(shí)間訪問(wèn)到的并非同一個(gè)對(duì)象,這樣就隔離了多個(gè)線程對(duì)數(shù)據(jù)的數(shù)據(jù)共享。如果讓你設(shè)計(jì)一個(gè)ThreadLocal,ThreadLocal 的目標(biāo)是讓不同的線程有不同的變量 V,那最直接的方法就是創(chuàng)建一個(gè) Map,它的 Key 是線程,Value 是每個(gè)線程擁有的變量 V,ThreadLocal 內(nèi)部持有這樣的一個(gè) Map 就可以了。你可能會(huì)設(shè)計(jì)成這樣。

    實(shí)際上Java的實(shí)現(xiàn)是下面這樣,Java 的實(shí)現(xiàn)里面也有一個(gè) Map,叫做 ThreadLocalMap,不過(guò)持有 ThreadLocalMap 的不是 ThreadLocal,而是 Thread。Thread 這個(gè)類內(nèi)部有一個(gè)私有屬性 threadLocals,其類型就是 ThreadLocalMap,ThreadLocalMap 的 Key 是 ThreadLocal。

    精簡(jiǎn)之后的代碼如下:

    class?Thread?{//內(nèi)部持有ThreadLocalMapThreadLocal.ThreadLocalMap?threadLocals; } class?ThreadLocal<T>{public?T?get()?{//首先獲取線程持有的//ThreadLocalMapThreadLocalMap?map?=Thread.currentThread().threadLocals;//在ThreadLocalMap中//查找變量Entry?e?=?map.getEntry(this);return?e.value;??}static?class?ThreadLocalMap{//內(nèi)部是數(shù)組而不是MapEntry[]?table;//根據(jù)ThreadLocal查找EntryEntry?getEntry(ThreadLocal?key){//省略查找邏輯}//Entry定義static?class?Entry?extendsWeakReference<ThreadLocal>{Object?value;}} }

    在Java的實(shí)現(xiàn)方案中,ThreadLocal僅僅只是一個(gè)代理工具類,內(nèi)部并不持有任何線程相關(guān)的數(shù)據(jù),所有和線程相關(guān)的數(shù)據(jù)都存儲(chǔ)在Thread里面,這樣的設(shè)計(jì)從數(shù)據(jù)的親緣性上來(lái)講,ThreadLocalMap屬于Thread也更加合理。所以ThreadLocal的get方法,其實(shí)就是拿到每個(gè)線程獨(dú)有的ThreadLocalMap。

    還有一個(gè)原因,就是不容易產(chǎn)生內(nèi)存泄漏,如果用我們的設(shè)計(jì)方案,ThreadLocal持有的Map會(huì)持有Thread對(duì)象的引用,這就意味著只要ThreadLocal對(duì)象存在,那么Map中的Thread對(duì)象就永遠(yuǎn)不會(huì)被回收。ThreadLocal的生命周期往往都比線程要長(zhǎng),所以這種設(shè)計(jì)方案很容易導(dǎo)致內(nèi)存泄漏。

    而Java的實(shí)現(xiàn)中Thread持有ThreadLocalMap,而且ThreadLocalMap里對(duì)ThreadLocal的引用還是弱引用,所以只要Thread對(duì)象可以被回收,那么ThreadLocalMap就能被回收。Java的實(shí)現(xiàn)方案雖然看上去復(fù)雜一些,但是更安全。

    ThreadLocal與內(nèi)存泄漏

    但是一切并不總是那么完美,如果在線程池中使用ThreadLocal可能會(huì)導(dǎo)致內(nèi)存泄漏,原因是線程池中線程的存活時(shí)間太長(zhǎng),往往和程序都是同生共死的,這就意味著Thread持有的ThreadLocalMap一直都不會(huì)被回收,再加上ThreadLocalMap中的Entry對(duì)ThreadLocal是弱引用,所以只要ThreadLocal結(jié)束了自己的生命周期是可以被回收掉的。但是Entry中的Value卻是被Entry強(qiáng)引用的,所以即便Value的生命周期結(jié)束了,Value也是無(wú)法被回收的,從而導(dǎo)致內(nèi)存泄漏。

    所以我們可以通過(guò)try{}finally{}方案來(lái)手動(dòng)釋放資源

    ExecutorService?es; ThreadLocal?tl; es.execute(()->{//ThreadLocal增加變量tl.set(obj);try?{//?省略業(yè)務(wù)邏輯代碼}finally?{//手動(dòng)清理ThreadLocal?tl.remove();} });

    epoll機(jī)制

    epoll機(jī)制在Handler中的應(yīng)用,在主線程的 MessageQueue 沒(méi)有消息時(shí),便阻塞在 loop 的 queue.next() 中的 nativePollOnce() 方法里,最終調(diào)用到epoll_wait()進(jìn)行阻塞等待。此時(shí)主線程會(huì)釋放 CPU 資源進(jìn)入休眠狀態(tài),直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生,通過(guò)往 pipe 管道寫端寫入數(shù)據(jù)來(lái)喚醒主線程工作。這里采用的 epoll 機(jī)制,是一種IO多路復(fù)用機(jī)制,可以同時(shí)監(jiān)控多個(gè)描述符,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮?#xff0c;本質(zhì)同步I/O,即讀寫是阻塞的。所以說(shuō),主線程大多數(shù)時(shí)候都是處于休眠狀態(tài),并不會(huì)消耗大量CPU資源。

  • 表面上看epoll的性能最好,但是在連接數(shù)少并且連接都十分活躍的情況下,select和poll的性能可能比epoll好,畢竟epoll的通知機(jī)制需要很多函數(shù)回調(diào)。

  • select低效是因?yàn)槊看嗡夹枰喸儭5托б彩窍鄬?duì)的,視情況而定,也可通過(guò)良好的設(shè)計(jì)改善。

  • 之所以選擇Handler底層選擇epoll機(jī)制,我感覺(jué)是epoll在效率上更高。在select/poll中,進(jìn)程只有在調(diào)用一定的方法后,內(nèi)核才對(duì)所有監(jiān)視的文件描述符進(jìn)行掃描,而epoll事先通過(guò)epoll_ctl()來(lái)注冊(cè)一個(gè)文件描述符,一旦基于某個(gè)文件描述符就緒時(shí),內(nèi)核會(huì)采用類似callback的回調(diào)機(jī)制,迅速激活這個(gè)文件描述符,當(dāng)進(jìn)程調(diào)用epoll_wait()時(shí)便得到通知。(此處去掉了遍歷文件描述符,而是通過(guò)監(jiān)聽(tīng)回調(diào)的的機(jī)制。這正是epoll的魅力所在。)

    Handler的同步屏障機(jī)制

    如果有一個(gè)緊急的Message需要優(yōu)先處理,該怎么做?這其實(shí)涉及到架構(gòu)方面的設(shè)計(jì)了,通用場(chǎng)景和特殊場(chǎng)景的設(shè)計(jì)。你可能會(huì)想到sendMessageAtFrontOfQueue()這個(gè)方法,實(shí)際也遠(yuǎn)遠(yuǎn)不只是如此,Handler中加入了同步屏障這種機(jī)制,來(lái)實(shí)現(xiàn)[異步消息優(yōu)先]執(zhí)行的功能。

    postSyncBarrier()發(fā)送同步屏障,removeSyncBarrier()移除同步屏障。

    同步屏障的作用可以理解成攔截同步消息的執(zhí)行,主線程的 Looper 會(huì)一直循環(huán)調(diào)用 MessageQueue 的 next() 來(lái)取出隊(duì)頭的 Message 執(zhí)行,當(dāng) Message 執(zhí)行完后再去取下一個(gè)。當(dāng) next() 方法在取 Message 時(shí)發(fā)現(xiàn)隊(duì)頭是一個(gè)同步屏障的消息時(shí),就會(huì)去遍歷整個(gè)隊(duì)列,只尋找設(shè)置了異步標(biāo)志的消息,如果有找到異步消息,那么就取出這個(gè)異步消息來(lái)執(zhí)行,否則就讓 next() 方法陷入阻塞狀態(tài)。如果 next() 方法陷入阻塞狀態(tài),那么主線程此時(shí)就是處于空閑狀態(tài)的,也就是沒(méi)在干任何事。所以,如果隊(duì)頭是一個(gè)同步屏障的消息的話,那么在它后面的所有同步消息就都被攔截住了,直到這個(gè)同步屏障消息被移除出隊(duì)列,否則主線程就一直不會(huì)去處理同步屏幕后面的同步消息。

    而所有消息默認(rèn)都是同步消息,只有手動(dòng)設(shè)置了異步標(biāo)志,這個(gè)消息才會(huì)是異步消息。另外,同步屏障消息只能由內(nèi)部來(lái)發(fā)送,這個(gè)接口并沒(méi)有公開(kāi)給我們使用。

    Choreographer 里所有跟 message 有關(guān)的代碼,你會(huì)發(fā)現(xiàn),都手動(dòng)設(shè)置了異步消息的標(biāo)志,所以這些操作是不受到同步屏障影響的。這樣做的原因可能就是為了盡可能保證上層 app 在接收到屏幕刷新信號(hào)時(shí),可以在第一時(shí)間執(zhí)行遍歷繪制 View 樹(shù)的工作。

    Choreographer 過(guò)程中的動(dòng)作也都是異步消息,這樣可以確保 Choreographer 的順利運(yùn)轉(zhuǎn),也確保了第一時(shí)間執(zhí)行 doTraversal(doTraversal → performTraversals 就是執(zhí)行 view 的 layout、measure、draw),這個(gè)過(guò)程中如果有其他同步消息,也無(wú)法得到處理,都要等到 doTraversal 之后。

    因?yàn)橹骶€程中如果有太多消息要執(zhí)行,而這些消息又是根據(jù)時(shí)間戳進(jìn)行排序,如果不加一個(gè)同步屏障的話,那么遍歷繪制 View 樹(shù)的工作就可能被迫延遲執(zhí)行,因?yàn)樗残枰抨?duì),那么就有可能出現(xiàn)當(dāng)一幀都快結(jié)束的時(shí)候才開(kāi)始計(jì)算屏幕數(shù)據(jù),那即使這次的計(jì)算少于 16.6ms,也同樣會(huì)造成丟幀現(xiàn)象。

    那么,有了同步屏障消息的控制就能保證每次一接收到屏幕刷新信號(hào)就第一時(shí)間處理遍歷繪制 View 樹(shù)的工作么?

    只能說(shuō),同步屏障是盡可能去做到,但并不能保證一定可以第一時(shí)間處理。因?yàn)?#xff0c;同步屏障是在 scheduleTraversals() 被調(diào)用時(shí)才發(fā)送到消息隊(duì)列里的,也就是說(shuō),只有當(dāng)某個(gè) View 發(fā)起了刷新請(qǐng)求時(shí),在這個(gè)時(shí)刻后面的同步消息才會(huì)被攔截掉。如果在 scheduleTraversals() 之前就發(fā)送到消息隊(duì)列里的工作仍然會(huì)按順序依次被取出來(lái)執(zhí)行。

    下面是部分詳細(xì)的分析:

    WindowManager維護(hù)著所有的Activity的DecorView和ViewRootImpl。在前面我們講過(guò),WindowManagerGlobal的addView方法中中初始化了ViewRootImpl,然后調(diào)用它的setView方法,將DecorView作為參數(shù)傳遞了進(jìn)去。所以我們看看ViewRootImpl做了什么。

    //ViewRootImpl.java //view是DecorView public?void?setView(View?view,?WindowManager.LayoutParams?attrs,?View?panelParentView)?{synchronized?(this)?{if?(mView?==?null)?{mView?=?view;···//?Schedule?the?first?layout?-before-?adding?to?the?window//?manager,?to?make?sure?we?do?the?relayout?before?receiving//?any?other?events?from?the?system.requestLayout();?//發(fā)起布局請(qǐng)求···view.assignParent(this);?//將當(dāng)前ViewRootImpl對(duì)象this作為參數(shù)調(diào)用了DecorView的????????????????????assignParent···}} }

    在setView()方法里調(diào)用了DecorView的assignParent。

    //View.java /**?Caller?is?responsible?for?calling?requestLayout?if?necessary.*?(This?allows?addViewInLayout?to?not?request?a?new?layout.)*/ @UnsupportedAppUsage void?assignParent(ViewParent?parent)?{if?(mParent?==?null)?{mParent?=?parent;}?else?if?(parent?==?null)?{mParent?=?null;}?else?{throw?new?RuntimeException("view?"?+?this?+?"?being?added,?but"+?"?it?already?has?a?parent");} }

    參數(shù)是ViewParent,而ViewRootImpl是實(shí)現(xiàn)了ViewParent接口的,所以在這里就將DecorView和ViewRootImpl綁定起來(lái)了。每個(gè)Activity的根布局都是DecorView,而DecorView的parent又是ViewRootImpl,所以在子View里執(zhí)行invalidate()之類的工作,循環(huán)找parent,最后都會(huì)找到ViewRootImpl里來(lái)。所以實(shí)際上View的刷新都是由ViewRootImpl來(lái)控制的。

    即使是界面上一個(gè)小小的 View 發(fā)起了重繪請(qǐng)求時(shí),都要層層走到 ViewRootImpl,由它來(lái)發(fā)起重繪請(qǐng)求,然后再由它來(lái)開(kāi)始遍歷 View 樹(shù),一直遍歷到這個(gè)需要重繪的 View 再調(diào)用它的 onDraw() 方法進(jìn)行繪制。

    View.invalidate()請(qǐng)求重繪的操作最后調(diào)用到的是ViewRootImpl.scheduleTraversals(),而ViewRootImpl.setView()方法中調(diào)用了requestLayout方法。

    @Override public?void?requestLayout()?{if?(!mHandlingLayoutInLayoutRequest)?{checkThread();mLayoutRequested?=?true;scheduleTraversals();} }

    最終也調(diào)用到了scheduleTraversals()方法,其實(shí)這個(gè)方法是屏幕刷新的關(guān)鍵。

    其實(shí)打開(kāi)一個(gè) Activity,當(dāng)它的 onCreate---onResume 生命周期都走完后,才將它的 DecoView 與新建的一個(gè) ViewRootImpl 對(duì)象綁定起來(lái),同時(shí)開(kāi)始安排一次遍歷 View 任務(wù)也就是繪制 View 樹(shù)的操作等待執(zhí)行,然后將 DecoView 的 parent 設(shè)置成 ViewRootImpl 對(duì)象。所以我們?cè)趏nCreate~onResume中獲取不到View寬高,界面的繪制也是在onResume之后才開(kāi)始執(zhí)行的。

    ViewRootImpl.scheduleTraversals()的一系列分析以及屏幕刷新機(jī)制可以參考這篇文章,這里的內(nèi)容也是大部分參考它的,同步屏障相關(guān)的分析內(nèi)容也在里面。Choreographer主要作用是協(xié)調(diào)動(dòng)畫,輸入和繪制的時(shí)間,它從顯示子系統(tǒng)接收定時(shí)脈沖(例如垂直同步),然后安排渲染下一個(gè)frame的一部分工作。可通過(guò)Choreographer.getInstance().postFrameCallback()來(lái)監(jiān)聽(tīng)?zhēng)是闆r。

    public?class?FPSFrameCallback?implements?Choreographer.FrameCallback?{private?static?final?String?TAG?=?"FPS_TEST";private?long?mLastFrameTimeNanos;private?long?mFrameIntervalNanos;public?FPSFrameCallback(long?lastFrameTimeNanos)?{mLastFrameTimeNanos?=?lastFrameTimeNanos;//每一幀渲染時(shí)間?多少納秒mFrameIntervalNanos?=?(long)?(1000000000?/?60.0);}@Overridepublic?void?doFrame(long?frameTimeNanos)?{?//Vsync信號(hào)到來(lái)的時(shí)間frameTimeNanos//初始化時(shí)間if?(mLastFrameTimeNanos?==?0)?{//上一幀的渲染時(shí)間mLastFrameTimeNanos?=?frameTimeNanos;}final?long?jitterNanos?=?frameTimeNanos?-?mLastFrameTimeNanos;if?(jitterNanos?>=?mFrameIntervalNanos)?{final?long?skippedFrames?=?jitterNanos?/?mFrameIntervalNanos;if?(skippedFrames?>?5)?{Log.d(TAG,?"Skipped?"?+?skippedFrames?+?"?frames!??"+?"The?application?may?be?doing?too?much?work?on?its?main?thread.");}}mLastFrameTimeNanos?=?frameTimeNanos;//注冊(cè)下一幀回調(diào)Choreographer.getInstance().postFrameCallback(this);} }

    調(diào)用方式在Application中注冊(cè)。

    Choreographer.getInstance().postFrameCallback(FPSFrameCallback(System.nanoTime()))

    丟幀的原因:造成丟幀大體上有兩類原因,一是遍歷繪制 View 樹(shù)計(jì)算屏幕數(shù)據(jù)的時(shí)間超過(guò)了 16.6ms;二是,主線程一直在處理其他耗時(shí)的消息,導(dǎo)致遍歷繪制 View 樹(shù)的工作遲遲不能開(kāi)始,從而超過(guò)了 16.6 ms 底層切換下一幀畫面的時(shí)機(jī)。

    Handler鎖相關(guān)問(wèn)題

    既然可以存在多個(gè)Handler往MessageQueue中添加數(shù)據(jù)(發(fā)送消息時(shí)各個(gè)Handler可能處于不同線程),那它內(nèi)部是如何確保線程安全的?

    Handler.sendXXX,Handler.postXXX最終會(huì)會(huì)調(diào)到MessageQueue的enqueueMessage方法。

    源碼如下:

    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)?{···}???? }

    其內(nèi)部通過(guò)synchronized關(guān)鍵字保證線程安全。同時(shí)messagequeue.next()內(nèi)部也會(huì)通過(guò)synchronized加鎖,確保取的時(shí)候線程安全,同時(shí)插入也會(huì)加鎖。這個(gè)問(wèn)題其實(shí)不難,只是看你有沒(méi)有了解源碼。

    Handler中的同步方法

    如何讓handler.post消息執(zhí)行之后然后再繼續(xù)往下執(zhí)行,同步方法runWithScissors。

    public?final?boolean?runWithScissors(@NonNull?Runnable?r,?long?timeout)?{if?(r?==?null)?{throw?new?IllegalArgumentException("runnable?must?not?be?null");}if?(timeout?<?0)?{throw?new?IllegalArgumentException("timeout?must?be?non-negative");}if?(Looper.myLooper()?==?mLooper)?{r.run();return?true;}BlockingRunnable?br?=?new?BlockingRunnable(r);return?br.postAndWait(this,?timeout); }

    Handler在系統(tǒng)以及第三方框架的一些應(yīng)用

    HandlerThread

    HandlerThread繼承于Thread,顧名思義,實(shí)際上是Handler和Thread的一個(gè)封裝,已經(jīng)為我們封裝的很好很安全了,內(nèi)部也通過(guò)synchronized來(lái)保證線程安全,比如getLooper方法

    public?Looper?getLooper()?{if?(!isAlive())?{return?null;}//?If?the?thread?has?been?started,?wait?until?the?looper?has?been?created.synchronized?(this)?{while?(isAlive()?&&?mLooper?==?null)?{try?{wait();}?catch?(InterruptedException?e)?{}}}return?mLooper;}

    在線程的run方法里,所以當(dāng)線程啟動(dòng)之后才能創(chuàng)建Looper并賦值給mLooper,這里的阻塞就是為了等待Looper的創(chuàng)建成功。同時(shí)該方法是用Public修飾的,說(shuō)明該方法是提供外部調(diào)用的,Looper創(chuàng)建成功提供給外部使用。

    IntentService

    簡(jiǎn)單看一下源碼就能看到Handler的應(yīng)用,Handler的handMessage最終會(huì)回調(diào)到onHandleIntent方法。

    public?abstract?class?IntentService?extends?Service?{private?volatile?Looper?mServiceLooper;@UnsupportedAppUsageprivate?volatile?ServiceHandler?mServiceHandler;

    如何打造一個(gè)不崩潰的程序

    打造一個(gè)不崩潰的程序,可以參考我的這篇文章:

    https://juejin.cn/post/6856654877142188046#heading-2

    Glide中的應(yīng)用

    Glide 相信大應(yīng)該非常熟悉了,我們都知道Glide生命周期的控制(如果不了解,可以看下Glide相關(guān)文章的分析,跟LiveData 是同一個(gè)原理)是通過(guò)添加一個(gè)空的Fragment到Activity 或者Fragment中,然后通過(guò)FragmentMannager管理Fragment的生命周期,從而達(dá)到生命周期的控制。下面是節(jié)選了Glide一段添加Fragment的代碼:

    private?RequestManagerFragment?getRequestManagerFragment(@NonNull?final?android.app.FragmentManager?fm,@Nullable?android.app.Fragment?parentHint,boolean?isParentVisible)?{//1.通過(guò)FragmentManager獲取?RequestManagerFragment,如果已添加到FragmentManager則返回實(shí)例,否則為空RequestManagerFragment?current?=?(RequestManagerFragment)?fm.findFragmentByTag(FRAGMENT_TAG);if?(current?==?null)?{//2.如果fm里面沒(méi)有則從map緩存里取current?=?pendingRequestManagerFragments.get(fm);if?(current?==?null)?{//3.第1和2步都沒(méi)有,說(shuō)明確實(shí)沒(méi)創(chuàng)建,則走創(chuàng)建的流程current?=?new?RequestManagerFragment();current.setParentFragmentHint(parentHint);if?(isParentVisible)?{current.getGlideLifecycle().onStart();}//4.將新創(chuàng)建的fragment?保存到map容器pendingRequestManagerFragments.put(fm,?current);//5.發(fā)送添加fragment事務(wù)事件fm.beginTransaction().add(current,?FRAGMENT_TAG).commitAllowingStateLoss();//6.發(fā)送remove?本地緩存事件handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER,?fm).sendToTarget();}}return?current; }//跟上面那個(gè)方法唯一的區(qū)別是?這個(gè)是Fragment?的FragmentManager,上面的是Acitivity?的FragmentManager private?SupportRequestManagerFragment?getSupportRequestManagerFragment(@NonNull?final?FragmentManager?fm,?@Nullable?Fragment?parentHint,?boolean?isParentVisible)?{SupportRequestManagerFragment?current?=(SupportRequestManagerFragment)?fm.findFragmentByTag(FRAGMENT_TAG);if?(current?==?null)?{current?=?pendingSupportRequestManagerFragments.get(fm);if?(current?==?null)?{current?=?new?SupportRequestManagerFragment();current.setParentFragmentHint(parentHint);if?(isParentVisible)?{current.getGlideLifecycle().onStart();}pendingSupportRequestManagerFragments.put(fm,?current);fm.beginTransaction().add(current,?FRAGMENT_TAG).commitAllowingStateLoss();handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER,?fm).sendToTarget();}}return?current; }@Override public?boolean?handleMessage(Message?message)?{boolean?handled?=?true;Object?removed?=?null;Object?key?=?null;switch?(message.what)?{case?ID_REMOVE_FRAGMENT_MANAGER://7.移除緩存android.app.FragmentManager?fm?=?(android.app.FragmentManager)?message.obj;key?=?fm;removed?=?pendingRequestManagerFragments.remove(fm);break;//省略代碼...}//省略代碼...return?handled; }

    看了上面的代碼,大家可能會(huì)有疑惑。

    • Fragment添加到FragmentManager為什么還要保存到map容器里(第4步)?

    • 判斷Fragment是否已添加為啥還要從map容器判斷(第2步),FragmentManager 去find 不就可以做到了嗎?

    其實(shí)答案很簡(jiǎn)單,學(xué)了Handler原理之后我們知道:就是在第5步執(zhí)行完之后并沒(méi)有將Fragment添加到FragmentManager(事件排隊(duì)中),而是發(fā)送添加Fragment的事件。接下來(lái)我們看代碼:

    //FragmentManagerImpl.java void?scheduleCommit()?{synchronized?(this)?{boolean?postponeReady?=mPostponedTransactions?!=?null?&&?!mPostponedTransactions.isEmpty();boolean?pendingReady?=?mPendingActions?!=?null?&&?mPendingActions.size()?==?1;if?(postponeReady?||?pendingReady)?{mHost.getHandler().removeCallbacks(mExecCommit);mHost.getHandler().post(mExecCommit);updateOnBackPressedCallbackEnabled();}} }

    添加Fragment 最終會(huì)走到FragmentManagerImpl的 scheduleCommit方法,我們可以看到他是通過(guò)Handler 發(fā)送事件。

    這也就解釋了為什么第5步執(zhí)行完之后Fragment為什么沒(méi)有立即添加到FragmentManager,所以需要Map緩存Fragment來(lái)標(biāo)記是否有Fragment添加。再接著有了第6步發(fā)送移除Map緩存的消息,因?yàn)镠andler處理消息是有序的。

    /? ?總結(jié)? ?/

    本文其實(shí)對(duì)源碼的分析并沒(méi)有非常仔細(xì),卻從整個(gè)系統(tǒng)各個(gè)方面的運(yùn)用進(jìn)行的不同的擴(kuò)展,以及一些第三方框架中的使用,希望本文對(duì)你有幫助,喜歡的點(diǎn)個(gè)贊吧~

    推薦閱讀:

    我又開(kāi)發(fā)了一個(gè)非常好用的開(kāi)源庫(kù)

    我的新書,《第一行代碼?第3版》已出版!

    Android上的網(wǎng)絡(luò)抓包原來(lái)是這樣工作的

    歡迎關(guān)注我的公眾號(hào)

    學(xué)習(xí)技術(shù)或投稿

    長(zhǎng)按上圖,識(shí)別圖中二維碼即可關(guān)注

    總結(jié)

    以上是生活随笔為你收集整理的Handler的内功心法,值得拥有!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    香蕉视频在线播放 | 久久综合网色—综合色88 | 97久久久免费福利网址 | 亚洲无吗视频在线 | 精品亚洲男同gayvideo网站 | 色九九在线 | 男女日麻批| 国产一区二区手机在线观看 | 99爱视频在线观看 | 欧美日韩在线观看一区 | 婷婷99| 日韩一级成人av | 日韩网站一区二区 | 中文字幕丝袜制服 | 国产综合小视频 | 午夜精品99久久免费 | 国产精品乱码高清在线看 | 国产精品午夜在线观看 | 特级a毛片 | 国产精品一区二区三区视频免费 | 久草视频在线资源站 | 免费a视频在线 | 亚洲毛片视频 | 日韩18p| 亚洲激情五月 | 色综合中文字幕 | 久久综合狠狠综合久久狠狠色综合 | 国产精品久久电影观看 | 国产精品毛片一区二区在线看 | 国产中文字幕网 | 国产伦理久久精品久久久久_ | 久久精品精品电影网 | 91av99| 最新av网站在线观看 | 一区二区视频免费在线观看 | 中文字幕在| 午夜精品福利在线 | 黄色小网站免费看 | 久久久久久看片 | 日韩欧美在线第一页 | 麻豆高清免费国产一区 | 久久色网站 | 国内精品久久久久影院日本资源 | 亚洲欧洲中文日韩久久av乱码 | 久草在| 国产精品自在欧美一区 | 国产日韩欧美在线一区 | 免费看片日韩 | 欧美精品xxx | 人人插人人看 | 精品视频资源站 | av电影中文字幕 | 男女激情麻豆 | 亚洲经典中文字幕 | 狠狠躁18三区二区一区ai明星 | 欧美性春潮 | 久久这里只有精品9 | 99精品黄色片免费大全 | 黄色成人av | 激情丁香久久 | 国产一级黄色av | 91精选 | 中文字幕一区二区三区久久蜜桃 | 少妇激情久久 | 亚洲成人高清在线 | 成人av在线电影 | 国产99免费 | 最新中文在线视频 | 日韩在线观看高清 | 亚洲影视九九影院在线观看 | 亚洲理论在线 | 久久久久久久综合色一本 | 国产精品2019 | 久久tv | 性色av免费观看 | 毛片99| 午夜狠狠操 | 久久综合九色综合久99 | 中文字幕免费高清 | 手机av电影在线 | 99免费在线观看视频 | 天天操天天操天天操天天操天天操 | 黄色成人av | 亚洲精品乱码久久久久v最新版 | 午夜精品麻豆 | 黄色片视频免费 | 免费电影一区二区三区 | 波多野结衣最新 | 五月婷婷综合在线 | 一区二区精 | 久久久国产精品人人片99精片欧美一 | 国产1区2区3区精品美女 | av久久在线| 亚洲高清视频在线 | 中文字幕在线久一本久 | 99久久99久久免费精品蜜臀 | 99热播精品| 在线观看不卡视频 | 国产精品丝袜久久久久久久不卡 | 狠狠色丁香婷婷综合橹88 | 黄色亚洲 | 视频在线观看99 | 午夜手机看片 | 毛片播放网站 | 久久在草 | 亚洲另类视频在线 | 久久国产经典视频 | 一区二区三区国产欧美 | 免费一级片观看 | 亚洲经典在线 | 国产精品黑丝在线观看 | 成人毛片在线视频 | 日韩精品久久一区二区三区 | 超碰在97 | 欧美日韩不卡在线 | 亚洲天堂网在线观看视频 | 粉嫩高清一区二区三区 | www·22com天天操| 国产又黄又硬又爽 | 久久专区| 毛片精品免费在线观看 | 爱色av.com | 久久久久在线 | 最新日韩视频在线观看 | 天天摸天天舔天天操 | 色吊丝在线永久观看最新版本 | 91精品视频在线播放 | 99免费在线观看 | 久久经典国产 | 99精品偷拍视频一区二区三区 | 久久久久久久久久久久电影 | 97看片吧| 久久免费在线 | 色a资源在线 | 日韩大片在线 | 美女网站色 | 人人插人人 | 久久福利电影 | 亚洲精品美女免费 | 国产在线观看中文字幕 | 激情视频在线观看网址 | 五月婷在线 | 国产成人av一区二区三区在线观看 | 99久久精品国产一区 | 97精品免费视频 | 国产精品毛片一区 | av在线免费在线 | 久久艹欧美 | 日韩在线观看 | 人人爽人人爽人人 | 久久久999精品视频 国产美女免费观看 | 久久久久日本精品一区二区三区 | 亚洲成人黄 | 久久天天躁夜夜躁狠狠85麻豆 | 91热在线| 超碰97免费 | 在线观看视频97 | 蜜臀av夜夜澡人人爽人人 | 久草爱 | 日韩黄色免费 | 久久免费精品一区二区三区 | 亚洲天天做 | 免费三级骚 | 天天干天天操人体 | 中文字幕在线免费97 | 手机成人在线电影 | 麻豆视频www | 狠狠色综合欧美激情 | 日韩一区二区三区免费电影 | 久久综合爱 | 天天天天天天天操 | 婷婷在线视频 | 日韩精品无 | 色视频成人在线观看免 | 久久69av | 欧美乱码精品一区二区 | 中文字幕在线国产 | 久久99精品久久久久久 | 又黄又爽又无遮挡的视频 | 欧美9999 | 精品久久综合 | 天天干夜夜爱 | 中文字幕在线播放日韩 | x99av成人免费 | 国产精品久久一区二区三区不卡 | 福利av在线 | 极品国产91在线网站 | 一区二区三区在线影院 | 国产黄色免费电影 | 黄色免费国产 | 国产精品一区二区精品视频免费看 | 成人免费色 | 精品中文字幕在线播放 | 国产主播大尺度精品福利免费 | 九草视频在线观看 | 91污污视频在线观看 | 午夜视频在线观看一区二区三区 | 午夜少妇一区二区三区 | 色.www| 手机看片中文字幕 | 97精品国自产拍在线观看 | 免费在线国产精品 | 狠狠搞,com | 亚洲国产日韩av | 久草综合视频 | 亚洲精品视频在线 | 久久艹在线| 亚洲精品视频免费观看 | 日韩欧美在线综合网 | 亚洲午夜精品久久久 | www.狠狠操.com| 香蕉视频久久 | www91在线观看| 天天躁日日躁狠狠躁av麻豆 | 久久综合狠狠综合久久狠狠色综合 | 超碰久热| 天天天操操操 | 欧美va天堂在线电影 | 久久久久久久久久久免费视频 | 亚洲乱码国产乱码精品天美传媒 | 欧美日韩国产成人 | 日韩免费中文字幕 | 在线看不卡av| 在线成人一区 | 婷婷精品国产欧美精品亚洲人人爽 | 亚洲综合激情小说 | 麻豆国产网站 | 国产精品久久久久久久久岛 | 国产精品久久久一区二区三区网站 | 国产小视频国产精品 | 国产精品久久久久一区二区三区 | 伊人久久一区 | 久久久久久激情 | 久久影视一区 | 久久这里只有精品视频首页 | 国产精品成人自产拍在线观看 | 伊人五月在线 | 97在线观看免费高清完整版在线观看 | 国产精品久久久久久久久大全 | 美女很黄免费网站 | 91大神精品视频 | 亚洲精品免费视频 | 精品国产精品国产偷麻豆 | 亚洲五月激情 | 久二影院| 草久热| 国产精品麻豆免费版 | 五月婷婷色综合 | 黄色大全在线观看 | 狠狠色狠狠色合久久伊人 | 午夜精品久久久久久99热明星 | 天天射天天干天天操 | 欧美日韩高清在线 | 伊人久久国产精品 | 在线三级播放 | 91精品一区二区三区蜜臀 | 97人人精品 | 97日日| 天天躁日日 | 在线综合色 | 国产a级精品 | 天天搞天天干 | 黄色在线网站噜噜噜 | 成人在线观看av | 久草视频免费看 | 奇米影音四色 | 色综合久久88色综合天天6 | 日韩动漫免费观看高清完整版在线观看 | 六月色婷 | 天天天干| 日韩精品视频在线免费观看 | 91av手机在线观看 | www色网站 | 色偷偷av男人天堂 | 在线亚洲高清视频 | 久久综合九色欧美综合狠狠 | 日本久久久影视 | 久久av免费 | 丁香婷婷综合网 | 久草网站 | 久久精品中文字幕免费mv | 美女在线免费观看视频 | 精品美女久久 | 日韩在线大片 | 免费看的黄色录像 | 午夜视频一区二区 | 久久精品视频免费观看 | 亚洲午夜久久久综合37日本 | 97超碰免费在线观看 | 久久久久国产精品免费免费搜索 | 成人国产精品一区 | 国产剧在线观看片 | 91精品久久久久久久99蜜桃 | 欧洲精品视频一区二区 | 国产一区二区在线影院 | 99热只有精品在线观看 | 欧美日本在线视频 | 在线观看国产麻豆 | 中文字幕一区二区三区在线视频 | 久色 网| 国产午夜精品一区 | 亚洲国产网站 | 日韩在线观看视频网站 | 国内精品久久久久影院优 | 久久久久婷 | 九九99视频 | 夜夜操夜夜干 | 色综合天天视频在线观看 | 国产精品永久免费观看 | 99视频黄| 婷婷日| 五月婷婷中文 | 国产精品成久久久久三级 | 欧美日韩在线观看不卡 | 麻豆91在线观看 | 91精品国自产在线 | 国产一区欧美在线 | 亚洲va欧美va | 久久黄网站 | a极黄色片 | 久久96国产精品久久99漫画 | 色妞色视频一区二区三区四区 | 婷婷久久久 | 免费视频99 | 色妞色视频一区二区三区四区 | 日本中文字幕电影在线免费观看 | 久草免费在线观看视频 | 国产二区av| 在线观看日韩精品 | av中文字幕免费在线观看 | 天堂久色 | 日韩在线视频免费观看 | 午夜免费福利片 | 国产一区欧美日韩 | 在线观看视频国产一区 | 97成人在线观看视频 | 久久久久综合精品福利啪啪 | 久久爱导航 | 久久人人爽人人爽 | 99精品在线免费 | 婷婷丁香七月 | 久久久毛片 | 久久人人爽爽 | 国产精品99久久久久久宅男 | 国产成人精品综合久久久久99 | 久久国产精品视频免费看 | 亚洲精品国偷拍自产在线观看蜜桃 | 久久精品国产一区二区三区 | 久久国产精品影片 | 丁香综合 | 香蕉视频色 | 国产视频精选在线 | 欧美一级性视频 | 色国产精品| 日韩免费观看一区二区三区 | av品善网 | 一区二区三区播放 | 日韩欧美高清一区二区三区 | 国产午夜精品一区二区三区四区 | 久草视频免费 | 91亚色视频 | 久久国内精品 | 国产精品久久久av久久久 | 欧美日韩三区二区 | 国产精品 国产精品 | 国内精品视频在线播放 | 手机av电影在线 | 天天色天天综合 | 97爱| 国产一区在线播放 | 成年人黄色免费网站 | 久久激情电影 | 欧美二区三区91 | 日韩一区精品 | 亚洲免费一级 | 亚州视频在线 | 亚洲一区二区三区四区在线视频 | 在线天堂8√ | 欧美a√大片 | 久久精品免费电影 | 超碰在线1 | 国产精品69av | 色香网 | 国产成人精品av在线观 | 久久精品99精品国产香蕉 | 国产精品久久久久久久久岛 | 日本黄色大片儿 | 久久在线观看视频 | 亚洲一区网站 | 麻豆传媒一区二区 | 中文字幕 国产视频 | 在线观看中文字幕视频 | 久久综合色天天久久综合图片 | 亚洲成色 | 精品国产乱码久久久久久1区二区 | 99在线精品免费视频九九视 | 成年人在线观看网站 | 色www精品视频在线观看 | 四虎影视8848aamm| 日韩最新在线 | 日韩一区二区三免费高清在线观看 | 国产经典三级 | 黄色录像av | 日日激情 | aⅴ精品av导航 | 国精产品999国精产品视频 | 久久精品国产一区二区三区 | 国内少妇自拍视频一区 | 欧美做受xxx | 免费观看9x视频网站在线观看 | 看v片| 久久噜噜少妇网站 | 久久久久久久久毛片精品 | 日韩在线观看视频在线 | 国产色网 | 国内成人精品2018免费看 | 免费久久99精品国产 | 在线直播av | 免费欧美高清视频 | 中文字幕在 | 亚洲欧美日韩一区二区三区在线观看 | 日韩在线免费小视频 | 国产一二三在线视频 | www黄色com | 欧美日韩高清一区二区三区 | 中文字幕久久网 | 欧美日本在线视频 | 99r在线播放 | 国产成人精品一二三区 | 日韩r级在线 | 亚洲日本韩国一区二区 | 美女视频久久 | 人人干人人艹 | 中文字幕在线免费观看 | 免费在线观看黄色网 | 久久视频| 国产999精品久久久久久 | 精品国产伦一区二区三区观看方式 | 91在线免费观看国产 | 成人免费91 | 久久经典视频 | 久久久久久国产一区二区三区 | 久久久www成人免费毛片麻豆 | 在线观看久草 | 精品99999| 免费福利视频网 | 99这里只有久久精品视频 | 国产小视频精品 | 在线观看成年人 | 日韩精品久久久久久久电影99爱 | 日韩精品黄 | 久久好看免费视频 | 色婷婷天天干 | 在线观看av不卡 | 99视频偷窥在线精品国自产拍 | 日韩激情第一页 | 亚洲久久视频 | 99精品在线观看视频 | 亚洲年轻女教师毛茸茸 | 婷婷综合导航 | 亚洲综合欧美激情 | 毛片随便看 | 在线观看成人一级片 | 欧美日韩午夜在线 | 亚洲天天综合 | 久久久国产精品麻豆 | 亚洲精品动漫成人3d无尽在线 | 人人爱夜夜操 | 肉色欧美久久久久久久免费看 | 中文av字幕在线观看 | 91av视频网站 | 亚洲伊人av | 国产精品99视频 | 97视频网站 | 免费在线黄网 | 国产一级在线看 | 五月婷综合网 | 91大神视频网站 | 麻豆精品在线 | 国产精品久久久久高潮 | www.在线观看av | 欧美综合在线视频 | 成人在线视频免费观看 | 久久国产三级 | 欧美日韩国内在线 | 97国产大学生情侣白嫩酒店 | 丁香六月婷婷开心婷婷网 | 成人黄大片 | 久草在线免费色站 | 国产在线不卡一区 | 亚洲伊人网在线观看 | 国内精品在线观看视频 | av黄色影院 | 97超碰在 | 少妇bbb| 在线观看va | 可以免费看av | 久久精品一级片 | 亚洲天堂免费视频 | 久久国产经典视频 | 久久久久高清 | 久久99九九99精品 | 超薄丝袜一二三区 | 久草精品视频在线播放 | 国产亚洲精品久久久久久 | 日本性久久| 中文字幕久久精品一区 | www.久艹 | 国产亚洲综合在线 | 在线观看中文字幕亚洲 | 有码一区二区三区 | 国产精品国产亚洲精品看不卡 | 免费视频黄色 | 曰韩精品 | 伊人黄色网 | 久久久国产精品麻豆 | 在线中文日韩 | 激情五月综合网 | 伊人色综合久久天天 | 亚洲好视频 | 欧美 亚洲 另类 激情 另类 | 国产人成在线观看 | 久久久鲁| 成人午夜电影在线 | 狠狠色丁香婷婷综合久小说久 | 久久国产精品免费一区二区三区 | 人人玩人人添人人澡超碰 | 99精品国产99久久久久久97 | 久久婷婷五月综合色丁香 | 网站免费黄 | 欧日韩在线视频 | av性网站 | 日韩精品一区二区在线观看视频 | 日韩av电影中文字幕 | 色综合天天色综合 | www操操| 国内视频在线 | 国产视频精品久久 | 欧美日韩中文字幕在线视频 | 久久刺激视频 | 免费看一级黄色 | 免费亚洲视频在线观看 | 婷婷爱五月天 | 色av资源网 | 99r国产精品 | 国产精品欧美久久久久三级 | 亚洲精品国偷拍自产在线观看 | 久久成人精品 | zzijzzij亚洲日本少妇熟睡 | 91精品亚洲影视在线观看 | 在线韩国电影免费观影完整版 | 视频在线在亚洲 | 国产精品成人国产乱 | 精品美女在线观看 | 亚洲视频久久久久 | 色偷偷88888欧美精品久久久 | 欧美在线视频第一页 | 91精品在线免费观看视频 | 又黄又爽的视频在线观看网站 | 亚洲精品视频免费在线观看 | 国产精品九九视频 | 超黄视频网站 | avwww在线观看| 亚洲在线视频免费 | 97电影手机| 亚洲视频,欧洲视频 | 91视频免费看 | 国产五月天婷婷 | 国产中文自拍 | av电影中文字幕在线观看 | 丝袜制服综合网 | 日韩成人免费在线观看 | 久久久久福利视频 | 99久久精品国产网站 | a视频在线播放 | 黄污视频大全 | 91av手机在线观看 | 波多野结衣久久精品 | 最近中文字幕免费 | 国产一二区视频 | 午夜美女网站 | 亚洲第一av在线播放 | 精产嫩模国品一二三区 | 成人宗合网 | 日韩午夜剧场 | 久草手机视频 | 婷婷网在线 | 久久久国产精品久久久 | 欧美日韩免费一区二区三区 | 97免费在线观看视频 | 91av视频播放 | 在线观看中文字幕第一页 | 久久精品99久久 | 三级av小说 | 一级片免费视频 | av资源网在线播放 | 日韩r级电影在线观看 | 激情久久综合 | 久久久久久97三级 | 狠狠色噜噜狠狠狠 | 午夜久久久久久久 | av色图天堂网 | 天天干天天想 | 超碰在线91 | 欧美成年网站 | 99热免费在线 | 成人av在线观 | 国产成人免费 | 99精品免费在线观看 | 九九久久久久久久久激情 | 国产精品久久久久久久久久久免费看 | 综合色站| 成片免费| 天天爱天天爽 | 精品亚洲一区二区 | 91成人精品一区在线播放69 | 国产在线一线 | 成人免费91 | 国产一区在线看 | 欧美色综合| 日日干天天操 | 久久精品视频一 | 毛片视频电影 | 91在线精品秘密一区二区 | 97在线视频免费观看 | 97碰在线视频 | 亚洲精品美女久久久久网站 | 69视频网站 | 麻豆国产精品va在线观看不卡 | 国产a视频免费观看 | 久久99久久99精品中文字幕 | 国产成人精品在线观看 | 激情婷婷欧美 | 人人澡超碰碰97碰碰碰软件 | 久久国产午夜精品理论片最新版本 | www好男人| 日韩中文字幕免费视频 | 日本精品一区二区三区在线观看 | 国产精品日韩 | 超碰97中文 | 国内精品久久久久国产 | 五月婷婷狠狠 | 伊人国产在线观看 | 免费麻豆 | 欧美一级免费高清 | 久久综合九色综合久久久精品综合 | 成人在线观看网址 | 一区二区三区国产欧美 | 久久婷婷五月综合色丁香 | 亚洲精品视频免费在线观看 | 毛片网站在线观看 | 91在线小视频 | 国产精品av免费 | 精品国产一区二区三区四区在线观看 | 97成人在线观看视频 | 亚洲精品www. | 国产九九热 | 国产麻豆精品传媒av国产下载 | 天堂av在线网址 | 国产又粗又猛又色 | 91精品办公室少妇高潮对白 | 久久精品视频在线看 | 欧美孕妇与黑人孕交 | 国产资源在线免费观看 | 黄色小视频在线观看免费 | 亚洲精品乱码白浆高清久久久久久 | 亚洲欧美色婷婷 | 亚洲电影图片小说 | 国产 日韩 在线 亚洲 字幕 中文 | 日日爽夜夜操 | 五月婷婷色播 | 日韩免费二区 | 国产成人免费网站 | 国产精品福利无圣光在线一区 | 精品国偷自产国产一区 | 欧美激情视频免费看 | 久久字幕精品一区 | www.夜夜干.com | 欧美在线free| 国产69久久精品成人看 | 成人精品一区二区三区中文字幕 | 久久婷亚洲五月一区天天躁 | 久久综合桃花 | 人人干狠狠干 | 亚洲视频在线免费观看 | 久草热久草视频 | av片中文| 久久久久久久国产精品影院 | 中国美女一级看片 | 蜜臀久久99精品久久久无需会员 | 欧美日韩二区在线 | 91在线porny国产在线看 | 中文字幕不卡在线88 | 在线播放91 | 涩涩网站在线观看 | v片在线看 | 91看成人| 在线免费视频你懂的 | 国产精品毛片一区二区在线 | 国产久草在线 | 久久国产免费视频 | 久久精品久久精品 | 日韩精品中文字幕有码 | 91亚洲精品久久久蜜桃借种 | 久久99精品国产麻豆宅宅 | 96av麻豆蜜桃一区二区 | 国产麻豆剧传媒免费观看 | 国产视频色| 天天草夜夜 | 8x成人免费视频 | 日韩视频一区二区三区在线播放免费观看 | 五月开心六月伊人色婷婷 | 国产小视频在线观看 | 亚洲视频一区二区三区在线观看 | 免费黄色av电影 | 亚洲精选视频在线 | 成年人黄色在线观看 | 久久刺激视频 | 美女视频黄是免费的 | 肉色欧美久久久久久久免费看 | 伊人伊成久久人综合网小说 | 丝袜少妇在线 | 性色av免费观看 | 成人h在线 | 中文字幕在线观看你懂的 | 国产九色视频在线观看 | 亚洲日韩欧美一区二区在线 | 久久精品高清视频 | 国产精品视频999 | 在线综合色 | www在线观看视频 | 天天色天天草天天射 | 亚洲视频一级 | 日韩电影中文字幕在线观看 | 国产91成人 | 欧美成人黄色片 | 在线色资源 | 午夜狠狠干 | 在线观看国产区 | 伊人国产在线播放 | 99产精品成人啪免费网站 | 久久情侣偷拍 | 成人网在线免费视频 | 午夜在线国产 | 亚洲乱码精品久久久久 | 日韩精品视频免费专区在线播放 | 狠狠干夜夜操天天爽 | 亚洲午夜精品久久久久久久久久久久 | 国产成人精品av | 日韩免费观看高清 | 免费av高清 | 国产免费小视频 | 成人免费视频网址 | 日韩高清片 | 日韩久久视频 | 三级黄色欧美 | 色综合天天狠天天透天天伊人 | 欧美精品少妇xxxxx喷水 | 欧美性色xo影院 | 久草视频观看 | 中文字幕免费国产精品 | 伊人天堂网 | 中文字幕有码在线 | av黄色免费网站 | 91一区二区三区久久久久国产乱 | 999久久a精品合区久久久 | 99在线精品免费视频九九视 | av网址在线播放 | 丁香久久 | 在线观看一 | www.五月天 | 91精品区| 婷婷色综合 | 91看片在线观看 | 一区二区三区在线免费观看 | 国产精品第二十页 | 在线播放日韩av | 久久不射电影院 | 天堂久久电影网 | 77国产精品 | 婷婷免费在线视频 | 一级c片 | 夜夜躁狠狠躁日日躁视频黑人 | 午夜手机电影 | 天天操婷婷 | 亚洲91精品在线观看 | 精品理论片 | 久久久久久免费 | 日韩电影在线一区 | 美女福利视频 | 久久人人爽人人爽人人片av免费 | 97超碰在线资源 | 中文国产字幕在线观看 | 免费男女网站 | 久久久穴 | 日韩va欧美va亚洲va久久 | 蜜臀aⅴ精品一区二区三区 久久视屏网 | 国产精品美女久久久久久久久久久 | 97精品一区 | 激情在线五月天 | 久久久久亚洲国产 | 五月天丁香综合 | 国产精品永久免费视频 | 91中文字幕在线播放 | 激情综合五月网 | 亚洲电影图片小说 | 欧美日韩一二三四区 | 欧美日韩精品免费观看 | 欧美亚洲三级 | 亚州精品成人 | 亚洲成aⅴ人片久久青草影院 | 日一日操一操 | 婷婷av资源| 久久人人爽爽 | a在线免费观看视频 | 欧美性色黄大片在线观看 | 国产国产人免费人成免费视频 | 国产3p视频| 欧美性天天 | 91黄站| 久久精品欧美一区二区三区麻豆 | 91色国产 | 6699私人影院 | 国产不卡免费av | 91亚洲精品久久久久图片蜜桃 | av电影免费在线看 | 日韩超碰 | 国产淫a| 九九九热精品免费视频观看网站 | 9999精品视频 | 中文字幕免费观看视频 | 国产一区在线看 | 97精品国自产拍在线观看 | 国产精品成人国产乱 | 色网站在线免费观看 | 夜夜操夜夜干 | 视频国产区 | 精品在线观看一区二区三区 | 精品国产一区在线观看 | 午夜性盈盈 | 国产精品96久久久久久吹潮 | 日韩在线免费视频 | 91免费观看视频在线 | 国产免费中文字幕 | 国产99久久久欧美黑人 | 99精品国产成人一区二区 | 成人毛片100免费观看 | 日韩久久久久久久久久 | 一区二区电影在线观看 | 亚洲精品美女久久17c | 日本黄色特级片 | 国产精品21区 | 国产精品网红直播 | 91九色在线视频观看 | www.在线观看av | 亚洲精品人人 | www.天天射.com | 国产精品欧美一区二区 | 91精品一区二区三区蜜桃 | 国产精品久一 | 中文字幕日韩免费视频 | 久久久久视 | 国产精品一区二区av日韩在线 | 99一级片| 免费a网址| 碰超在线观看 | 一区二区三区在线免费观看视频 | 日韩天天干 | 最新国产精品亚洲 | 成人av在线影院 | 亚洲播放一区 | 天天操天天操一操 | 久久久久高清毛片一级 | 97精品国产91久久久久久 | 国产欧美精品一区二区三区 | 国产97色 | 天天操天天舔天天爽 | 日韩高清成人 | 人人揉人人揉人人揉人人揉97 | 99久久er热在这里只有精品66 | 蜜臀久久99精品久久久无需会员 | 亚洲精品五月 | 久久狠狠干 | 亚洲欧洲精品久久 | 国产剧情亚洲 | 国产精品99久久久久的智能播放 | 最近日韩免费视频 | 高清中文字幕 | 成人黄色片在线播放 | 中文字幕在线观看av | 日本少妇高清做爰视频 | 日韩av在线看| 最近中文字幕高清字幕免费mv | 久草观看视频 | 国产成人免费精品 | 香蕉视频免费看 | 国产无遮挡又黄又爽馒头漫画 | 国产一区二区久久久久 | 成人国产一区二区 | 成年性视频 | 国产精品门事件 | 久久久免费精品 | 久草青青在线观看 | 国产精品黄网站在线观看 | 天天色影院 | 最近免费中文字幕mv在线视频3 | 在线观看视频你懂的 | 激情欧美国产 | 日韩欧美一区二区在线观看 | 81精品国产乱码久久久久久 | 夜夜视频欧洲 | 一本色道久久综合亚洲二区三区 | 久草热视频 | 国产精品亚洲综合久久 | 亚洲在线网址 | 日韩欧美综合精品 | 欧美极度另类性三渗透 | 久久久wwww| free,性欧美 九九交易行官网 | 日韩中文字幕网站 | 国产裸体视频bbbbb | 亚洲国产成人精品久久 | 久久呀| 精品国产久| 又爽又黄在线观看 | 精品1区2区3区 | 国产精品久久久久婷婷二区次 | 欧美色噜噜噜 | 在线看的av网站 | 超碰人人超 | 在线观看视频福利 | 国产精品小视频网站 | 欧美日韩国产精品爽爽 | 国产黄色理论片 | 成人小视频在线观看免费 | 亚洲精品免费在线 | 五月黄色 | 天天鲁天天干天天射 | 国产亚洲精品久久久网站好莱 | 在线免费试看 | 日韩动态视频 | 精品中文字幕在线 | 91九色网址| 蜜臀av.com| 黄网站色成年免费观看 | 久草在线99 | 久久69精品久久久久久久电影好 | 中国黄色一级大片 | 欧美激情视频久久 | 久久精品精品电影网 | 日日夜av| 黄色免费国产 | 五月天最新网址 | 伊人久久婷婷 | www狠狠| 91人人澡人人爽 | 在线 日韩 av | 日韩中文字幕免费在线播放 | 69视频国产 | 欧美日韩裸体免费视频 | 在线观看免费国产小视频 | 亚洲精品久久久蜜臀下载官网 | 在线观看视频你懂的 | 99国产精品视频免费观看一公开 | 国产一级做a | 三级a视频| 蜜臀av性久久久久av蜜臀三区 | 狠狠色丁香婷婷综合 | 久久天堂精品视频 | 久久av伊人 | 午夜视频免费播放 | 亚洲免费视频在线观看 | 亚洲天堂网站 | 中文字幕国产一区二区 | 久久久久亚洲精品男人的天堂 | 欧美精品色 | 精品国产99国产精品 | 在线国产99 | 伊甸园av在线| 亚洲视频一级 | 免费男女羞羞的视频网站中文字幕 | 久草在线视频在线观看 | 中文字幕一区2区3区 | 亚洲久草在线视频 | se视频网址| 五月婷婷.com | 在线天堂v | 久久精品牌麻豆国产大山 | 欧美日韩另类视频 | 成人毛片在线观看视频 | 精品一区二区在线免费观看 | 91av99| 处女av在线 | 色网站免费在线观看 | 久久久久久黄 | 午夜性色 | 日本久久高清视频 | 久久久久久久久网站 |