Android 第十四课——Handler Looper Message

1、基础概念

1)android.os.Handler 

2)主要接受子线程发送的数据, 并用此数据配合主线程更新UI. 

3)应用程序一旦启动,Android UI 这个主线程的生命周期就开始了,然而,Android UI 线程并不是线程安全的,也就是说,更新UI只能在主线程中同步更新,子线程中异步操作是危险的。所以,项目中如果我们直接new Thread 内部去更改Android UI,往往会报错误如下:

    java.lang.RuntimeException: Can‘t create handler inside thread that has not called

为了避免错误发生,那么更新Android UI 必须在主线程中操作,具体如何操作呢?Handler就发挥了特的作用。Handler的使用同时也会带来许多相关的知识,下面一一讲解。


2、实例化

1)采用默认的方式 new Handler();项目中很少使用这种方式。如果他是主线程中运行的,那么这个handler对象内部绑定了一个Looper对象Looper.getMainLooper();Looper对象是用来干嘛的呢? Looper类是用来为一个线程开启一个消息循环。可以这样获取对应的线程:handler.getLooper().getThread()。默认情况下Android中新诞生的线程是没有开启消息循环的(主线程中new的则有)。换句话说,Looper内部有一个线程对象,这个对象会轮询获取消息队列的消息,这个消息队列就是MessageQueue,Looper对象通过MessageQueue来存放消息和事件,一个线程只能有一个Looper,对应一个MessageQueue。

总结就是:

    A:主线程中 handler = new Handler();那么这个handler内部绑定了 Looper.getMainLooper() 对象或Looper.myLooper();

    B:在子线程中,如在Thread中 handler = new Handler();那么这个handler的Looper对象时空的。

那么,关于子线程中应该如何为handler注册Looper呢?可以这样使用

  HandlerThread handlerThread = new HandlerThread("sub thread");

  handlerThread.start(); // 这句很重要 ,应该是为线程异步运行提供环境

  handler = new Handler(handlerThread.getLooper());

2)采用继承方式,继承方式就是多了Looper对象的绑定方法,这样绑定确实有点麻烦。

     class MyHandler extends Handler {
         public MyHandler() {
         }
 
         public MyHandler(Looper looper) {
             super(looper);
         }
 
         // 子类必须重写此方法,接受数据
         @Override
         public void handleMessage(Message msg) {
              // msg.what 进行判断,调用对应的方法更新UI
         }
     }

3)采用内部类方式,内部类方式与默认new Handler相差无几,唯一的区别就是内部类需要重写handleMessage方法,否者意义就不大了,项目中往往使用这种方式。那么为什么推荐这种方式呢?首先我们定义个全局的私有Handler,然后定义时采用内部类实例化对象,下次使用时直接调用handler,只要保证这个handler的Looper对象是主线程的Looper就好,也就是不要在自己的Thread中去实例化handler。


3、handler调用

1)由于一切关于消息队列的事情都有Looper对象处理,那么就不必过于深究了。现在只要好好理解如何使用handler,关于handler同步的问题,如果handler内部绑定的是主线程的Looper对象,那么,在我们调用 handler.post(new Thread())的时候,这个注册的Thread其实是在主线中串行,而不是并行。就好像调用的是Thread对象的run方法而非start方法,那么意义就不大了!

2)项目中耗时的工作应该给一个异步的子线程处理,如果你放在主线程中的话,界面会出现假死现象,,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。然而子线程又不能更新主线程的UI,那么问题就来了,应该如何结合Handler让子线程与主线程并行呢?一种方式就是采用 handlerThread 为 当前的handler对象绑定一个全新的 Looper对象,那么就可以实现以上要求了。当然,更好的方式是:单独new Thread ,在Thread内部调用已经实例化话好的handler对象,这样也是一个异步调用。


4 handler消息传送

1)Message对象是与handler绑定的,handler可以采用sendMessage(Message)方式把Message对象发送到handler的handleMessage方法中,一般我们采用message.what 方法作为不同消息类型的标识;另外,message的两个参数 arg1 与 arg2 是效率相对较高的,因为他们消耗系统性能较少。

2)Bundle对象是handler的一个成员变量,可以把bundle看成是一个特殊的Map对象,二者都是用来缓存数据,只是Bundle的key只能是String,而且Bundle的方法相对Map更为丰富。另外,Bundle对象不局限与handler,在intent或者在Activity的onCreate方法中处处可见。


总结:

    不能在子线程中直接更新Android UI ,需要采用Handler处理,然而handler处理时需要注意内部的Looper对象是否已经绑定,如果没有绑定需要手动注册一个Looper。对于同步还是异步调用,也需要适当的处理。


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。