转自https://blog.csdn.net/geanwen/article/details/54233895,感谢作者的无私分享。
容易造成内存泄漏的一种Handler使用方法:将Handler声明为Activity的内部类。在Java语言中,非静态内部类会持有外部类的一个隐试引用,这样就可能造成外部类无法被垃圾回收。而导致内存泄漏。
private final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// ...
}
};
那么正确的使用就是:
1.将Handler声明为静态内部类。并持有外部类的若引用。
2.在子线程中使用Handler,这是需要我们自己创建一个Looper对象。
下面代码介绍一下第一种用法:
/**
* 正确使用Handler方式
*/
public class HandlerActivity extends AppCompatActivity {
private final MyHandler mHandler = new MyHandler(this);
/**
* 静态的匿名内部类不会持有外部类的引用
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() {
// ...你的操作
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
mHandler.post(sRunnable);
}
/**
* 声明一个静态的Handler内部类,并持有外部类的弱引用
*/
private static class MyHandler extends Handler{
private final WeakReference<HandlerActivity> mActivty;
private MyHandler(HandlerActivity mActivty) {
this.mActivty = new WeakReference<HandlerActivity>(mActivty);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
HandlerActivity activity = mActivty.get();
if (activity != null){
// ....
}
}
}
}
上面在Activity使用就需要声明一个内部类,下面再看一个,将自定义Handler抽出去,也同样达到效果的小栗子:
1.首先创建一个类,通过泛型将实例传入
public class UIHandler<T> extends Handler {
protected WeakReference<T> ref;
public UIHandler(T cls){
ref = new WeakReference<T>(cls);
}
public T getRef(){
return ref != null ? ref.get() : null;
}
}
2.看下activity中使用,直接用myHandler对象发送message即可。
private static class UIMyHandler extends UIHandler<HandlerActivity>{
public UIMyHandler(HandlerActivity cls) {
super(cls);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
HandlerActivity activity = ref.get();
if (activity != null){
if (activity.isFinishing())
return;
switch (msg.what){
case 1:{
break;
}
// ...
}
}
}
}
private UIMyHandler myHandler = new UIMyHandler(this);
最后在简单介绍一下,Java弱引用以及软引用还有虚引用。
个人认为,如果只是想避免OutOfMemory异常的发生,则可以使用软引用。若果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。
和弱引用功能类似的是WeakHashMap,他对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收,回收以后,其条目从映射中有效的一处。WeakHashMap使用ReferenceQueue实现的这种机制。
java.lang.ref包中提供了几个类:SoftReference类—>软引用;
WeakReference类—>弱引用;
PhantomReference—>虚引用;(相当于没有持有引用,形同虚设,随时可能被回收)
弱引用实践场景:自己定义Handler类采用弱引用方式,防止内存泄漏。
软引用场景:应用中肯定会有很多的默认的图片资源,会多次用到。如果每次都去获取,读取文件需要硬件操作,速度慢,导致性能降低。所以考虑将图片缓存起来,需要时直接从内存中获取,但是图片占用内存空间很大,容易OutOfMemory。这时考虑软引用。
Handler实现原理
使用Handler方式进行异步消息处理主要由Message,Handler,MessageQueue,Looper四部分组成:
(1)Message,线程之间传递的消息,用于不同线程之间的数据交互。Message中的what字段用来标记区分多个消息,arg1、arg2 字段用来传递int类型的数据,obj可以传递任意类型的字段。
(2)Handler,用于发送和处理消息。其中的sendMessage()用来发送消息,handleMessage()用于消息处理,进行相应的UI操作。
(3)MessageQueue,消息队列(先进先出),用于存放Handler发送的消息,一个线程只有一个消息队列。
(4)Looper,可以理解为消息队列的管理者,当发现MessageQueue中存在消息,Looper就会将消息传递到handleMessage()方法中,同样,一个线程只有一个Looper。
Handler实现原理如下图:
结合上文的的代码示例以及上图的实现流程,要使用Handler实现异步消息处理,首先我们需要在主线程中创建Handler对象并重写handleMessage()方法,然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handlerr将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。由于Halldler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,从而实现子线程通过Handler机制实现UI线程操作的目的。
4. Handler内存泄漏分析
4.1 Handler内存泄漏问题的引出:
上面的Handler实现代码中,其实在Android Studio中会提示以下问题:
大致意思就是应该让Handler类为静态的,不然就会产生内存泄漏。 原因也说的很清楚,Handler被声明为一个非静态内部类或者匿名类可能会阻止外部类的垃圾回收(大家可以了解下Android的gc回收机制)。过多的内存泄漏使程序占用的内存超出系统限制,导致OOM(内存溢出),程序出错。
4.2 防止Handler引起内存泄漏:
方法一:通过程序逻辑进行保护:
(1)在关闭Activity时停掉对应的后台线程。线程停止就相当于切断了Handle和外部链接的线,Activity自然会在合适的时候被回收。
(2)如果Handler是被delay的Message持有了引用,那就使用Handler的removeCallbacks()方法将消息对象从消息队列移除即可。
方法二:将Handler声明为静态类,静态类不持有外部类的对象,所以Activity可以被随意回收。此处使用了弱引用WeakReference,也就是说当在内存不足时,系统会销毁弱/回收引用引用的对象,从而达到优化内存的目的。