Handler的初级、中级、高级问法,你都掌握了吗?

handler是Android中的消息处理机制,是一种线程间通信的解决方案,同时你也可以理解为它天然的为我们在主线程创建一个队列,队列中的消息顺序就是我们设置的延迟的时间,如果你想在Android中实现一个队列的功能,不妨第一时间考虑一下它。本文分为三部分:

  •  Handler的源码和常见问题的解答

           1.  一个线程中最多有多少个Handler,Looper,MessageQueue?

           2.  Looper死循环为什么不会导致应用卡死,会耗费大量资源吗?

           3.  子线程的如何更新UI,比如Dialog,Toast等?系统为什么不建议子线程中更新UI?

           4.  主线程如何访问网络?

           5.  如何处理Handler使用不当造成的内存泄漏?

           6.  Handler的消息优先级,有什么应用场景?

           7.  主线程的Looper何时退出?能否手动退出?

           8.  如何判断当前线程是安卓主线程?

           9.  正确创建Message实例的方式?

  •  Handler深层次问题解答

           1.  ThreadLocal

           2.  epoll机制

           3.  Handle同步屏障机制

           4.  Handler的锁相关问题

           5.  Handler中的同步方法

  •  Handler在系统以及第三方框架的一些应用

           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允许你发送Message/Runnable到线程的消息队列(MessageQueue)中,每个Handler实例和一个线程以及那个线程的消息队列相关联。当你创建一个Handler时应该和一个Looper进行绑定(主线程默认已经创建Looper了,子线程需要自己创建Looper),它向Looper的对应的消息队列传送Message/Runnable同时在那个Looper所在线程处理对应的Message/Runnable。下面这张图就是Handler的工作流程

Handler工作流程图

可以看到在Thread中,Looper的这个传送带其实就一个死循环,它不断的从消息队列MessageQueue中不断的取消息,最后交给Handler.dispatchMessage进行消息的分发,而Handler.sendXXX,Handler.postXXX这些方法把消息发送到消息队列中MessageQueue,整个模式其实就是一个生产者-消费者模式,源源不断的生产消息,处理消息,没有消息时进行休眠。MessageQueue是一个由单链表构成的优先级队列(取的都是头部,所以说是队列)。

前面说过,当你创建一个Handler时应该和一个Looper进行绑定(绑定也可以理解为创建,主线程默认已经创建Looper了,子线程需要自己创建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()方法中创建了当前线程的Looper,同时将Looper实例存放到线程局部变量sThreadLocal(ThreadLocal)中,也就是每个线程有自己的Looper。在创建Looper的时候也创建了该线程的消息队列,可以看到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等的一些消息,比如创建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 (;;) { //死循环  
  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方法中是一个死循环,在这里从消息队列中不断的获取消息queue.next(),然后通过Handler(msg.target)进行消息的分发,其实并没有什么具体的绑定,因为Handler在每个线程中对应只有一个Looper和消息队列MessageQueue,自然要靠它来处理,也就是是调用Looper.loop()方法。在Looper.loop()的死循环中不断的取消息,最后回收复用。

这里要强调一下Message中的参数target(Handler),正是这个变量,每个Message才能找到对应的Handler进行消息分发,让多个Handler同时工作。

再来看看子线程中是如何处理的,首先在子线程中创建一个Handler并发送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()方法,实际上就是要创建一个Looper和你的Handler进行“关联”。   

 
 
 
 
  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()创建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.               //创建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方法来终止消息循环,否则这个子线程就会一直处于循环等待的状态,因此不需要的时候终止Looper,调用Looper.myLooper().quit()。

看完上面的代码可能你会有一个疑问,在子线程中更新UI(进行Toast)不会有问题吗,我们Android不是不允许在子线程更新UI吗,实际上并不是这样的,在ViewRootImpl中的checkThread方法会校验mThread != Thread.currentThread(),mThread的初始化是在ViewRootImpl的的构造器中,也就是说一个创建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来建立联系,因此,它是以View的形式存在的。我们来看一下Toast中的ViewRootImpl的创建过程,调用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的创建  
  11.             trySendAccessibilityEvent();  
  12.         } catch (WindowManager.BadTokenException e) {  
  13.             /* ignore */  
  14.         }  
  15.     }  

这个mWM(WindowManager)的最终实现者是WindowManagerGlobal,其的addView方法中会创建ViewRootImpl,然后进行root.setView(view, wparams, panelParentView),通过ViewRootImpl来更新界面并完成Window的添加过程。 

 
 
 
 
  1. //WindowManagerGlobal.java  
  2. root = new ViewRootImpl(view.getContext(), display); //创建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的创建是在子线程,所以mThread的值也是子线程,同时我们的更新也是在子线程,所以不会产生异常,同时也可以参考这篇文章分析,写的非常详细。同理下面的代码也可以验证这个情况 

 
 
 
 
  1. //子线程中调用      
  2. public void showDialog(){  
  3.         new Thread(new Runnable() {  
  4.             @Override  
  5.             public void run() {  
  6.                 //创建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的重点是创建它的ViewRootImpl和checkThread所在的线程是否一致。

如何在主线程中访问网络

在网络请求之前添加如下代码 

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

StrictMode(严苛模式)Android2.3引入,用于检测两大问题:ThreadPolicy(线程策略)和VmPolicy(VM策略),这里把严苛模式的网络检测关了,就可以在主线程中执行网络操作了,一般是不建议这么做的。关于严苛模式可以查看这里。

系统为什么不建议在子线程中访问UI?

这是因为 Android 的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那么为什么系统不对UI控件的访问加上锁机制呢?缺点有两个

  1.  首先加上锁机制会让UI访问的逻辑变得复杂
  2.  锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。

所以最简单且高效的方法就是采用单线程模型来处理UI操作。(安卓开发艺术探索)

子线程如何通知主线程更新UI(都是通过Handle发送消息到主线程操作UI的)

  1.  主线程中定义 Handler,子线程通过 mHandler 发送消息,主线程 Handler 的 handleMessage 更新UI。
  2.  用 Activity 对象的 runOnUiThread 方法。
  3.  创建 Handler,传入 getMainLooper。
  4.  View.post(Runnable r) 。

Looper死循环为什么不会导致应用卡死,会耗费大量资源吗?

从前面的主线程、子线程的分析可以看出,Looper会在线程中不断的检索消息,如果是子线程的Looper死循环,一旦任务完成,用户应该手动退出,而不是让其一直休眠等待。(引用自Gityuan)线程其实就是一段可执行的代码,当可执行的代码执行完成后,线程的生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder 线程也是采用死循环的方法,通过循环方式不同与 Binder 驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。Android是基于消息处理机制的,用户的行为都在这个Looper循环中,我们在休眠时点击屏幕,便唤醒主线程继续进行工作。

主线程的死循环一直运行是不是特别消耗 CPU 资源呢?其实不然,这里就涉及到 Linux  pipe/epoll机制,简单说就是在主线程的 MessageQueue 没有消息时,便阻塞在 loop 的 queue.next() 中的 nativePollOnce() 方法里,此时主线程会释放 CPU 资源进入休眠状态,直到下个消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作。这里采用的 epoll 机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

主线程的Looper何时退出

在App退出时,ActivityThread中的mH(Handler)收到消息后,执行退出。 

 
 
 
 
  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执行消息循环loop()时会执行下面这行代码,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,就只执行这个回调,然后就结束了,创建该Message的CallBack代码如下: 

 
 
 
 
  1. Message msgCallBack = Message.obtain(handler, new Runnable() {  
  2.     @Override  
  3.     public void run

    网站题目:Handler的初级、中级、高级问法,你都掌握了吗?
    网页路径:http://www.csdahua.cn/qtweb/news35/348935.html

    网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网