Android之Handler消息传递机制详解

2019-03-23  本文已影响0人  darryrzhong

前言


1.Handler是什么?


2.Handler在Android中的作用


3. 我们为什么要使用Handler去处理更新UI操作呢?


4.Handler异步消息传递所涉及的相关概念


5.使用方式

1.使用 Handler.sendMessage()方式

/** 
  * 方式1:新建Handler子类(内部类)
  */

    // 步骤1:自定义Handler子类(继承Handler类) & 复写handleMessage()方法
    class mHandler extends Handler {

        // 通过复写handlerMessage() 从而确定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
         ...// 执行UI操作
            
        }
    }

    // 步骤2:在主线程中创建Handler实例
        private Handler mhandler = new mHandler();

    // 步骤3:创建所需的消息对象
        Message msg = Message.obtain(); // 实例化消息对象
        msg.what = 1; // 消息标识
        msg.obj = "AA"; // 消息内容存放

    // 步骤4:在工作线程中 通过Handler发送消息到消息队列中
        mHandler.sendMessage(msg);


/** 
  * 方式2:匿名内部类
  */
   // 步骤1:在主线程中 通过匿名内部类 创建Handler类对象
            private Handler mhandler = new  Handler(){
                // 通过复写handlerMessage()
                @Override
                public void handleMessage(Message msg) {
                        ...// 需执行UI操作
                    }
            };

  // 步骤2:创建消息对象
    Message msg = Message.obtain(); // 实例化消息对象
  msg.what = 1; // 消息标识
  msg.obj = "AA"; // 消息内容存放
  // 步骤3:在工作线程中 通过Handler发送消息到消息队列中
   mHandler.sendMessage(msg);

2.使用Handler.post()

 new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 通过psot()发送,传入1个Runnable对象
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        // 指定操作UI内容
                   
                    }

                });
            }
        }.start();


6.Handler底层原理及源码分析

在源码分析前,先来了解Handler机制中的几个核心类

下面开始源码分析,注意力集中了
上文中我们提到过Handler发送消息有两种方式,分别是

方式1:使用 Handler.sendMessage()

  //通过匿名内部类 创建Handler类对象
    private Handler mhandler = new  Handler(){
        // 通过复写handlerMessage()从而确定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
                ...// 需执行的UI操作
            }
    };

---------->>开始源码分析

public Handler() {

            this(null, false);
            // ->>此处this指代的就是当前的Handler实例,调用有参构造

    }

public Handler(Callback callback, boolean async) {

            ...// 无关代码我就不贴了

            // 1. 指定Looper对象
                mLooper = Looper.myLooper();
                if (mLooper == null) {
                    throw new RuntimeException(
                        "Can't create handler inside thread that has not called Looper.prepare()");
                }
                // Looper.myLooper()作用:获取当前线程的Looper对象;若线程无Looper对象则抛出异常

                // 可通过执行Loop.getMainLooper()方法获得主线程的Looper对象

            // 2. 绑定消息队列对象(MessageQueue)
                mQueue = mLooper.mQueue;
                // 获取该Looper对象中保存的消息队列对象(MessageQueue)
                // 至此,完成了handler 与 Looper对象中MessageQueue的关联
    }

public static void main(String[] args) {
            ... // 无关的代码

            Looper.prepareMainLooper(); 
            // 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)

            ActivityThread thread = new ActivityThread(); 
            // 2. 创建主线程

            Looper.loop(); 
            // 3. 自动开启 消息循环 

        }

方式1: 使用Handler.post()

 public void dispatchMessage(Message msg) {

    // 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
    // 则执行handleCallback(msg),即回调Runnable对象里复写的run()
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }

            // 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息,即回调复写的handleMessage(msg)
            handleMessage(msg);

        }
    }


   public void handleMessage(Message msg) {  
          ... // 创建Handler实例时复写
   } 

从以上源码来看,使用Handler.post()时,系统会自动回调Runnable对象里复写的run()方法,将其打包成msg对象, 实际上和sendMessage(Message msg)发送方式相同。

至此,关于Handler的异步消息传递机制的解析就完成了。


7.关于Handler 内存泄露的原因

3.内存泄漏的原因
首先我们先要了解一些其他的知识点。

而在Handler处理消息的时候,Handler必须处理完所有消息才会与外部类解除引用关系,如果此时外部Activity需要提前被销毁了,而Handler因还未完成消息处理而继续持有外部Activity的引用。由于上述引用关系,垃圾回收器(GC)便无法回收MainActivity,从而造成内存泄漏。


8.如何解决Handler内存泄漏

1.静态内部类+弱引用

将Handler的子类设置成 静态内部类,同时,还可加上 使用WeakReference弱引用持有Activity实例。
原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。


    // 设置为:静态内部类
    private static class FHandler extends Handler{

        // 定义 弱引用实例
        private WeakReference<Activity> reference;

        // 在构造方法中传入需持有的Activity实例
        public FHandler(Activity activity) {
            // 使用WeakReference弱引用持有Activity实例
            reference = new WeakReference<Activity>(activity); }

        // 复写handlerMessage() 
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                 //更新UI
                    break;
                case 2:
                //更新UI
                    break;

            }

2.当外部l类结束生命周期时,清空Handler内消息队列

@Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
        // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
    }

推荐使用上述解决方法一,以保证保证Handler中消息队列中的所有消息都能被执行


总结

本文主要讲述了Handler的基本原理和使用方法,以及造成内存泄漏的原因和解决方案。


欢迎关注作者darryrzhong,更多干货等你来拿哟.

请赏个小红心!因为你的鼓励是我写作的最大动力!

更多精彩文章请关注

上一篇下一篇

猜你喜欢

热点阅读