Android 常见内存泄漏总结、避免踩坑、提供解决方案。(1)

本文详细探讨了静态实例持有非静态内部类可能导致的内存泄漏问题,涉及匿名内部类、耗时任务、Handler的使用以及Context管理。提供了替换策略和最佳实践,以及如何通过学习资源提升Android开发技能。
摘要由CSDN通过智能技术生成
    • 静态实例持有非静态内部类
    • 描述🤦‍♀️
  • 解决办法🙆‍♀️

  • 耗时任务相关的匿名内部类/非静态内部类

    • 描述🤦‍♀️
  • 解决方案🙆‍♀️

  • Handle内存泄漏

    • 描述🤦‍♀️
  • 解决方案🙆‍♀️

  • Context被长期持有

    • 描述🤦‍♀️
  • 解决方案🙆‍♀️

  • View被静态修饰

    • 描述🤦‍♀️
  • 解决方案🙆‍♀️

  • 大对象/监听器释放

    • 描述🤦‍♀️
  • 解决方案🙆‍♀️

  • 资源对象注意关闭

    • 描述🤦‍♀️
  • 解决方案🙆‍♀️

  • 集合对象

    • 描述🤦‍♀️
  • 解决方案🙆‍♀️

对常见的内存泄漏进行一波总结,希望可以帮到大家。

静态实例持有非静态内部类


描述🤦‍♀️

非静态内部类会持有外部类的实例,所以如果非静态内部类的实例是静态的话,那么它的生命周期就是整个APP的生命周期,而它则会一直持有外部类的引用,阻止外部类实例被系统回收。

举个例子🌰:

public class TestActivity extends AppCompatActivity {

static InnerClass innerClass;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.item_layout);

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

innerClass = new InnerClass();

}

});

}

class InnerClass{}

}

此时点击button时,会创建InnerClass实例并且赋值给innerClass。因为innerClassstatic修饰,所以InnerClass实例的生命周期会和应用程序一样长,但是它会持有TestActivity的实例,所以就会导致如果系统需要回收不了TestActivity的实例。造成内存泄漏😮。

解决办法🙆‍♀️

  • 将非静态内部类替换成静态内部类,因为静态内部类不会持有外部类的引用

  • 一定要用非静态内部类的话,要保证内部类的生命周期短于外部类

耗时任务相关的匿名内部类/非静态内部类


描述🤦‍♀️

这个和上一个类似,非静态内部类持有外部类的实例大家都知道了,这里不在叙述了;匿名内部类也会持有外部类的实例,而且匿名内部类会结合线程使用得多,这里就拉出来讲一下。

同理因为匿名内部类会持有外部类的实例,比如线程的Runable如果在里面做了耗时任务,在外部类对象需要回收的时候,但是线程任务没有执行完,那么就会因为匿名内部类持有外部类的引用,进而阻止系统回收外部类对象了。

简单举个例子🌰

public class TestActivity extends AppCompatActivity {

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.item_layout);

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.MICROSECONDS, new SynchronousQueue(), new ThreadFactory() {

@Override

public Thread newThread(Runnable r) {

return new Thread(new ThreadGroup(“test-thread”),r);

}

});

threadPoolExecutor.submit(new Runnable() {

@Override

public void run() {

while (true){

Log.d(“TAG”, “run: test”);

}

}

});

}

});

}

}

点击button执行线程任务,提交了一个runable进去,因为里面死循环永远不会结束。所以匿名内部类会一直持有TestActivitty对象。不会被系统回收掉😮。因为匿名内部类这玩意进场使用,所以还是需要注意的!!!🤦‍♀️

解决方案🙆‍♀️

  • 将匿名内部类/非静态内部类替换成静态内部类,因为静态内部类不会持有外部类的引用

  • 一定要用匿名内部类/非静态内部类的话,要保证内部类的生命周期短于外部类

Handle内存泄漏


描述🤦‍♀️

这个就有点老生常谈了😂,但还是的说一下。

Handler发送的Message会存储在MessageQueue里面,但是他们不一定马上就被处理了。

另外我们知道Message的Target会持有记录当前的Handler对象,用于进行消息分发。所以如果Message不被及时处理,那么Handler就无法被回收。

那么如果此时Handler是非静态的,则Handler也会导致引用它的Activity不能被回收😮。

举个例子🌰

public class TestActivity extends AppCompatActivity {

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.item_layout);

Handler mHandler = new Handler(){

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

}

};

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

mHandler.sendMessageDelayed(new Message(),100000);

finish();

}

});

}

}

当点击button时,会finish当前activity。但是因为消息没有被及时处理,间接引用了Handler对象,Handler又是匿名内部类实例,持有了activity对象。所以导致内存泄漏🤦‍♀️。

解决方案🙆‍♀️

  • 使用静态Handler内部类,handler的持有者用弱引用。

  • 在onDestroy中将未执行的消息和Callbacks清除。

if (mHandler != null) {

mHandler.removeCallbacksAndMessages(null);

}

Context被长期持有


描述🤦‍♀️

这个也很简单,比如你把Activity的Context传给了一个长期存在的对象,那其实activity的context就是它自身,那么因为被持有就回收不了。造成内存泄漏

解决方案🙆‍♀️

  • 对于不是必须使用Activity的Context的情况(Dialog的Context必须使用Activity的Context),可以考虑使用Application来代替Activity的Context,因为一般使用Context无非时获取一些资源而已。
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总)

面试成功其实是必然的,因为我做足了充分的准备工作,包括刷题啊,看一些Android核心的知识点,看一些面试的博客吸取大家面试的一些经验,下面这份PDF是我翻阅了差不多1个月左右一些Android大博主的博客从他们那里取其精华去其糟泊所整理出来的一些Android的核心知识点, 全部都是精华中的精华,我能面试到现在资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。

这份PDF囊括了JVM,Java集合,Java多线程并发,Java基础,生命周期,微服务, 进程,Parcelable 接口,IPC,屏幕适配,线程异步,ART,架构,Jetpack,NDK开发,计算机网络基础,类加载器,Android 开源库源码分析,设计模式汇总,Gradle 知识点汇总…

由于篇幅有限,就不做过多的介绍,大家请自行脑补

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

试到现在资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。
[外链图片转存中…(img-b2N8JPAt-1713755683551)]

这份PDF囊括了JVM,Java集合,Java多线程并发,Java基础,生命周期,微服务, 进程,Parcelable 接口,IPC,屏幕适配,线程异步,ART,架构,Jetpack,NDK开发,计算机网络基础,类加载器,Android 开源库源码分析,设计模式汇总,Gradle 知识点汇总…

由于篇幅有限,就不做过多的介绍,大家请自行脑补

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值