Android基础之Handler理解

作者: 正阳Android | 来源:发表于2017-11-17 11:29 被阅读4次

    1.Handler: 用于线程之间的通信,是一种异步回调机制.

    2.Handler内部实现主要涉及到Looper,Message,MessageQueue以及Thread类.

       Handler: 发送消息和接收处理消息

       Message: 消息载体

       MessageQueue: 消息队列,存放发送的消息,内部使用单链表结构.

       Looper: 轮询器,从消息队列里面循环获取消息,有消息立即处理,没有消息就会阻塞,等待消息到来.

       Thread :线程,每一个线程最多只允许一个Looper.

    3.流程可以总结为:

           调用Looper.prepare方法,创建Looper对象,Looper的构造方法中会创建一个MessageQueue,同时将looper绑定   到当前线程. 

          创建Handler的时候,其构造方法里会获取到当前线程的Looper对象,并获取到该looper绑定的消息队列,messagequeue;当调用sendmessage()的时候其底层实际上调用了enqueuemessage方法,该方法主要做了两步,第一步是msg.target= this,也就是指向当前handler;第二步是,调用了ueue.enqueueMessage(msg, uptimeMillis);方法,将消息插入到当前线程的消息队列.

            Looper.loop方法,会循环的从messagequeue读取消息,并调用msg.target.dispatchmessage(msg)方法分发消息.,上面可以知道msg.target其实就是当前线程的handler,所以其实就是调用了handler的dispatchmessage方法,交给handlemessag处理.

    现在我们来思考几个问题

    1.上面说了,轮询器去读取消息是循环读取,这是一个死循环,为什么我们的程序没有卡死呢?

    答; ui线程,也就是我们说的主线程,也被称作ActivityThread,默认初始化了Looper,这也是我们可以使用handler的原因.我们知道线程是一段可执行的代码,代码执行完毕,线程生命周期也就结束了,步入死亡;ui线程也就是main线程如果也是这样执行结束就死亡肯定不是我们期望的结果,所以如果我们想要将线程一直执行下午,那么最简单的做法就是让代码一直执行下去,而死循环便能保证不会退出.当然这里不是简单的死循环,没有消息的时候会进行休眠,让出cpu,这样我们的程序就不会卡死.;这里既然是死循环,那么其他事务是如何处理的呢?其实我们看源码会知道,有一个thread.attach(false),创建了新的线程去处理.真正会卡死主线程的操作是在回调方法 onCreate/onStart/onResume 等操作时间过长,会导致掉帧,甚至发生 ANR,looper.loop 本身不会导致应用卡死。

    2.Handler是如何 能线程切换呢?

    答: 首先我们要知道线程是共享资源的,所以 Handler 处理不同线程问题就只要注意异步情况即可。Handler 创建的时候会采用当前线程的 Looper 来构造消息循环系统,Looper 在哪个线程创建,就跟哪个线程绑定,并且Handler 是在他关联的 Looper 对应的线程中处理消息的.

    3.系统为什么不允许在子线程中访问UI?

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

    ①首先加上锁机制会让UI访问的逻辑变得复杂

    ②锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。

    所以最简单且高效的方法就是采用单线程模型来处理UI操作。

    那么子线程一定不能更新UI吗?

    答:当然是可以的,我们可以使用Handler来完成;也可以使用 Activity 对象的 runOnUiThread 方法。

    4.那为什么我们在子线程中使用toast和dialog会报错呢?

    答:直接调用会报错,提示需要使用loopprepare和looper.loop方法(但是不建议这么做,因为它会使线程无法执行结束,导致内存泄露)

    5.如何处理Handler 使用不当导致的内存泄露?

    答: 上文内存泄漏的我们讲过,使用handler可能会导致内存泄漏,譬如,延迟发送消息,Activity销毁但是消息还没执行,另外就是handler若是使用到了context传入了Actiity也会导致出现内存泄漏的问题.

    解决办法: 1.有延时消息,要在 Activity 销毁的时候移除 Messages

                    2. 匿名内部类导致的泄露改为匿名静态内部类,并且对上下文或者 Activity 使用弱引用。.

    本文参考文章: https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA==&mid=2650241824&idx=1&sn=aa4be9ab94828bfff4ee9108a85a0f0b&chksm=88638a4fbf1403594b7e2e0fc04a30ced3ec7819cc891b4e872b52e29692b9f7a11a8658a97d&mpshare=1&scene=23&srcid=1117IBUFS6V6EDHPf0emAMs3#rd

    有兴趣的话可以看下源码;

    private static void prepare(booleanquitAllowed){// 创建一个Looper对象

             if(sThreadLocal.get() !=null) {// 每个线程只允许有一个looper

                        thrownewRuntimeException("Only one Looper may be created per thread"); 

                } 

                sThreadLocal.set(newLooper(quitAllowed)); 

     }

    // 看下Looper的构造方法

    private   Looper(boolean   quitAllowed){  

          mQueue =newMessageQueue(quitAllowed);// new了一个消息队列

           mThread = Thread.currentThread(); // 绑定到当前线程

     }

    //  Looper.loop()方法,循环从消息队列中读取消息,并且分发消息

    public static void loop(){

              //myLooper方法会从ThreadLocal中获取到当前线程的Looper

               finalLooper me = myLooper();

                    if(me ==null) {// 若获取不到looper对象

                           thrownewRuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

                    }

              final   MessageQueue queue = me.mQueue; 

               Binder.clearCallingIdentity();

              final  long  ident = Binder.clearCallingIdentity();

             for(;;) {//循环的去MessageQueue中读取消息

                       Message msg = queue.next();

                           if(msg ==null) {

                             return; 

                      } 

                      Printer logging = me.mLogging;

            if(logging !=null) { 

                    logging.println(">>>>> Dispatching to "+ msg.target +" "+ msg.callback +": "+ msg.what); 

     }//调用发送该消息的Handler的dispatchMessage方法处理该消息 

      //这里的msg.target == Handler,是在Handler发送消息的时候进行赋值的

               msg.target.dispatchMessage(msg);

                     if(logging !=null) { 

                     logging.println("<<<<< Finished to "+ msg.target +" "+ msg.callback);

                      }

                    final        long    newIdent = Binder.clearCallingIdentity();

                     if(ident != newIdent) { 

               Log.wtf(TAG,"Thread identity changed from 0x"+ Long.toHexString(ident) +" to 0x"+             Long.toHexString(newIdent) +" while dispatching to "+ msg.target.getClass().getName() +" "+ msg.callback +" what="+ msg.what);

                            } 

                 msg.recycleUnchecked();

    }

    Handler的构造方法

    public    Handler(

                    Callback callback,booleanasync){

                         if(FIND_POTENTIAL_LEAKS) {

                           finalClass klass = getClass();

                                  if((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) ==0) { 

                      Log.w(TAG,"The following Handler class should be static or leaks might occur: "+       klass.getCanonicalName()); 

                 } 

                   }

                   //获取当前线程中的Looper 

                mLooper = Looper.myLooper();

                 if(mLooper ==null) {

               //如果当前线程没有Looper就会抛出异常。

                           thrownewRuntimeException("Can't create handler inside thread that has not called Looper.prepare()"); 

          }

                 //获取Looper中的MessageQueue,可以看出,Looper中为每个线程维护了一个Message

                  mQueue = mLooper.mQueue;

                       mCallback = callback; 

                  mAsynchronous = async;

    }


    //Handler发送消息的方法,追踪senndmessage到最底层的方法就是enqueueMessage。

    private boolean enqueueMessage(MessageQueue queue, Message msg,longuptimeMillis){

             msg.target =this;

             if(mAsynchronous) { 

          msg.setAsynchronous(true);

     }

        returnqueue.enqueueMessage(msg, uptimeMillis);

    }

    相关文章

      网友评论

        本文标题:Android基础之Handler理解

        本文链接:https://www.haomeiwen.com/subject/ivkrvxtx.html