AsyncTask 坑 (二) AsyncTask对象生命周期

当我第一次看到AsyncTask的调用代码时候,我第一感就有个疑问。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
            @Override
            protected String doInBackground(Void... params) {
                Log.v("AsyncTask", "doInBackground");
                return "hello";
            }

            @Override
            protected void onPostExecute(String result)
            {
                Log.v("AsyncTask", result);
            }
        };

        task.execute();
    }

task对象的生命周期。这个例子里面task是在onCreate函数里面创建的,那么等onCreate函数跑完了,task对象的生命周期是不是结束了呢?

1. task变量本身是在调用线程栈上面创建出来的,如果32bit的话,那么就是4个字节的一个变量。这个变量在onCreate执行完后,肯定被弹出栈,所以task变量本身死亡了,这是肯定的。

2. task指向的AsyncTask对象,AsyncTask对象是在堆里面被创建的,那么当task变量本身死亡了,堆上的内存块是不是也被收回了呢?根据JAVA的定义,堆上面的内存如果没有一个引用指向它,那么将被当作GC的回收对象。

那么AsyncTask这个例子,到底堆上的内存会不会被收回呢?具体就看Asynctask的实现了。

本文使用的是Android-21


AsyncTask构造函数

先看看构造函数:

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

注意构造函数里面创建了两个内部类对象。根据JAVA内部类的规则,内部类对象是会有个隐式引用的。我们下断点看看。


看上图中的variables,mFuture和mWorker各有一个this$0, 他两的值是一样的,实际上就是AsyncTask对象的地址。

然后从代码里面还可以看到mWorker作为参数传给我了mFuture。

看一下mFuture的构造函数:

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

显然,mWorker被mFuture的一个数据成员被引用了。


AsyncTask的execute函数

execute函数中有一行:

exec.execute(mFuture);

exec是个AsyncTask的静态成员变量,ThreadPoolExecutor,看看它的execute:

   public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

基本上把command也就是mFuture送给了addWorker。那么addWorker呢?

ThreadPoolExecutor有个成员:

private final HashSet<Worker> workers = new HashSet<Worker>();

addWorker的代码就不贴出来了,基本上就是被存到了workers里面。


ThreadPoolExecutor的runWorker函数

ThreadPoolExecutor会把workers里面所有的work都在线程里面执行。具体的流程就不看了,主要看下面这个函数。

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

这个函数会执行一个worker,注意finally里面的processWorkerExit。当执行完任务后,processWorkerExit就会被调用。那么processWorkerExit做了什么事情呢?

   private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        tryTerminate();

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

基本上,就是把这个worker从HashSet里面删除了。


图解

画几个图来概括一下:

第一个图是内存布局:

现在,我们就可以分析一开始抛出的问题了,

a. 当调用了task.execute之后,内存布局就类似于上图。

b. 当onCreate调用结束后,栈里面的变量肯定不存在了。那么1号线就没有了。

c. ThreadPoolExecutor开始跑任务,当它跑完后,根据runWorker函数的流程,worker会被从HashSet<Worker>里面删除,那么5号线就没有了。

d. 一旦5号线没有了,mFuture就可以释放了, 那么3号线和4号线也就没有了

e. 然后mWorker就可以释放了,之后2号线也没有了。

f. 到现在为止,AsyncTask就可以被释放了。


结论: 如果任务没有执行完,AsyncTask对象就会被ThreadPoolExecutor的HashSet<Worker>所拥有,所以不会释放,只有当任务执行完了,AsyncTask对象才会被释放。










猜你喜欢

转载自blog.csdn.net/zj510/article/details/51498936