坑爹的AsyncTask之内存泄露

作者: 尹star | 来源:发表于2015-11-21 01:12 被阅读3493次

    当AsyncTask被引入到Android中时,它被贴上“无忧线程”的标签。其目的是让与UI线程交互的子线程变得更容易。AsyncTask其本质是一个由5个核心线程组成的,最大队列数为128的线程池。我们在使用的过程中,通常会重写doInBackground(Params…) 方法,比较耗时的操作都可以放在这里。这个方法在子线程,是不能直接操作UI的。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用onPostExecute(Result)方法,相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回。

    AsyncTask的用法很简单,那么我们看下面这段代码,这样写正确吗?

    private TextView mTextview;
    
    new AsyncTask<...> {
    
    @Override
    
    protected void onPostExecute(Objecto) {
    
    mTextview.setText("text");
    
    }
    
    }.execute();
    

    乍一看好像没什么问题,但这段代码会导致内存泄露,线程有可能会超出当前Activity的生命周期之后仍然在run,因为这个时候线程已经不受控制了。Activity生命周期已经结束,需要被系统回收掉,但是AsyncTask还在持有TextView的引用,这样就导致了内存泄露。

    那我们把上面的代码改一改

    private TextView mTextview;
    
    new AsyncTask<...> {
    
    @Override
    
    protected void onPostExecute(Objecto) {
    
    //mTextview.setText("text");
    
    }
    
    }.execute();
    

    算了,懒得改 ,我直接注释掉,不做UI操作了,这样总不会有问题了吧。真的吗?
    仔细看,这里是个内部类,由于Java内部类的特点,AsyncTask内部类会持有外部类的隐式引用。即使从代码上看我在AsyncTask里没有持有外部的任何引用,但是写在Activity里,对context仍然会有个强引用,这样如果线程超过Activity生命周期,Activity还是无法回收造成内存泄露。

    那问题怎么解决呢,有两种办法:第一,在Activity生命周期结束前,去cancel AsyncTask,因为Activity都要销毁了,这个时候再跑线程,绘UI显然已经没什么意义了。第二,如果一定要写成内部类的形式,对context采用WeakRefrence,在使用之前判断是否为空。

    如有刊误,欢迎指正。

    相关文章

      网友评论

      • I喵先生:这样会泄露?
      • 82f7d163c7c2:这篇文章写的最差,没有之一,自己用MAT分析下再说
        钟离四郎: @岁月不爱流年 留德呀,大佬,
      • 明镜本清净anany:我想问一下,这个内存泄露,照坑爹这样说,handler也很坑爹啊!
        06b713d199c1:@小柯_ 明明是用错了,不能乖AsyncTask
        小柯_:@兰公子 handler也很坑爹的,也很容易导致内存泄漏

      本文标题:坑爹的AsyncTask之内存泄露

      本文链接:https://www.haomeiwen.com/subject/phnlhttx.html