android 简单封装handler,防止内存泄露

探讨Android开发中因不当使用Handler及匿名内部类导致的内存泄露问题。深入解析内存泄露原因,提供解决方案,如适时回收Handler或使用静态内部类结合弱引用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于内存泄露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的安全,而我在合适的时机也会关闭没必要的线程。两者并没多大关系

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值