目录:
1.内存泄漏定义
2.Handler造成内存泄漏的原因
3.优化方案
1.内存泄漏定义
首先我们需要了解Java中的常见内存分配,包括静态存储区(方法区)、栈和堆等。
静态存储区:存储的是静态方法和全局的static数据和常量等,该区域的内存在程序编译时已经分配完成,在程序运行的整个过程都存在。
栈区:在执行方法时,方法体内的局部变量(包括基础数据类型和对象的引用等)都在栈上创建,在方法执行结束后,该区域局部变量所持有的内存会自动释放。栈内存分配运算内置于处理器的指令集中,效率高,但该区域的容量有限。
堆区:又称动态分配区,通常是存储new出来的对象的实例,该部分内存在没有引用时会由GC回收。
因此,我们通常说的内存泄漏是指:在堆区不断的创建对象,在该对象已经使用结束,不会再使用该对象时,但是还存在别的对象(生命周期较长)引用,导致该对象无法及时被GC回收,导致堆区可使用的内存越来越少,导致内存泄漏的产生,最终的后果就是OOM。其实Android中的内存泄漏的原因与Java中类似:生命周期较长的对象持有生命周期较短的对象的引用。
2.Handler造成内存泄漏的原因
在Android中的跨线程交互时,尤其是子线程与UI线程交互时,通过Handler在子线程中发送现象,Handler中做更新UI的操作,如下所示:
private Hand = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case UPDATE_UI:
//更新UI操作
break;
}
}
};
那么,通过Handler为何可以在子线程发送消息,在handleMessage中可以执行更新UI的操作?我们知道通常只有在主线程(通常指UI线程)可以执行更新UI的操作,因此最终还是调用UI线程更新。调用流程分析如下:
2.1 创建Handler对象
查看源码可以看到如下解释,当我们创建Handler对象时,就与该线程和该线程的消息队列相绑定,如果未与当前线程和线程队列绑定就无法正常执行事件的分发处理。我们在主线程创建Handler,因此就与主线程相绑定,Handler对象隐式的持有外部对象的引用,该外部对象通常是指Activity。
When you create a new Handler, it is bound to the thread /
* message queue of the thread that is creating it
2.2消息队列处理
子线程执行处理结束后,通过Handler将消息发送处理,但如果此时当前界面已经销毁(Activity销毁),正常情况下,如果Activity不在使用,就可能被GC回收,但由于子线程仍未处理结束,仍持有Handler的引用(否则无法正常发送Handler消息),而该Handler持有外部Activty的引用,导致该Activity无法正常回收,导致内存的泄漏。
该引用的链如下:
MessageQueue -> Message -> Handler -> Activity
3.优化方案
- Activity销毁时及时清理消息队列;
- 自定义静态Handler类+软引用。
3.1 Activity销毁时及时清理消息队列
在Activity销毁时,调用removeCallbacksAndMessages清除Message和Runnable。
if(mHandler!=null){
mHandler.removeCallbacksAndMessages(null);
mHandler=null;
}
/* * Remove any pending posts of callbacks and sent messages whose
* <var>obj</var> is <var>token</var>. If <var>token</var> is null,
* all callbacks and messages will be removed.
*/
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
3.2 自定义静态Handler类+弱引用
static class MyHandler extends Handler {
WeakReference<Activity> mWeakReference;
private MyHandler(WeakReference<Activity> mWeakReference){
this.mWeakReference = mWeakReference;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(mWeakReference!=null){
Activity activity = mWeakReference.get();
if(activity!=null){
//handler消息处理
}
}
}
}
由3.1的分析中可以看出,Handler造成内存泄漏的主要原因是持有当前Activty的强引用,造成Activity无法及时被回收,我们知道GC处理弱引用的机制为当对象销毁时,即使有弱引用存在,也会将其回收。
4.总结
避免使用Handler造成内存泄漏的方法如下:
- 在Activity的onDestory()方法中及时清理handler的消息队列;
- 自定义静态Handelr类,避免非静态内部类造成内存泄漏;
- 使用弱引用,使引用对象可以及时回收。
通过以上三种方式结合使用,可以有效的避免使用Handler不当,造成内存泄漏的情况。
欢迎关注晓涵说(CSDN)、xukang868(github)账号信息,查看更多文章。
网友评论