Android中Handler使用不当引起的内存泄露

2024-05-09 11:48

本文主要是介绍Android中Handler使用不当引起的内存泄露,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

通常我们在Android编程中,常常会用到它自己提供的一种异步回调机制Handler,通过它,我们可以在进行异步操作后处理返回结果,通常我们的代码是这么实现的:在主线程中,new一个Handler对象实现其handleMessage方法,在handleMessage中提供收到消息后的相应处理的方法即可,示例代码如下:


package com.tb.demo.utils.hangview;
 
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
 
/**
 * Created by tb
 */
public class DemoHandlerActivity extends Activity {
    private final int LOADDBDATA = 0x1;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getLocalData();
    }
 
    /**
     * 获取本地数据
     */
    private void getLocalData() {
        //start get data from db
        //do something
        //...
        //...
        //end get data from db
        handler.sendEmptyMessage(LOADDBDATA);
    }
 
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case LOADDBDATA:
                    // do something when msg.what=0x01
                    // ...
                    // ...
                    break;
                default:
                    break;
            }
        }
    };
}
然而写完这个Handler之后Android Studio报了一个警告,如下图:

解释如下:

1.当这个App启动的时候,会自动创建一个供应用主线程使用的Looper实例。Looper负责管理MessageQueue和Message对象,读取到MessageQueue中的Message之后就会采用sendMessage的方式把消息发送给对应的Handler来处理,Looper接收到一条一条的消息后逐一进行处理,所以主线程中的Looper和App的生命周期一样长。

2.当一个Handler在主线程进行了初始化之后,我们发送一个target为这个Handler的消息到Looper处理的消息队列时,实际上已经发送的消息包含了一个Handler实例的引用,只有这样Looper在处理到这条消息时才可以调用 Handler#handleMessage(Message)完成消息的正确处理。

3.在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用。

解决思路:

不使用非静态内部类,继承Handler时,或者放在单独的类文件中,或者使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理。注意:一个静态的匿名内部类实例不会持有外部类的引用。

调整代码如下:

package com.tb.demo.utils.hangview;
 
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
 
import java.lang.ref.WeakReference;
 
/**
 * Created by tangbin on 15/9/5.
 */
public class DemoHandlerActivity extends Activity {
 
    private MyHandler myHandler;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        setContentView(R.layout.activity_main);
        myHandler = new MyHandler(this);
        getLocalData();
 
    }
 
    /**
     * 获取本地数据
     */
    private void getLocalData() {
        // start get data from db
        // do something
        // ...
        // ...
        // end get data from db
        myHandler.sendEmptyMessage(myHandler.LOADDBDATA);
    }
 
    public void logOut() {
        System.out.println("data is ok");
    }
 
    /**
     * Instances of static inner classes do not hold an implicit reference to
     * their outer class.
     */
    static class MyHandler extends Handler {
        public static final int LOADDBDATA = 0x1;
        WeakReference<DemoHandlerActivity> activity;
 
        MyHandler(DemoHandlerActivity mActivity) {
            activity = new WeakReference<DemoHandlerActivity>(mActivity);
        }
 
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            DemoHandlerActivity ac = activity.get();
            switch (msg.what) {
            case LOADDBDATA:
                // do something when msg.what=0x01
                // ...
                // ...
                ac.logOut();
                break;
            default:
                break;
            }
        }
    }
}

好了,终于不报警告了。在Android中很多的内存泄露都是由于在Activity中使用了非静态内部类导致的,所以当我们使用时要非静态内部类时要格外注意,如果其实例的持有对象的生命周期大于其外部类对象,那么就有可能导致内存泄露。

这篇关于Android中Handler使用不当引起的内存泄露的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/973332

相关文章

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

最新Spring Security的基于内存用户认证方式

《最新SpringSecurity的基于内存用户认证方式》本文讲解SpringSecurity内存认证配置,适用于开发、测试等场景,通过代码创建用户及权限管理,支持密码加密,虽简单但不持久化,生产环... 目录1. 前言2. 因何选择内存认证?3. 基础配置实战❶ 创建Spring Security配置文件

java内存泄漏排查过程及解决

《java内存泄漏排查过程及解决》公司某服务内存持续增长,疑似内存泄漏,未触发OOM,排查方法包括检查JVM配置、分析GC执行状态、导出堆内存快照并用IDEAProfiler工具定位大对象及代码... 目录内存泄漏内存问题排查1.查看JVM内存配置2.分析gc是否正常执行3.导出 dump 各种工具分析4.

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

Android DataBinding 与 MVVM使用详解

《AndroidDataBinding与MVVM使用详解》本文介绍AndroidDataBinding库,其通过绑定UI组件与数据源实现自动更新,支持双向绑定和逻辑运算,减少模板代码,结合MV... 目录一、DataBinding 核心概念二、配置与基础使用1. 启用 DataBinding 2. 基础布局

Android ViewBinding使用流程

《AndroidViewBinding使用流程》AndroidViewBinding是Jetpack组件,替代findViewById,提供类型安全、空安全和编译时检查,代码简洁且性能优化,相比Da... 目录一、核心概念二、ViewBinding优点三、使用流程1. 启用 ViewBinding (模块级