对于内存泄露android开发的小伙伴们一点都不陌生,这个老生常谈的问题,有时候会很让人头疼,其中匿名内部的不当使用会让你的应用不知不觉中中招,当使用内部类的时候包括匿名内部类创建handler的时候,handler会隐式地持有外部类的引用,通常是一个activity。通常而Handler会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler对应的activity呢?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。而activity是你应用中最耗费内存的一个大类。这玩具要是收不掉,那你的应用就会不知不觉的越用越卡。
基于此,我们在使用handler的时候一定要慎重,一般解决这个问题有俩种方式,要么我们在合适的时间回收掉handler,直接调用handler.removeCallbacksAndMessages(null);通常是在activity ondestroy的时候使用该方法,但是很多时候我们都会习惯的创建一个handler的内部类而忽略了回收。还有一种方法就是创建handler的时候使用静态的内部类结合弱引用,顺便普及一下Java引用的知识:
那我们可以简单的分装一下handler让他使用起来方便安全,
public abstract class PostTimer<T> extends Handler {
protected final WeakReference<Context> mReference;
protected final WeakReference<T> mReferenceT;
public PostTimer(Context context, T t){
mReference = new WeakReference<>(context);
mReferenceT = new WeakReference<>(t);
}
/**
* 执行体
* @param t
*/
public abstract void execute(T t);
private Runnable innerRun = new Runnable() {
@Override
public void run() {
Context context = mReference.get();
if(context != null) {
if(context instanceof Activity && ((Activity)context).isFinishing()){
release();
Logger.w("The timer executed, but "+context.getClass().getSimpleName()+" is finishing!");
}else {
T t = mReferenceT.get();
if(t != null) {
execute(t);
Logger.d("The timer executed in "+context.getClass().getName());
}else{
Logger.w("The timer executed, but callback class is null");
}
}
}else {
release();
Logger.w("The timer executed, but activity destoryed!");
}
}
};
/**
* 暂停任务
*/
public void stop() {
removeCallbacks(innerRun);
}
/**
* 销毁任务
*/
public void release(){
stop();
mReference.clear();
mReferenceT.clear();
}
/**
* 延迟执行
* @param time 延迟时间
*/
public void postDelayed(long time) {
stop();
postDelayed(innerRun, time);
}
}
WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了。
静态内部类不等于静态对象,这里是没有问题的。 关于你那同事,静态变量也只是程序运行的时候存在,也会随着程序结束(类卸载)被回收的。静态变量只存在一份,在单例模式中被大量使用,对于安卓这种短时间运行的程序来说也是没问题的。 如果是我,我会同时使用两种方案。其实是我在开发MyHandler的时候会保证MyHandler的安全,而我在合适的时机也会关闭没必要的线程。两者并没多大关系