AsyncTask的cancel方法并没有停止任务

今天使用LeakCanary,使用AsyncTask模拟一个内存泄漏的情况:

onCreate里面调用startAsyncTask():

/**
 * 模拟一个内存泄漏  AsyncTask任务没有结束,Activity退出
 */
void startAsyncTask() {
    // This async task is an anonymous class and therefore has a hidden reference to the outer
    // class MainActivity. If the activity gets destroyed before the task finishes (e.g. rotation),
    // the activity instance will leak.
     task = new AsyncTask, Void, Void>() {
        @Override protected Void doInBackground(Void... params) {
            // Do some slow work in background

            if(isCancelled()){

            }
            SystemClock.sleep(20000);
            return null;
        }
    }.execute();
}
果不其然,内存泄漏的错误报出来了,如下:

 * com.tddapp.main.module.other.SafetySettingActivity has leaked:
                                                             * GC ROOT thread java.lang.Thread. (named 'AsyncTask #2')
                                                             * references com.tddapp.main.module.other.SafetySettingActivity$2.this$0 (anonymous class extends android.os.AsyncTask)
                                                             * leaks com.tddapp.main.module.other.SafetySettingActivity instance
                                                             
                                                             * Reference Key: 2c00146b-4a55-4a87-a8f2-82bc0d7dea54
                                                             * Device: nubia nubia NX513J htc_pico
                                                             * Android Version: 4.4.2 API: 19
                                                             * Durations: watch=5019ms, gc=126ms, heap dump=281ms, analysis=6046ms

既然错误出来了,我们就得解决问题呀。这个根本问题就是内部类AsyncTask依赖Activity的context,当Activity退出时AsyncTask任务还没有结束,方法一就是让AsyncTask结束任务。我记得AsyncTask有个Cancel方法。于是就在onDestroy里面添加了如下操作:

  @Override
    protected void onDestroy() {
        super.onDestroy();
        task.cancel(true);
    }
}

添加完了后运行发现还是报内存泄漏的问题,这是为什么呢?  下面是我百度来的答案:

AsyncTask.cancel()的结束问题

实际项目中有这么一个问题,用户进入详情界面,那么我们就要网络加载数据并展现在UI上,这个加载用线程或者异步。

这里就拿项目中统一用异步任务来获取网络数据把。

用户可能会有这么一个操作,它在一个商品(说说等)列表中,点击一个列表项,进入到相应的详情界面,这时候,我们会开启一个异步任务来获取网络数据,但是网络差的情况下, 用户可能就不愿意等了,立马按后退按钮回到列表,点击下一个别的列表项进入详情界面,发现加载太慢,又按后退键,如此反复,那么就导致此时有多个异步任务在执行,或者出现OOM问题,或者出现异步任务等待问题。

那么,作为开发者,我们对应的解决方案,便是在用户在详情界面按退出按钮退回到上一个界面的时候,把没有执行完的异步任务给结束掉。

------------------------------------------------------------------------------------------------------------------------------

现在我们发现了这个问题,又有了解决方案。那么就用代码来实现了。

那么 取消异步任务怎么做?

我一开始这样做,AsyncTask.cancel(true);

看下参数的定义:

@param mayInterruptIfRunning true if the thread executing this
     *        task should be interrupted; otherwise, in-progress tasks are allowed
     *        to complete.

1、如果是true,如果线程执行,则会被打断

2、如果是false,线程将会被运行执行完成

 

看到这,很显然,我们以为.cancel(true)就会结束掉我们开启的正在执行的异步任务

但是实际上并没有结束掉我们想要结束的异步任务~~

看了些别人对此的解释:

AsyncTask不会不考虑结果而直接结束一个线程。调用cancel()其实是给AsyncTask设置一个"canceled"状态。这取决于你去检查AsyncTask是否已经取消,之后决定是否终止你的操作。对于mayInterruptIfRunning——它所作的只是向运行中的线程发出interrupt()调用。在这种情况下,你的线程是不可中断的,也就不会终止该线程。

那么该如何结束线程呢?

可见.cancel()是给AsyncTask设置一个"canceled"的状态,那么想要终止异步任务,就需要在异步任务当中结束。

复制代码
@Override
public void onProgressUpdate(Integer... value) {
// 判断是否被取消
if(isCancelled()) return;
.........
}

@Override
protected Integer doInBackground(Void... mgs) {
// Task被取消了,马上退出
if(isCancelled()) return null;
.......
// Task被取消了,马上退出

if(isCancelled()) return null;
}
...
复制代码

 

另外结束异步任务的条件:

复制代码
if(loadAsyncVedio!=null && !loadAsyncVedio.isCancelled()
                    && loadAsyncVedio.getStatus() == AsyncTask.Status.RUNNING){
                loadAsyncVedio.cancel(true);
                loadAsyncVedio = null;
            }
loadAsyncVedio(异步任务)
复制代码

如此,便可以有效及时的结束异步任务

你可能感兴趣的:(android)