iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android 消息机制以及handler的内存泄露
  • 982
分享到

Android 消息机制以及handler的内存泄露

消息机制handlerAndroid 2022-06-06 07:06:41 982人浏览 独家记忆
摘要

Handler 每个初学Android开发的都绕不开Handler这个“坎”,为什么说是个坎呢,首先这是Android架构的精髓之一,其次大部分人都是知其然却不知其所以然。今

Handler

每个初学Android开发的都绕不开Handler这个“坎”,为什么说是个坎呢,首先这是Android架构的精髓之一,其次大部分人都是知其然却不知其所以然。今天看到Handler.post这个方法之后决定再去翻翻源代码梳理一下Handler的实现机制。

异步更新UI

先来一个必背口诀“主线程不做耗时操作,子线程不更新UI”,这个规定应该是初学必知的,那要怎么来解决口诀里的问题呢,这时候Handler就出现在我们面前了(AsyncTask也行,不过本质上还是对Handler的封装),来一段经典常用代码(这里忽略内存泄露问题,我们后面再说):

首先在Activity中新建一个handler:


private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      switch (msg.what) {
        case 0:
          mTestTV.setText("This is handleMessage");//更新UI
          break;
      }
    }
  };

然后在子线程里发送消息:


new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络
          mHandler.sendEmptyMessage(0);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }).start();

至此完成了在子线程的耗时操作完成后在主线程异步更新UI,可是并没有用上标题的post,我们再来看post的版本:


new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络
          Handler handler = new Handler();
          handler.post(new Runnable() {
            @Override
            public void run() {
              mTestTV.setText("This is post");//更新UI
            }
          });
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }).start();

从表面上来看,给post方法传了个Runnable,像是开了个子线程,可是在子线程里并不能更新UI啊,那么问题来了,这是怎么个情况呢?带着这个疑惑,来翻翻Handler的源码

先来看看普通的sendEmptyMessage是什么样子:


public final boolean sendEmptyMessage(int what)
  {
    return sendEmptyMessageDelayed(what, 0);
  }

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
  }

将我们传入的参数封装成了一个消息,然后调用sendMessageDelayed:


public final boolean sendMessageDelayed(Message msg, long delayMillis)
  {
    if (delayMillis < 0) {
      delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  }

再调用sendMessageAtTime:


public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = MQueue;
    if (queue == null) {
      RuntimeException e = new RuntimeException(
          this + " sendMessageAtTime() called with no mQueue");
      Log.w("Looper", e.getMessage(), e);
      return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
  }

好了,我们再来看post():


public final boolean post(Runnable r)
  {
    return sendMessageDelayed(getPostMessage(r), 0);//getPostMessage方法是两种发送消息的不同之处
  }

方法只有一句,内部实现和普通的sendMessage是一样的,但是只有一点不同,那就是 getPostMessage(r) 这个方法:


private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
  }

这个方法我们发现也是将我们传入的参数封装成了一个消息,只是这次是m.callback = r,刚才是msg.what=what,至于Message的这些属性就不看了

Android消息机制

看到这里,我们只是知道了post和sendMessage原理都是封装成Message,但是还是不清楚Handler的整个机制是什么样子,继续探究下去。

刚才看到那两个方法到最终都调用了sendMessageAtTime


public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
      RuntimeException e = new RuntimeException(
          this + " sendMessageAtTime() called with no mQueue");
      Log.w("Looper", e.getMessage(), e);
      return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
  }

这个方法又调用了 enqueueMessage,看名字应该是把消息加入队列的意思,点进去看下:


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
      msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
  }

mAsynchronous这个异步有关的先不管,继续将参数传给了queue的enqueueMessage方法,至于那个msg的target的赋值我们后面再看,现在继续进入MessageQueue类的enqueueMessage方法,方法较长,我们看看关键的几行:


Message prev;
for (;;) {
  prev = p;
  p = p.next;
  if (p == null || when < p.when) {
    break;
  }
  if (needWake && p.isAsynchronous()) {
    needWake = false;
  }
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;

果然像方法名说的一样,一个无限循环将消息加入到消息队列中(链表的形式),但是有放就有拿,这个消息怎样把它取出来呢?

翻看MessageQueue的方法,我们找到了next(),代码太长,不赘述,我们知道它是用来把消息取出来的就行了。不过这个方法是在什么地方调用的呢,不是在Handler中,我们找到了Looper这个关键人物,我叫他环形使者,专门负责从消息队列中拿消息,关键代码如下:


for (;;) {
   Message msg = queue.next(); // might block
   ...
   msg.target.dispatchMessage(msg);
   ...
   msg.recycleUnchecked();
}

简单明了,我们看到了我们刚才说的msg.target,刚才在Handler中赋值了msg.target=this,所以我们来看Handler中的dispatchMessage:


public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
      handleCallback(msg);
    } else {
      if (mCallback != null) {
        if (mCallback.handleMessage(msg)) {
          return;
        }
      }
      handleMessage(msg);
    }
  }

1.msg的callback不为空,调用handleCallback方法(message.callback.run())
2.mCallback不为空,调用mCallback.handleMessage(msg)
3.最后如果其他都为空,执行Handler自身的 handleMessage(msg) 方法
msg的callback应该已经想到是什么了,就是我们通过Handler.post(Runnable r)传入的Runnable的run方法,这里就要提提Java基础了,直接调用线程的run方法相当于是在一个普通的类调用方法,还是在当前线程执行,并不会开启新的线程。

所以到了这里,我们解决了开始的疑惑,为什么在post中传了个Runnable还是在主线程中可以更新UI。

继续看如果msg.callback为空的情况下的mCallback,这个要看看构造方法:


1.
public Handler() {
    this(null, false);
  }
2.  
public Handler(Callback callback) {
    this(callback, false);
  }
3.
public Handler(Looper looper) {
    this(looper, null, false);
  }
4.
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
  }
5.
public Handler(boolean async) {
    this(null, async);
  }
6.
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
      final Class<? extends Handler> 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());
      }
    }
    mLooper = Looper.myLooper();
    if (mLooper == null) {
      throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
  }
7.
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
  }

具体的实现就只有最后两个,已经知道mCallback是怎么来的了,在构造方法中传入就行。

最后如果这两个回调都为空的话就执行Handler自身的handleMessage(msg)方法,也就是我们熟知的新建Handler重写的那个handleMessage方法。

Looper

看到了这里有一个疑惑,那就是我们在新建Handler的时候并没有传入任何参数,也没有哪里显示调用了Looper有关方法,那Looper的创建以及方法调用在哪里呢?其实这些东西Android本身已经帮我们做了,在程序入口ActivityThread的main方法里面我们可以找到:


 public static void main(String[] args) {
  ...
  Looper.prepareMainLooper();
  ...
  Looper.loop();
  ...

总结

已经大概梳理了一下Handler的消息机制,以及post方法和我们常用的sendMessage方法的区别。来总结一下,主要涉及四个类Handler、Message、MessageQueue、Looper:

新建Handler,通过sendMessage或者post发送消息,Handler调用sendMessageAtTime将Message交给MessageQueue

MessageQueue.enqueueMessage方法将Message以链表的形式放入队列中

Looper的loop方法循环调用MessageQueue.next()取出消息,并且调用Handler的dispatchMessage来处理消息

在dispatchMessage中,分别判断msg.callback、mCallback也就是post方法或者构造方法传入的不为空就执行他们的回调,如果都为空就执行我们最常用重写的handleMessage。

最后谈谈handler的内存泄露问题
再来看看我们的新建Handler的代码:


private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      ...
    }
  };

当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有Activity的引用。

而Handler通常会伴随着一个耗时的后台线程一起出现,这个后台线程在任务执行完毕后发送消息去更新UI。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束。

另外,如果执行了Handler的postDelayed()方法,那么在设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。

解决方法之一,使用弱引用:


static class MyHandler extends Handler {
  WeakReference<Activity > MactivityReference;
  MyHandler(Activity activity) {
    mActivityReference= new WeakReference<Activity>(activity);
  }
  @Override
  public void handleMessage(Message msg) {
    final Activity activity = mActivityReference.get();
    if (activity != null) {
      mImageView.setImageBitmap(mBitmap);
    }
  }
}

以上就是对Android handler 消息机制的资料整理,后续继续补充相关资料,谢谢大家对本站的支持!

您可能感兴趣的文章:android异步消息机制 源码层面彻底解析(1)代码分析Android消息机制Android异步消息机制详解android线程消息机制之Handler详解android利用消息机制获取网络图片Android 消息机制详解及实例代码Android的消息机制Android消息机制Handler的工作过程详解深入剖析Android消息机制原理Android6.0 消息机制原理解析Android 消息机制问题总结深入浅析Android消息机制Android编程中的消息机制实例详解Android编程之消息机制实例分析android异步消息机制 从源码层面解析(2)


--结束END--

本文标题: Android 消息机制以及handler的内存泄露

本文链接: https://www.lsjlt.com/news/23849.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android中怎么利用Handler防止内存泄露
    今天就跟大家聊聊有关Android中怎么利用Handler防止内存泄露,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。 Handler可能导致的内存泄露及其优化 &...
    99+
    2023-05-30
    android handler
  • Android Handler消息机制分析
    目录Handler是什么?Handler 的基本使用用法一:通过 send 方法用法二:通过 post 方法Handler 类MessageQueue 类Looper 类Handle...
    99+
    2024-04-02
  • Android中使用Handler造成的内存泄露如何解决
    这篇文章将为大家详细讲解有关Android中使用Handler造成的内存泄露如何解决,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、什么是内存泄露?  Java使用有向图机制,通过GC自动...
    99+
    2023-05-30
    android handler
  • Android消息机制Handler深入理解
    目录概述Handler的使用Handler架构Handler的运行流程源码分析在子线程创建Handler主线程的LooperLooperHandler分发消息总结概述 Handler...
    99+
    2024-04-02
  • Android消息机制Handler用法总结
    1.简述 Handler消息机制主要包括: MessageQueue、 Handler、 Looper、Message。 Message:需要传递的消息,可以传递数据; ...
    99+
    2024-04-02
  • Android消息机制Handler如何使用
    这篇文章主要介绍“Android消息机制Handler如何使用”,在日常操作中,相信很多人在Android消息机制Handler如何使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android消息机制Ha...
    99+
    2023-06-21
  • Android handler异步消息机制是什么
    Android中的Handler是一种基于消息机制的异步处理机制。它可以用来将消息或Runnable对象发送到主线程或者后台线程中执...
    99+
    2023-10-18
    Android
  • Android消息机制Handler用法有哪些
    这篇文章主要讲解了“Android消息机制Handler用法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android消息机制Handler用法有哪些”吧!1.简述Handler消息...
    99+
    2023-06-21
  • android线程消息机制之Handler详解
    android线程消息机制主要由Handler,Looper,Message和MessageQuene四个部分组成。平常在开发中,我们常用来在子线程中通知主线程来更新,其实整个安卓生命周期的驱动都是通过Handler(ActivityThr...
    99+
    2023-05-30
    android 线程消息机制 handler
  • 详解Android内存泄露及优化方案
    目录一、常见的内存泄露应用场景?1、单例的不恰当使用 2、静态变量导致内存泄露 3、非静态内部类导致内存泄露 4、未取消注册或回调导致内存泄露 5、定时器Timer 和 TimerT...
    99+
    2024-04-02
  • 掌握Android Handler消息机制核心代码
    目录一、handler基本认识1、基本组成2、基本使用方法3、工作流程二、发送消息 三、消息进入消息队列1、入队前的准备工作2、将消息加入队列四、从消息队列里取出消息1、准备工作2、...
    99+
    2024-04-02
  • 基于ThreadLocal 的用法及内存泄露(内存溢出)
    目录使用构造方法静态方法公共方法内存泄露解决方法为什么要将ThreadLocal 定义成 static 变量对ThreadLocal内存泄漏引起的思考概述使用场景样例代码Thread...
    99+
    2024-04-02
  • android内存泄露的根本原因是什么
    Android内存泄漏的根本原因是因为对象在不再使用时未能被垃圾回收器正确地回收,导致内存持续占用和增加。这主要是因为以下几个常见情...
    99+
    2023-09-15
    android
  • 内存溢出、内存泄露的概述及常见情形
    内存溢出(OutofMemoryError) 简述 java doc 中对 Out Of Memory Error 的解释是,没有空闲内存,并且垃圾收集器也无法提供更多内存。 JVM 提供的内存管理机...
    99+
    2023-09-01
    jvm java 面试 内存泄露 内存溢出
  • java内存管理关系及内存泄露的原理分析
    目录java内存管理关系及内存泄露原理java对象和内存的关系创建对象null的作用内存泄露检测内存泄露的原理java内存管理关系及内存泄露原理 这可能是最近写的博客中最接近底层的了...
    99+
    2024-04-02
  • 分析Android常见的内存泄露和解决方案
    目录一、前言二、Android 内存泄露场景2.1、非静态内部类的静态实例2.2、多线程相关的匿名内部类/非静态内部类2.3、Handler 内存泄露2.4、静态 Activity ...
    99+
    2024-04-02
  • Android同步异步任务、多线程及Handler消息处理机制实例分析
    这篇“Android同步异步任务、多线程及Handler消息处理机制实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“A...
    99+
    2023-07-04
  • Handler消息传递机制类引入及执行流程详解
    目录提要1.学习路线图:2.Handler类的引入3.Handler的执行流程图4.Handler的相关方法5.Handler的使用示例1)Handler写在主线程中2)Handle...
    99+
    2023-05-15
    Handler消息传递 Handler 执行流程
  • Java内存泄露监控工具以及JVM监控工具的实例分析
    Java内存泄露监控工具以及JVM监控工具的实例分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。jstack -- 如果java程序崩溃生成core文件,jstack工具...
    99+
    2023-06-17
  • 没有resolve及reject的Promise是否会造成内存泄露
    目录正文DevTools测试执行queryObjects(Promise)测试事件回调可疑的泄露对象正文 DevTools测试 可以用 DevTools 的 queryObjects...
    99+
    2022-11-13
    Promise内存泄露 Promise内存
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作