Handler-消息机制

Handler-消息机制_第1张图片

 

一,作用:是用来做异步的

 

二,关键类的详细介绍

1,概念介绍:

       handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己。MessageQueue就是一个存储消息的容器。

Message

· 定义:消息,理解为线程间通讯的数据单元(Handler接受和处理的消息对象。)

例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程

Message Queue

       通常翻译为“消息队列”,以队列的形式对外提供插入和删除操作。虽然被称之为消息队列,但是实际上它的数据结构却是采用的单链表的结构来存储消息列表(单链表在插入和删除操作上效率比较高)。

     MessageQueue主要包含两个操作:插入(enqueueMessage)和读取(next)。

 

Handler

· 定义:对于handler来说工作主要是消息的发送和接收过程

.作用:负责将Message添加到消息队列&处理Looper分派过来的Message

 

Looper

定义:循环器,扮演Message Queue和Handler之间桥梁的角色

作用:主要负责消息循环:循环取出Message Queue的Message;消息派发:将取出的Message交付给相应的Handler

 

         一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue

        线程是默认没有Looper的,如果需要使用Handler,就必须为线程创建Looper。我们经常提到的主线程,也叫UI线程,它就是ActivityThread,

       ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。

 

2,相关方法介绍

2.1,Handler

· 提供sendMessage方法,将消息放置到队列中

· 提供handleMessage方法,定义个各种消息的处理方式;

 

2.2,Looper

· Looper.prepare():实例化Looper对象;为当前线程生成一个消息队列;

  Looper.loop() :循环从消息队列中获取消息,交给Handler处理;此时线程处于无限循环中,不停的从MessageQueue中获取Message 消息 ;如果没有消息就阻塞(死循环)

 

为什么死循环不会造成程序卡死?

           主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/epoll机制,

           A, Android为了保证主线程在生命周期内不退出,所以用了无限循环,在循环中,如果没有事件需要处理,则使用epoll进行休眠时监听,休眠会让线程让出CPU主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源.因此节省系统资源

          B, 而在next取消息时,如果当前没有消息要处理,则会调用本地层的Looper的pollOnce来epoll_wait监听之前创建的Pipe,主线程进入休眠状态;

       C, 而唤醒则需要别的线程进行唤醒;熟悉Android源码的知道,AMS向Activity发送生命周期消息是通过Binder来实现的,ActivityThread中有Binder的服务器端即ApplicationThread,它是运行在Binder线程中的;当AMS发送消息时,ApplicationThread会通过主线程的Handler向主线程发送消息,通过往pipe里面写入字节,来将epoll_wait唤醒,即将主线程唤醒;

 

 

2.3,MessageQueue

· 提供enqueueMessage 方法,将消息根据时间放置到队列中;

· 提供next方法,从队列中获取消息,没有消息的时候阻塞;

 

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                Log.e("date","接收到传递的信息了" );
                break;
        }
        super.handleMessage(msg);
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    new Thread(new Runnable() {
        @Override
        public void run() {
            Message msg = Message.obtain();
            msg.what = 1;
            mHandler.sendMessage(msg);
        }
    }).start();
}

 

 

 

三,实现原理:

 

        1,  每当创建一个handler的时候,就会创建一个MessageQueue来存储消息,接着创建Lopper循环器,在Messagequeue队列里不停的轮训,看看有没有消息,没有消息就一直轮训,当Handler拿到消息,就会通过sendMessage()方法,将message从子线程发送出去,这个时候looper轮训到有消息来,就会将传递出去,传递到handler的handlerMessage()方法中,这个时候就可以在UI线程刷新数据了.

        这个MessageQueue队列的结构其实并不是队列,而是采用单链表的数据结构来维护消息队列,因为单链表的数据结构在插入和删除上比较有优势.

      2, Handler一般只能只在主线程创建对象,如果在子线程创建对象,会报错,(has not called Looper.prepare())因为Handler工作需要Looper,没有lopper就会报错.在主线程内部自带的调用了Lopper.prepare()方法创建Lopper对象.如果想要在子线程创建对象,就必须自己调用prepare()方法,如果想在子线程接收消息,还得调用Looper.loop()方法不停轮询,才能收到消息.但在事情结束后,要调用quit()来终止消息循环,否则线程就会处于一直等到状态

 

3,其他重要的类:

ThreadLocal,它是一个可以在指定线程中存储数据的类,数据存储以后,只有在指定的线程中可以获取到存储的数据,在其他线程无法获取该数据,ActivityThread,ActivityManagerService就用到了就用到了ThreadLocal.

4,不同线程有不同的looper

 

5,Handler是怎么实现线程切换的?

同一个进程中线程和线程之间的资源是 共享的,对于任何变量其他线程都是可以访问和修改的,只要做好同步即可。

拿到主线程的MessageQueue的实例,就可以往主线程的MessageQueue放入消息。Handler是在主线程创建的,通过Handler就可以拿到主线程的MessageQueue实例的引用;Handler 在sendMessage的时候就通过这个引用往消息队列里插入新消息。

四,部分源码

1,自身的创建&创建Message Queue:prepare()方法

 

Handler-消息机制_第2张图片

 

五,Handler造成的内存泄漏,

         1,原因

         当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。

         而Handler通常伴随着一个耗时任务,若在Activity销毁的时候还有未执行完的任务。它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)

         2,解决方法:
                2.1,静态static可以解决内存泄漏,静态类不持有外部类的对象,所以你的Activity可以随意被回收。

               

private static class MyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        
    }
}


                2.2,使用弱引用也可以解决内存泄漏,但是需要等到handler的中任务都执行完,才会释放activity内存,不如直接static释放的快

               

 private static class MyHandler extends Handler {
        private final WeakReference mActivity;

        public MyHandler(SettingActivity activity) {
            mActivity = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            System.out.println(msg);
            if (mActivity.get() == null) {
                return;
            }
           
        }
    }

               2.3,  在ondestroy()中,使用  handler.removeCallbacksAndMessages(null);

               

  public void onDestory() {
 
    if (handler != null)
        handler.removeCallbacksAndMessages(null);

  }

         当Activity finish后 handler对象还是在Message中排队,还会处理消息,所以在onDestroy的时候,取消掉该Handler对象的Message和Runnable。上面3步结合起来用。

 

   3,handler造成内存泄漏有 两种方案:

                   一种是业务逻辑上,在activity销毁的时候移除所有未执行的任务。

                   一种是从GC上,通过static的Handler或者弱引用解决。但是单独的使用弱引用性能不是太高。

 

handler内存泄漏解决方式

 

借鉴:

Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)

Handler机制深入解析

 

优秀文章:

深入源码解析Android中的Handler,Message,MessageQueue,Looper

Android消息机制1-Handler(Java层)

 

 

 

 

 

 

 

你可能感兴趣的:(异步控件)