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

相关文章

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

Android App安装列表获取方法(实践方案)

《AndroidApp安装列表获取方法(实践方案)》文章介绍了Android11及以上版本获取应用列表的方案调整,包括权限配置、白名单配置和action配置三种方式,并提供了相应的Java和Kotl... 目录前言实现方案         方案概述一、 androidManifest 三种配置方式

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C