av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

Handler的初級、中級、高級問法,你都掌握了嗎?

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

  •  Handler的源碼和常見問題的解答

           1.  一個線程中最多有多少個Handler,Looper,MessageQueue?

           2.  Looper死循環(huán)為什么不會導致應用卡死,會耗費大量資源嗎?

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

           4.  主線程如何訪問網(wǎng)絡?

           5.  如何處理Handler使用不當造成的內存泄漏?

           6.  Handler的消息優(yōu)先級,有什么應用場景?

           7.  主線程的Looper何時退出?能否手動退出?

           8.  如何判斷當前線程是安卓主線程?

           9.  正確創(chuàng)建Message實例的方式?

  •  Handler深層次問題解答

           1.  ThreadLocal

           2.  epoll機制

           3.  Handle同步屏障機制

           4.  Handler的鎖相關問題

           5.  Handler中的同步方法

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

           1.  HandlerThread

           2.  IntentService

           3.  如何打造一個不崩潰的APP

           4.  Glide中的運用

Handler的源碼和常見問題的解答

下面來看一下官方對其的定義: 

 
 
 
 
  1. 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到線程的消息隊列(MessageQueue)中,每個Handler實例和一個線程以及那個線程的消息隊列相關聯(lián)。當你創(chuàng)建一個Handler時應該和一個Looper進行綁定(主線程默認已經(jīng)創(chuàng)建Looper了,子線程需要自己創(chuàng)建Looper),它向Looper的對應的消息隊列傳送Message/Runnable同時在那個Looper所在線程處理對應的Message/Runnable。下面這張圖就是Handler的工作流程

Handler工作流程圖

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

前面說過,當你創(chuàng)建一個Handler時應該和一個Looper進行綁定(綁定也可以理解為創(chuàng)建,主線程默認已經(jīng)創(chuàng)建Looper了,子線程需要自己創(chuàng)建Looper),因此我們先來看看主線程中是如何處理的: 

 
 
 
 
  1. //ActivityThread.java  
  2. public static void main(String[] args) {  
  3.     ···  
  4.     Looper.prepareMainLooper();  
  5.     ··· 
  6.      ActivityThread thread = new ActivityThread();  
  7.     thread.attach(false, startSeq);  
  8.     if (sMainThreadHandler == null) {  
  9.         sMainThreadHandler = thread.getHandler();  
  10.     }  
  11.     if (false) {  
  12.         Looper.myLooper().setMessageLogging(new  
  13.                 LogPrinter(Log.DEBUG, "ActivityThread"));  
  14.     }  
  15.     // End of event ActivityThreadMain.  
  16.     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  17.     Looper.loop();  
  18.     throw new RuntimeException("Main thread loop unexpectedly exited");  

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

 
 
 
 
  1. //Looper.java    
  2. /**  
  3. * Initialize the current thread as a looper, marking it as an  
  4. * application's main looper. The main looper for your application  
  5. * is created by the Android environment, so you should never need  
  6. * to call this function yourself.  See also: {@link #prepare()}  
  7. */  
  8. public static void prepareMainLooper() {  
  9.      prepare(false);  
  10.      synchronized (Looper.class) {  
  11.          if (sMainLooper != null) {  
  12.              throw new IllegalStateException("The main Looper has already been prepared.");  
  13.          } 
  14.           sMainLooper = myLooper();  
  15.      }  
  16. }  
  17. //prepare  
  18. private static void prepare(boolean quitAllowed) {  
  19.         if (sThreadLocal.get() != null) {  
  20.             throw new RuntimeException("Only one Looper may be created per thread");  
  21.         }  
  22.         sThreadLocal.set(new Looper(quitAllowed));  

可以看到在Looper.prepareMainLooper()方法中創(chuàng)建了當前線程的Looper,同時將Looper實例存放到線程局部變量sThreadLocal(ThreadLocal)中,也就是每個線程有自己的Looper。在創(chuàng)建Looper的時候也創(chuàng)建了該線程的消息隊列,可以看到prepareMainLooper會判斷sMainLooper是否有值,如果調用多次,就會拋出異常,所以也就是說主線程的Looper和MessageQueue只會有一個。同理子線程中調用Looper.prepare()時,會調用prepare(true)方法,如果多次調用,也會拋出每個線程只能由一個Looper的異常,總結起來就是每個線程中只有一個Looper和MessageQueue。 

 
 
 
 
  1. //Looper.java  
  2. private Looper(boolean quitAllowed) {  
  3.     mQueue = new MessageQueue(quitAllowed);  
  4.     mThread = Thread.currentThread();  

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

 
 
 
 
  1. //ActivityThread.java  
  2. final H mH = new H();   
  3. @UnsupportedAppUsage  
  4.   final Handler getHandler() {  
  5.     return mH;  

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

 
 
 
 
  1. //ActivityThread.java  
  2. class H extends Handler {  
  3.     ···  
  4.     public void handleMessage(Message msg) {  
  5.         if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));  
  6.         switch (msg.what) {  
  7.             case BIND_APPLICATION:  
  8.                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");  
  9.                 AppBindData data = (AppBindData)msg.obj;  
  10.                 handleBindApplication(data);  
  11.                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  12.                 break;  
  13.             case EXIT_APPLICATION:  
  14.                 if (mInitialApplication != null) {  
  15.                     mInitialApplication.onTerminate();  
  16.                 }  
  17.                 Looper.myLooper().quit();  
  18.                 break;  
  19.             case RECEIVER:  
  20.                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");  
  21.                 handleReceiver((ReceiverData)msg.obj);  
  22.                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  23.                 break;  
  24.             case CREATE_SERVICE:  
  25.                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));  
  26.                 handleCreateService((CreateServiceData)msg.obj);  
  27.                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  28.                 break;  
  29.             case BIND_SERVICE:  
  30.                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");  
  31.                 handleBindService((BindServiceData)msg.obj);  
  32.                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  33.                 break;  
  34.             case UNBIND_SERVICE:  
  35.                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind"); 
  36.                 handleUnbindService((BindServiceData)msg.obj);  
  37.                 schedulePurgeIdler();  
  38.                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  39.                 break;  
  40.             case SERVICE_ARGS:  
  41.                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));  
  42.                 handleServiceArgs((ServiceArgsData)msg.obj);  
  43.                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  44.                 break;  
  45.             case STOP_SERVICE:  
  46.                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");  
  47.                 handleStopService((IBinder)msg.obj);  
  48.                 schedulePurgeIdler();  
  49.                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  50.                 break;  
  51.             ···  
  52.             case APPLICATION_INFO_CHANGED:  
  53.                 mUpdatingSystemConfig = true;  
  54.                 try {  
  55.                     handleApplicationInfoChanged((ApplicationInfo) msg.obj);  
  56.                 } finally {  
  57.                     mUpdatingSystemConfig = false;  
  58.                 }  
  59.                 break;  
  60.             case RUN_ISOLATED_ENTRY_POINT:  
  61.                 handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1,  
  62.                         (String[]) ((SomeArgs) msg.obj).arg2);  
  63.                 break;  
  64.             case EXECUTE_TRANSACTION:  
  65.                 final ClientTransaction transaction = (ClientTransaction) msg.obj;  
  66.                 mTransactionExecutor.execute(transaction);  
  67.                 if (isSystem()) {  
  68.                     // Client transactions inside system process are recycled on the client side  
  69.                     // instead of ClientLifecycleManager to avoid being cleared before this  
  70.                     // message is handled.  
  71.                     transaction.recycle();  
  72.                 }  
  73.                 // TODO(lifecycler): Recycle locally scheduled transactions.  
  74.                 break;  
  75.             case RELAUNCH_ACTIVITY:  
  76.                 handleRelaunchActivityLocally((IBinder) msg.obj);  
  77.                 break;  
  78.             case PURGE_RESOURCES:  
  79.                 schedulePurgeIdler();  
  80.                 break;  
  81.         }  
  82.         Object obj = msg.obj;  
  83.         if (obj instanceof SomeArgs) {  
  84.             ((SomeArgs) obj).recycle();  
  85.         }  
  86.         if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));  
  87.     }  

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

 
 
 
 
  1. //Looper.java  
  2. public static void loop() {  
  3.     //獲取ThreadLocal中的Looper  
  4.     final Looper me = myLooper();  
  5.     ···  
  6.     final MessageQueue queue = me.mQueue;  
  7.     ···  
  8.     for (;;) { //死循環(huán)  
  9.         //獲取消息  
  10.         Message msg = queue.next(); // might block  
  11.         if (msg == null) {  
  12.             // No message indicates that the message queue is quitting.  
  13.             return;  
  14.         }  
  15.         ···  
  16.         msg.target.dispatchMessage(msg);  
  17.         ···  
  18.         //回收復用    
  19.         msg.recycleUnchecked();  
  20.     }  

在loop方法中是一個死循環(huán),在這里從消息隊列中不斷的獲取消息queue.next(),然后通過Handler(msg.target)進行消息的分發(fā),其實并沒有什么具體的綁定,因為Handler在每個線程中對應只有一個Looper和消息隊列MessageQueue,自然要靠它來處理,也就是是調用Looper.loop()方法。在Looper.loop()的死循環(huán)中不斷的取消息,最后回收復用。

這里要強調一下Message中的參數(shù)target(Handler),正是這個變量,每個Message才能找到對應的Handler進行消息分發(fā),讓多個Handler同時工作。

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

 
 
 
 
  1. @Override  
  2.     protected void onCreate(@Nullable Bundle savedInstanceState) {  
  3.         super.onCreate(savedInstanceState);  
  4.         setContentView(R.layout.activity_three); 
  5.          new Thread(new Runnable() {  
  6.             @Override  
  7.             public void run() {  
  8.                 new Handler().post(new Runnable() {  
  9.                     @Override  
  10.                     public void run() {  
  11.                         Toast.makeText(HandlerActivity.this,"toast",Toast.LENGTH_LONG).show();  
  12.                     }  
  13.                 });  
  14.             }  
  15.         }).start();  
  16.     } 

運行后可以看到錯誤日志,可以看到提示我們需要在子線程中調用Looper.prepare()方法,實際上就是要創(chuàng)建一個Looper和你的Handler進行“關聯(lián)”。   

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

添加Looper.prepare()創(chuàng)建Looper,同時調用Looper.loop()方法開始處理消息。 

 
 
 
 
  1. @Override  
  2.   protected void onCreate(@Nullable Bundle savedInstanceState) {  
  3.       super.onCreate(savedInstanceState);  
  4.       setContentView(R.layout.activity_three);  
  5.       new Thread(new Runnable() {  
  6.           @Override  
  7.           public void run() {  
  8.               //創(chuàng)建Looper,MessageQueue  
  9.               Looper.prepare();  
  10.               new Handler().post(new Runnable() {  
  11.                   @Override  
  12.                   public void run() {  
  13.                       Toast.makeText(HandlerActivity.this,"toast",Toast.LENGTH_LONG).show();  
  14.                   }  
  15.               });  
  16.               //開始處理消息  
  17.               Looper.loop();  
  18.           }  
  19.       }).start();  
  20.   } 

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

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

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

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

 
 
 
 
  1. //Toast.java  
  2. public void handleShow(IBinder windowToken) {  
  3.     ···  
  4.     if (mView != mNextView) {  
  5.         // Since the notification manager service cancels the token right  
  6.         // after it notifies us to cancel the toast there is an inherent  
  7.         // race and we may attempt to add a window after the token has been  
  8.         // invalidated. Let us hedge against that.  
  9.         try {  
  10.             mWM.addView(mView, mParams); //進行ViewRootImpl的創(chuàng)建  
  11.             trySendAccessibilityEvent();  
  12.         } catch (WindowManager.BadTokenException e) {  
  13.             /* ignore */  
  14.         }  
  15.     }  

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

 
 
 
 
  1. //WindowManagerGlobal.java  
  2. root = new ViewRootImpl(view.getContext(), display); //創(chuàng)建ViewRootImpl  
  3.     view.setLayoutParams(wparams);  
  4.     mViews.add(view);  
  5.     mRoots.add(root);  
  6.     mParams.add(wparams);  
  7.     // do this last because it fires off messages to start doing things  
  8.     try {  
  9.         //ViewRootImpl  
  10.         root.setView(view, wparams, panelParentView);  
  11.     } catch (RuntimeException e) {  
  12.         // BadTokenException or InvalidDisplayException, clean up.  
  13.         if (index >= 0) {  
  14.             removeViewLocked(index, true);  
  15.         }  
  16.         throw e;  
  17.     }  

setView內部會通過requestLayout來完成異步刷新請求,同時也會調用checkThread方法來檢驗線程的合法性。 

 
 
 
 
  1. @Override  
  2. public void requestLayout() {  
  3.     if (!mHandlingLayoutInLayoutRequest) {  
  4.         checkThread();  
  5.         mLayoutRequested = true;  
  6.         scheduleTraversals();  
  7.     }  

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

 
 
 
 
  1. //子線程中調用      
  2. public void showDialog(){  
  3.         new Thread(new Runnable() {  
  4.             @Override  
  5.             public void run() {  
  6.                 //創(chuàng)建Looper,MessageQueue  
  7.                 Looper.prepare();  
  8.                 new Handler().post(new Runnable() {  
  9.                     @Override  
  10.                     public void run() {  
  11.                         builder = new AlertDialog.Builder(HandlerActivity.this);  
  12.                         builder.setTitle("jackie");  
  13.                         alertDialog = builder.create();  
  14.                         alertDialog.show();  
  15.                         alertDialog.hide();  
  16.                     }  
  17.                 });  
  18.                 //開始處理消息  
  19.                 Looper.loop();  
  20.             }  
  21.         }).start();  
  22.     } 

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

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

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

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

在網(wǎng)絡請求之前添加如下代碼 

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

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

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

這是因為 Android 的UI控件不是線程安全的,如果在多線程中并發(fā)訪問可能會導致UI控件處于不可預期的狀態(tài),那么為什么系統(tǒng)不對UI控件的訪問加上鎖機制呢?缺點有兩個

  1.  首先加上鎖機制會讓UI訪問的邏輯變得復雜
  2.  鎖機制會降低UI訪問的效率,因為鎖機制會阻塞某些線程的執(zhí)行。

所以最簡單且高效的方法就是采用單線程模型來處理UI操作。(安卓開發(fā)藝術探索)

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

  1.  主線程中定義 Handler,子線程通過 mHandler 發(fā)送消息,主線程 Handler 的 handleMessage 更新UI。
  2.  用 Activity 對象的 runOnUiThread 方法。
  3.  創(chuàng)建 Handler,傳入 getMainLooper。
  4.  View.post(Runnable r) 。

Looper死循環(huán)為什么不會導致應用卡死,會耗費大量資源嗎?

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

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

主線程的Looper何時退出

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

 
 
 
 
  1. //ActivityThread.java  
  2. case EXIT_APPLICATION:  
  3.     if (mInitialApplication != null) {  
  4.         mInitialApplication.onTerminate();  
  5.     }  
  6.     Looper.myLooper().quit();  
  7.     break; 

如果你嘗試手動退出主線程Looper,便會拋出如下異常 

 
 
 
 
  1. Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.  
  2.     at android.os.MessageQueue.quit(MessageQueue.java:428)  
  3.     at android.os.Looper.quit(Looper.java:354)  
  4.     at com.jackie.testdialog.Test2Activity.onCreate(Test2Activity.java:29)  
  5.     at android.app.Activity.performCreate(Activity.java:7802)  
  6.     at android.app.Activity.performCreate(Activity.java:7791)  
  7.     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)  
  8.     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)  
  9.     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)   
  10.     at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)   
  11.     at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)   
  12.     at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)   
  13.     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)   
  14.     at android.os.Handler.dispatchMessage(Handler.java:107)   
  15.     at android.os.Looper.loop(Looper.java:214)   
  16.     at android.app.ActivityThread.main(ActivityThread.java:7356)   
  17.     at java.lang.reflect.Method.invoke(Native Method)   
  18.     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)   
  19.     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 

為什么不允許退出呢,因為主線程不允許退出,一旦退出就意味著程序掛了,退出也不應該用這種方式退出。

Handler的消息處理順序

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

 
 
 
 
  1. msg.target.dispatchMessage(msg); 

我們來看看dispatchMessage的源碼 

 
 
 
 
  1. public void dispatchMessage(@NonNull Message msg) {  
  2.      if (msg.callback != null) {  
  3.          handleCallback(msg);  
  4.      } else {  
  5.          //如果 callback 處理了該 msg 并且返回 true, 就不會再回調 handleMessage  
  6.          if (mCallback != null) { 
  7.               if (mCallback.handleMessage(msg)) {  
  8.                  return;  
  9.              }  
  10.          }  
  11.          handleMessage(msg);  
  12.      }  
  13.  } 

如果Message這個對象有CallBack回調的話,這個CallBack實際上是個Runnable,就只執(zhí)行這個回調,然后就結束了,創(chuàng)建該Message的CallBack代碼如下: 

 
 
 
 
  1. Message msgCallBack = Message.obtain(handler, new Runnable() {  
  2.     @Override  
  3.     public void run
    網(wǎng)站標題:Handler的初級、中級、高級問法,你都掌握了嗎?
    當前鏈接:http://uogjgqi.cn/article/djchijs.html
掃二維碼與項目經(jīng)理溝通

我們在微信上24小時期待你的聲音

解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網(wǎng)交流