AsyncTask 坑 AsyncTask对象生命周期
Posted zj510
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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对象才会被释放。
以上是关于AsyncTask 坑 AsyncTask对象生命周期的主要内容,如果未能解决你的问题,请参考以下文章