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

- Handler的源碼和常見(jiàn)問(wèn)題的解答
1. 一個(gè)線(xiàn)程中最多有多少個(gè)Handler,Looper,MessageQueue?
2. Looper死循環(huán)為什么不會(huì)導(dǎo)致應(yīng)用卡死,會(huì)耗費(fèi)大量資源嗎?
3. 子線(xiàn)程的如何更新UI,比如Dialog,Toast等?系統(tǒng)為什么不建議子線(xiàn)程中更新UI?
4. 主線(xiàn)程如何訪(fǎng)問(wèn)網(wǎng)絡(luò)?
5. 如何處理Handler使用不當(dāng)造成的內(nèi)存泄漏?
6. Handler的消息優(yōu)先級(jí),有什么應(yīng)用場(chǎng)景?
7. 主線(xiàn)程的Looper何時(shí)退出?能否手動(dòng)退出?
8. 如何判斷當(dāng)前線(xiàn)程是安卓主線(xiàn)程?
9. 正確創(chuàng)建Message實(shí)例的方式?
- Handler深層次問(wèn)題解答
1. ThreadLocal
2. epoll機(jī)制
3. Handle同步屏障機(jī)制
4. Handler的鎖相關(guān)問(wèn)題
5. Handler中的同步方法
- Handler在系統(tǒng)以及第三方框架的一些應(yīng)用
1. HandlerThread
2. IntentService
3. 如何打造一個(gè)不崩潰的APP
4. 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到線(xiàn)程的消息隊(duì)列(MessageQueue)中,每個(gè)Handler實(shí)例和一個(gè)線(xiàn)程以及那個(gè)線(xiàn)程的消息隊(duì)列相關(guān)聯(lián)。當(dāng)你創(chuàng)建一個(gè)Handler時(shí)應(yīng)該和一個(gè)Looper進(jìn)行綁定(主線(xiàn)程默認(rèn)已經(jīng)創(chuàng)建Looper了,子線(xiàn)程需要自己創(chuàng)建Looper),它向Looper的對(duì)應(yīng)的消息隊(duì)列傳送Message/Runnable同時(shí)在那個(gè)Looper所在線(xiàn)程處理對(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)建,主線(xiàn)程默認(rèn)已經(jīng)創(chuàng)建Looper了,子線(xiàn)程需要自己創(chuàng)建Looper),因此我們先來(lái)看看主線(xiàn)程中是如何處理的:
- //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(new
- LogPrinter(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)前線(xiàn)程的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)前線(xiàn)程的Looper,同時(shí)將Looper實(shí)例存放到線(xiàn)程局部變量sThreadLocal(ThreadLocal)中,也就是每個(gè)線(xiàn)程有自己的Looper。在創(chuàng)建Looper的時(shí)候也創(chuàng)建了該線(xiàn)程的消息隊(duì)列,可以看到prepareMainLooper會(huì)判斷sMainLooper是否有值,如果調(diào)用多次,就會(huì)拋出異常,所以也就是說(shuō)主線(xiàn)程的Looper和MessageQueue只會(huì)有一個(gè)。同理子線(xiàn)程中調(diào)用Looper.prepare()時(shí),會(huì)調(diào)用prepare(true)方法,如果多次調(diào)用,也會(huì)拋出每個(gè)線(xiàn)程只能由一個(gè)Looper的異常,總結(jié)起來(lái)就是每個(gè)線(xiàn)程中只有一個(gè)Looper和MessageQueue。
- //Looper.java
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mThread = Thread.currentThread();
- }
再來(lái)看看主線(xiàn)程sMainThreadHandler = thread.getHandler(),getHandler獲取到的實(shí)際上就是mH這個(gè)Handler。
- //ActivityThread.java
- final H mH = new H();
- @UnsupportedAppUsage
- final Handler getHandler() {
- return mH;
- }
mH這個(gè)Handler是ActivityThread的內(nèi)部類(lè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中的Looper
- final Looper me = myLooper();
- ···
- final MessageQueue queue = me.mQueue;
- ···
- for (;;) { //死循環(huán)
- //獲取消息
- Message msg = queue.next(); // might block
- if (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è)線(xiàn)程中對(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)看看子線(xiàn)程中是如何處理的,首先在子線(xiàn)程中創(chuàng)建一個(gè)Handler并發(fā)送Runnable
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_three);
- new Thread(new Runnable() {
- @Override
- public void run() {
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(HandlerActivity.this,"toast",Toast.LENGTH_LONG).show();
- }
- });
- }
- }).start();
- }
運(yùn)行后可以看到錯(cuò)誤日志,可以看到提示我們需要在子線(xiàn)程中調(diào)用Looper.prepare()方法,實(shí)際上就是要?jiǎng)?chuàng)建一個(gè)Looper和你的Handler進(jìn)行“關(guān)聯(lián)”。
- --------- beginning of crash
- 020-11-09 15:51:03.938 21122-21181/com.jackie.testdialog E/AndroidRuntime: FATAL EXCEPTION: Thread-2
- Process: com.jackie.testdialog, PID: 21122
- java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
- at android.os.Handler.
(Handler.java:207) - at android.os.Handler.
(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)始處理消息。
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_three);
- new Thread(new Runnable() {
- @Override
- public void run() {
- //創(chuàng)建Looper,MessageQueue
- Looper.prepare();
- new Handler().post(new Runnable() {
- @Override
- public 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è)子線(xiàn)程就會(huì)一直處于循環(huán)等待的狀態(tài),因此不需要的時(shí)候終止Looper,調(diào)用Looper.myLooper().quit()。
看完上面的代碼可能你會(huì)有一個(gè)疑問(wèn),在子線(xiàn)程中更新UI(進(jìn)行Toast)不會(huì)有問(wèn)題嗎,我們Android不是不允許在子線(xiàn)程更新UI嗎,實(shí)際上并不是這樣的,在ViewRootImpl中的checkThread方法會(huì)校驗(yàn)mThread != Thread.currentThread(),mThread的初始化是在ViewRootImpl的的構(gòu)造器中,也就是說(shuō)一個(gè)創(chuàng)建ViewRootImpl線(xiàn)程必須和調(diào)用checkThread所在的線(xiàn)程一致,UI的更新并非只能在主線(xiàn)程才能進(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)建ViewRootImpl
- view.setLayoutParams(wparams);
- mViews.add(view);
- mRoots.add(root);
- mParams.add(wparams);
- // do this last because it fires off messages to start doing things
- try {
- //ViewRootImpl
- root.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)線(xiàn)程的合法性。
- @Override
- public void requestLayout() {
- if (!mHandlingLayoutInLayoutRequest) {
- checkThread();
- mLayoutRequested = true;
- scheduleTraversals();
- }
- }
因此,我們的ViewRootImpl的創(chuàng)建是在子線(xiàn)程,所以mThread的值也是子線(xiàn)程,同時(shí)我們的更新也是在子線(xiàn)程,所以不會(huì)產(chǎn)生異常,同時(shí)也可以參考這篇文章分析,寫(xiě)的非常詳細(xì)。同理下面的代碼也可以驗(yàn)證這個(gè)情況
- //子線(xiàn)程中調(diào)用
- public void showDialog(){
- new Thread(new Runnable() {
- @Override
- public void run() {
- //創(chuàng)建Looper,MessageQueue
- Looper.prepare();
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- builder = new AlertDialog.Builder(HandlerActivity.this);
- builder.setTitle("jackie");
- alertDialog = builder.create();
- alertDialog.show();
- alertDialog.hide();
- }
- });
- //開(kāi)始處理消息
- Looper.loop();
- }
- }).start();
- }
在子線(xiàn)程中調(diào)用showDialog方法,先調(diào)用alertDialog.show()方法,再調(diào)用alertDialog.hide()方法,hide方法只是將Dialog隱藏,并沒(méi)有做其他任何操作(沒(méi)有移除Window),然后再在主線(xiàn)程調(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: main
- Process: com.jackie.testdialog, PID: 24819
- android.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)
所以在線(xiàn)程中更新UI的重點(diǎn)是創(chuàng)建它的ViewRootImpl和checkThread所在的線(xiàn)程是否一致。
如何在主線(xiàn)程中訪(fǎng)問(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(線(xiàn)程策略)和VmPolicy(VM策略),這里把嚴(yán)苛模式的網(wǎng)絡(luò)檢測(cè)關(guān)了,就可以在主線(xiàn)程中執(zhí)行網(wǎng)絡(luò)操作了,一般是不建議這么做的。關(guān)于嚴(yán)苛模式可以查看這里。
系統(tǒng)為什么不建議在子線(xiàn)程中訪(fǎng)問(wèn)UI?
這是因?yàn)?Android 的UI控件不是線(xiàn)程安全的,如果在多線(xiàn)程中并發(fā)訪(fǎng)問(wèn)可能會(huì)導(dǎo)致UI控件處于不可預(yù)期的狀態(tài),那么為什么系統(tǒng)不對(duì)UI控件的訪(fǎng)問(wèn)加上鎖機(jī)制呢?缺點(diǎn)有兩個(gè)
- 首先加上鎖機(jī)制會(huì)讓UI訪(fǎng)問(wèn)的邏輯變得復(fù)雜
- 鎖機(jī)制會(huì)降低UI訪(fǎng)問(wèn)的效率,因?yàn)殒i機(jī)制會(huì)阻塞某些線(xiàn)程的執(zhí)行。
所以最簡(jiǎn)單且高效的方法就是采用單線(xiàn)程模型來(lái)處理UI操作。(安卓開(kāi)發(fā)藝術(shù)探索)
子線(xiàn)程如何通知主線(xiàn)程更新UI(都是通過(guò)Handle發(fā)送消息到主線(xiàn)程操作UI的)
- 主線(xiàn)程中定義 Handler,子線(xiàn)程通過(guò) mHandler 發(fā)送消息,主線(xiàn)程 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)大量資源嗎?
從前面的主線(xiàn)程、子線(xiàn)程的分析可以看出,Looper會(huì)在線(xiàn)程中不斷的檢索消息,如果是子線(xiàn)程的Looper死循環(huán),一旦任務(wù)完成,用戶(hù)應(yīng)該手動(dòng)退出,而不是讓其一直休眠等待。(引用自Gityuan)線(xiàn)程其實(shí)就是一段可執(zhí)行的代碼,當(dāng)可執(zhí)行的代碼執(zhí)行完成后,線(xiàn)程的生命周期便該終止了,線(xiàn)程退出。而對(duì)于主線(xiàn)程,我們是絕不希望會(huì)被運(yùn)行一段時(shí)間,自己就退出,那么如何保證能一直存活呢?簡(jiǎn)單做法就是可執(zhí)行代碼是能一直執(zhí)行下去的,死循環(huán)便能保證不會(huì)被退出,例如,binder 線(xiàn)程也是采用死循環(huán)的方法,通過(guò)循環(huán)方式不同與 Binder 驅(qū)動(dòng)進(jìn)行讀寫(xiě)操作,當(dāng)然并非簡(jiǎn)單地死循環(huán),無(wú)消息時(shí)會(huì)休眠。Android是基于消息處理機(jī)制的,用戶(hù)的行為都在這個(gè)Looper循環(huán)中,我們?cè)谛菝邥r(shí)點(diǎn)擊屏幕,便喚醒主線(xiàn)程繼續(xù)進(jìn)行工作。
主線(xiàn)程的死循環(huán)一直運(yùn)行是不是特別消耗 CPU 資源呢?其實(shí)不然,這里就涉及到 Linux pipe/epoll機(jī)制,簡(jiǎn)單說(shuō)就是在主線(xiàn)程的 MessageQueue 沒(méi)有消息時(shí),便阻塞在 loop 的 queue.next() 中的 nativePollOnce() 方法里,此時(shí)主線(xiàn)程會(huì)釋放 CPU 資源進(jìn)入休眠狀態(tài),直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生,通過(guò)往 pipe 管道寫(xiě)端寫(xiě)入數(shù)據(jù)來(lái)喚醒主線(xiàn)程工作。這里采用的 epoll 機(jī)制,是一種IO多路復(fù)用機(jī)制,可以同時(shí)監(jiān)控多個(gè)描述符,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮?,本質(zhì)同步I/O,即讀寫(xiě)是阻塞的。所以說(shuō),主線(xiàn)程大多數(shù)時(shí)候都是處于休眠狀態(tài),并不會(huì)消耗大量CPU資源。
主線(xiàn)程的Looper何時(shí)退出
在App退出時(shí),ActivityThread中的mH(Handler)收到消息后,執(zhí)行退出。
- //ActivityThread.java
- case EXIT_APPLICATION:
- if (mInitialApplication != null) {
- mInitialApplication.onTerminate();
- }
- Looper.myLooper().quit();
- break;
如果你嘗試手動(dòng)退出主線(xiàn)程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)橹骶€(xià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) handleMessage
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
如果Message這個(gè)對(duì)象有CallBack回調(diào)的話(huà),這個(gè)CallBack實(shí)際上是個(gè)Runnable,就只執(zhí)行這個(gè)回調(diào),然后就結(jié)束了,創(chuàng)建該Message的CallBack代碼如下:
- Message msgCallBack = Message.obtain(handler, new Runnable() {
- @Override
- public void run
網(wǎng)站題目:Handler的初級(jí)、中級(jí)、高級(jí)問(wèn)法,你都掌握了嗎?
轉(zhuǎn)載來(lái)源:http://m.fisionsoft.com.cn/article/djchijs.html


咨詢(xún)
建站咨詢(xún)
