Handler 消息机制以及内存泄漏

简介:

1. 消息机制

1.1 post系列

通过查看源码可知,post(Runnable r)postDelayed(Runnable r, long delayMillis)最终调用的都是sendMessageDelayed方法:

// post
 public final boolean post(Runnable r){
    return sendMessageDelayed(getPostMessage(r), 0);
}
// postDelayed
public final boolean postDelayed(Runnable r, long delayMillis){
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

1.2 postAtTime

postAtTime(Runnable r, long uptimeMillis)最终调用的是sendMessageAtTime方法:

// postAtTime
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

这里面都有一个共同的方法getPostMessage

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

m.callback = r这句可以看出:getPostMessage就是把传入的 Runnable 赋值给 Message 对象的 callback 属性

1.3 sendEmptyMessage

sendEmptyMessage最终指向sendEmptyMessageDelayed函数:

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

msg.what = what这句可以看出:sendEmptyMessageDelayed就是把 what 赋值给 Message 的 what 属性。

1.4 sendMessage(msg : Message)

至于常用的sendMessage(msg : Message)就不用细说了,这是直接传入 Message 类型的参数。

综合以上这几点来说,各种发送消息的方法最终都是把消息赋值给 Message 对象(或者 Message 的属性),而且这些方法最终调用的都是 MessageQueue 中的enqueueMessage方法,就是把消息加入消息队列

1.5 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;

用一个无限循环将消息加入到消息队列中(链表的形式),到这里把消息发出去并加入队列这两步算是完成了,接下来就是取出并处理消息。

1.6 Looper 取出消息

Looper 中有一个死循环(Looper.loop())用来不断从队列中取出消息:

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next();
        ...代码省略
        msg.target.dispatchMessage(msg);
        ...代码省略
        msg.recycleUnchecked();
    }
}

queue.next()每次取出一条 Message 消息,然后交由msg.targer.dispatchMessage(msg)处理,从上篇文章中可以知道,msg.targer就是发出消息的 Handler,所以我们只需要关注dispatchMessage(msg)

1.7 dispatchMessage(msg)处理消息

dispatchMessage(msg)在 Handler 类中

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)方法

第 1 点就是上面的 【1.1 post系列】 和 【1.2 postAtTime】,第 3 点就是我们最常见的handleMessage方法。需要注意一下就是callback.run()这里直接调用线程的 run 方法,相当于普通方法调用,不会开启新的线程

现在谈谈第 2 点,Handler 有很多种构造方法,除了上一篇文章提到的public Handler(Looper looper)Handler()等,还有一个:

public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

Callback 是这样的:

public interface Callback {
        public boolean handleMessage(Message msg);
    }

需要重写handleMessage,这不就是 Handler 里面的handleMessage吗?其实两者是有区别的:

// Handler
public void handleMessage(Message msg) {}


// Callback
public boolean handleMessage(Message msg);

Callback 里面的handleMessage返回值是 Boolean 类型的,那么接下来分别返回 true 和 false 看看效果吧:

class MainActivity : AppCompatActivity() {
    var handler: Handler? = null
    var looper: Looper? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        looper = Looper.getMainLooper()
        val callback = object: Handler.Callback{
            override fun handleMessage(msg: Message?): Boolean {
                Log.e("abc","--- Callback:threadName ---" + Thread.currentThread().name
                )
                return true
            }
        }
        val thread = object : Thread() {
            override fun run() {
                super.run()
                handler = object : Handler(looper, callback) {
                    override fun handleMessage(msg: Message?) {
                        super.handleMessage(msg)
                        Log.e("abc","--- handleMessage:threadName ---" + Thread.currentThread().name
                        )
                    }
                }
            }
        }

        thread.start()

        myBtn.setOnClickListener {
            handler?.sendEmptyMessage(4)
        }
    }
}

// Log 打印情况
--- Callback:threadName ---main

如果返回值类型改成 false:

val callback = object: Handler.Callback{
            override fun handleMessage(msg: Message?): Boolean {
                Log.e("abc", "--- Callback:threadName ---" + Thread.currentThread().name
                )
                return false
            }
        }
        
// Log 打印情况
--- Callback:threadName ---main
--- handleMessage:threadName ---main

所以,Callback 中的handleMessage返回 true 就不继续执行 Handler 中的handlerMessage了,反之则两个handleMessage都执行。其实这些从dispatchMessage方法中可以看出来(返回 true 则 return 终止,否则继续执行 handleMessage):

 if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
        return;
    }
}
handleMessage(msg);

总结

上面主要讲了消息的发送和取出,大概知道了 Handler 消息机制的工作流程:

  1. Handler 对象通过post(postDelayedpostAtTime)或者sendMessagesendEmptyMessage)把消息(Message)交给 MessageQueue
  2. MessageQueue.enqueueMessage方法将 Message 以链表的形式放入消息队列中
  3. Looper.loop()循环调用 MessageQueue 的next()取出消息,交给 Handler 的dispatchMessage方法处理消息
  4. dispatchMessage()中分别判断msg.callback和构造函数传入的mCallback是否为空,不为空则执行它们的回调,为空则执行 Handler 自身的handlerMessage方法。

2. Handler 内存泄漏问题

2.1 引起内存泄漏的原因

下面这样写会有内存泄漏风险:

val mHandler = object : Handler() {
        override fun handleMessage(msg: Message?) {
            super.handleMessage(msg)
        }
    }

Android Studio 会标黄警告,鼠标放在 handler 代码块部分还会弹出提示,大概意思就是建议你用静态模式或者弱引用。上面这种写法相当于定义了一个匿名内部类,非静态的匿名内部类默认是持有外部类(对应 Activity 等)引用的。如果发消息的 handler 所在线程还在执行,当前 Activity 就被 finish 了,那么该 Handler 的匿名内部类持有 Activity 的引用,所以 Activity 对象是无法被 GC 机制回收的。即:执行了 finish 代码,但 Activity 对象还在内存中(内存泄漏)。这种对象如果越来越多,就会有 OOM(内存溢出)的可能。

2.2 解决办法

kotlin 中没有静态类这个概念,这里用 java 静态内部类举例:

static class MyHandler extends Handler {
        WeakReference<MyActivity> weakActivity;

        MyHandler(MyActivity activity) {
            weakActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MyActivity activity = weakActivity.get();
            // activity.text = "......"
        }
    }
  1. 静态内部类不持有外部类引用,所以不会导致 Activity 对象泄漏(java 中 「静态的」等于「类的」,静态内部类如果能持有外部类引用,那说明外部类的引用就是内部「类的」,这不符合逻辑,这样写编译都不通过)。
  2. 但该静态内部类必须使用外部类的引用(比如操作 UI),此时就可以用弱引用的方式。上面代码用的是把 Activity 的弱引用在 Handler 构造函数中初始化,这样如果需要操作 UI,可以使用activity.text = "test"这种方式。

参考文章:

  1. 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露
  2. Handler内存泄露及解决方案
相关文章
|
4天前
|
存储 缓存
操作系统的虚拟内存管理机制
在现代计算机系统中,虚拟内存是一种至关重要的内存管理技术。它允许操作系统使用硬盘空间来扩展物理内存容量,从而支持更多并发运行的程序。本文将深入探讨虚拟内存的概念、实现方式以及其在操作系统中的作用和重要性。
|
6天前
|
缓存 算法 Linux
深入理解操作系统的内存管理机制
【6月更文挑战第23天】内存管理是操作系统中一个至关重要的功能,它直接影响到系统性能和资源利用效率。本文旨在深入探讨现代操作系统中内存管理的核心概念、关键技术以及面临的挑战。通过分析内存管理的基本原理、内存分配策略、虚拟内存技术、缓存管理和内存安全等方面,揭示内存管理在提升操作系统稳定性、安全性与高效性方面的作用。文章将结合具体操作系统实例,如Windows、Linux等,阐述不同内存管理技术的实现细节和优化策略,为读者提供对操作系统内存管理机制全面而深刻的认识。
18 3
|
8天前
|
存储 缓存 算法
探索现代操作系统的虚拟内存管理机制
【6月更文挑战第21天】在数字时代的浪潮中,操作系统作为计算机系统的核心,其设计和管理策略直接影响着计算效率和用户体验。本文将深入探讨现代操作系统中的虚拟内存管理机制,包括其工作原理、实现方式及其对系统性能的影响。通过分析虚拟内存技术如何优化资源分配、提高多任务处理能力及对硬件资源的抽象管理,揭示其在现代操作系统中的重要性和应用价值。
|
8天前
|
算法 Java 开发者
深入理解Python的内存管理机制
Python 以其简单易学的语法和强大的功能深受开发者欢迎。然而,许多开发者在使用 Python 时并不了解其背后的内存管理机制。本文旨在深入探讨 Python 的内存管理,包括对象的生命周期、引用计数以及垃圾回收机制,从而帮助开发者编写出更加高效和稳定的代码。
|
9天前
|
Java Go
Go 中 slice 的内存管理机制
Go 中 slice 的内存管理机制
|
10天前
|
存储 算法
深入理解操作系统的内存管理机制
【6月更文挑战第19天】本文旨在探讨现代操作系统中内存管理的关键技术和策略,分析其对系统性能及稳定性的影响。通过介绍分页、分段、虚拟内存等概念,揭示操作系统如何有效管理物理与虚拟内存资源,以及这些技术在多任务处理和内存保护方面的应用。文章将重点讨论内存泄漏、碎片整理和页面置换算法等高级主题,以期为读者提供对内存管理复杂性及其解决方案的深刻理解。
13 2
|
11天前
|
算法 安全 调度
深入理解操作系统的虚拟内存管理机制
【6月更文挑战第18天】在现代计算机系统中,虚拟内存是实现多任务处理和内存保护的关键技术。本文将深入探讨操作系统如何通过虚拟内存管理机制来优化物理资源的使用,提高系统效率,并确保进程间的隔离与安全。我们将从虚拟内存的基本概念出发,逐步解析分页、分段、内存交换以及页面替换算法等核心组件,揭示它们如何协同工作以支撑起整个系统的内存需求。
|
12天前
|
算法 Java
Java垃圾回收(Garbage Collection,GC)是Java虚拟机(JVM)的一种自动内存管理机制,用于在运行时自动回收不再使用的对象所占的内存空间
【6月更文挑战第18天】Java的GC自动回收内存,包括标记清除(产生碎片)、复制(效率低)、标记整理(兼顾连续性与效率)和分代收集(区分新生代和老年代,用不同算法优化)等策略。现代JVM通常采用分代收集,以平衡性能和内存利用率。
36 3
|
15天前
|
监控 算法 安全
深入理解操作系统的内存管理机制
在数字时代的心脏,内存管理扮演着至关重要的角色。它是操作系统中的一项核心功能,负责协调、监控和控制计算机系统中的内存资源分配与回收。本文将深入探讨内存管理的基本原理、关键算法以及它在现代操作系统中的实现方式,揭示如何有效地利用和管理内存资源以优化系统性能和稳定性。
|
4天前
|
存储 缓存 算法
深入理解操作系统之内存管理机制
【6月更文挑战第25天】内存是计算机系统中至关重要的资源,其管理效率直接关系到系统性能。本文旨在探讨操作系统中内存管理的基本原理、关键技术以及面临的挑战,通过分析不同的内存管理策略,揭示内存分配与回收的复杂性,并讨论虚拟内存技术的实现细节及其在现代操作系统中的应用。文章将着重于内存管理对系统稳定性的影响,并提出未来内存技术的发展方向。