Android AsyncTask类使用详解和实现原理

一,认识 AsyncTask

AsyncTask能够在UI线程中准确且简易地使用。这个类允许我们执行后台操作并在UI线程上发布结果,而不必操作线程和/或Handler

AsyncTask被设计成一个围绕ThreadHandler的帮助类,它不构成一个通用的线程框架。AsyncTasks理想情况下应该用于短操作(最多几秒钟)。如果需要让线程长时间运行,谷歌强烈建议我们使用java.util.concurrent提供的各种api,如 ExecutorThreadPoolExecutorFutureTask

异步任务是由在后台线程上运行并在UI线程上发布其结果的计算定义的。异步任务由3个泛型类型ParamsProgressResult和4个步骤 onPreExecutedoInBackgroundonProgressUpdateonPostExecute 定义。

1,三个类型

AsyncTask的异步任务使用的三种泛型类型如下:

  • Params:执行时发送给任务的参数的类型。
  • Progress:在后台计算期间发布的进度单元的类型。
  • Result:后台计算结果的类型。

异步任务并不总是所有类型都被使用。要标记一个类型为空类型,需使用类型Void:
AsyncTask<Void, Void, Void>

2,四个步骤

在执行异步任务时,该任务将经历4个步骤:

  • onPreExecute:在执行任务之前在UI线程上调用。这个步骤通常用于设置任务,例如在用户界面中显示进度条。

  • doInBackground:在onPreExecute完成执行后立即在后台线程上调用。此步骤用于执行可能花费较长时间的后台计算。异步任务的参数被传递到这一步,计算结果必须由这一步返回,并将被传递回最后一步。在这个步骤中也可以使用publishProgress来发布一个或多个进度单元,这些值发布在UI线程的onProgressUpdate步骤中。

  • onProgressUpdate:调用publishProgress之后在UI线程中调用。执行的时间没有定义。此方法用于在后台计算仍在执行时在用户界面中显示任何形式的进度。例如,它可以用于动画进度条或在文本字段中显示日志。

  • onPostExecute:后台计算完成后在UI线程上调用。后台计算的结果作为参数传递给这一步。

3,取消任务

我们可以在任何时候通过调用cancel(boolean)来取消任务。调用此方法将导致后续对isCancelled()的调用返回true。在调用这个方法后调用onCancelled(Object),而不是onPostExecute(Object)将在doInBackground(Object[])返回后被调用。为了确保尽可能快地取消任务,应该经常定期从doInBackground(Object[])返回的值(例如在loop中)检查isCancelled()

4,使用的线程规则

这个类正常工作必须遵循几个线程规则:

  • AsyncTask类必须在UI线程上加载。从Android4.1开始是自动完成这个操作。
  • 任务实例必须在UI线程上创建
  • execute必须在UI线程上调用
  • onPreExecute()、onPostExecute}、doInBackground、onProgressUpdate不能手动调用
  • 该任务只能执行一次(如果尝试第二次执行,将抛出异常)。

5,内存可观测性

AsyncTask保证所有回调调用都是同步的,以确保在不显式同步的情况下完成以下操作。

  • onPreExecute的内存效果、以及在调用execute之前执行的任何其他操作、包括AsyncTask对象的构造,对doInBackground都是可见的。
  • doInBackground的内存效果对onPostExecute是可见的。
  • 调用publishProgress之前doInBackground的任何内存效果对相应的onProgressUpdate调用都是可见的。(但是doInBackground继续运行,需要注意的是,以后doInBackground中的更新不会干扰正在进行的onProgressUpdate调用。)
  • 在调用返回trueisCancelled之后、或者在调用onCancelled期间和之后,任何在调用cancel之前的内存效果都是可见的。

6,执行顺序

AsyncTasks的初始设计是在单个后台线程上串行执行的。从Android1.6开始。将其更改为线程池,允许多个任务并行操作。从Android3.0开始。任务在单个线程上执行,以避免并行执行导致的常见应用程序错误。

如果真的想要并行执行,可以调用带有THREAD_POOL_EXECUTOR对象的方法,即:executeOnExecutor(THREAD_POOL_EXECUTOR, Object[])

二,AsyncTask的实现

AsyncTask中提供了如下几个可供我们外部调用的方法:
在这里插入图片描述
AsyncTask中允许我们子类化实现的方法如下(doInBackground是抽象方法,必须实现):
在这里插入图片描述
我们知道AsyncTask默认是串行的,但也能实现并行;我们还知道onPreExecuteonProgressUpdate是运行于主线程的,publishProgress是运行于工作线程的;AsyncTask是如何做到这些的呢?

1, 类结构

/**
 * 按顺序一次执行一个任务的Executor。这种串行化对于特定进程是全局有效的。
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
/**
 * 可用于并行执行任务的Executor。
 */
public static final Executor THREAD_POOL_EXECUTOR;
// 默认在当前进程中的所有AsyncTask任务都是串行的
@UnsupportedAppUsage
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
// AsyncTask内部创建的Handler子类,绑定至UI线程,一旦创建,存活于整个进程。
// 用以将onProgressUpdate和finish方法的执行发布至UI线程
private static InternalHandler sHandler;
// WorkerRunnable是一个抽象类,CallBack的实现类,持有Params[]。
// mWorker作为AsyncTask任务的运行工作区,doInBackground将在其call()方法里执行
@UnsupportedAppUsage
private final WorkerRunnable<Params, Result> mWorker;
// 用以在适时的时候将Result通过sHandler通知到UI线程
// FutureTask是Future和Runnable的子接口,一个可取消的异步任务。
// mFuture中持有mWorker对象,当任务被执行时会运行mFuture.run(),并在这个方法中调用mWorker.call()
@UnsupportedAppUsage
private final FutureTask<Result> mFuture;
// 用于标记当前任务是否已经调用过cancel或执行过程中发生异常
private final AtomicBoolean mCancelled = new AtomicBoolean();
// 用于标记当前任务是否已经调用过postResult将消息发至主线程
@UnsupportedAppUsage
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
// 关联与AsyncTask创建所属的线程(对于我们而言只能是UI线程)的Handler,
// 当是UI线程时,mHandler实际上与sHandler指向同一个地址,区别在于:
// sHandler的作用域是整个进程,而mHandler只存在于当前AsyncTask的实例。
// 对于我们而言mHandler == sHandler
private final Handler mHandler;

2, 类初始化

AsyncTask内共有三个构造方法,但是作为开放api可供我们使用的只有一个无参构造方法,其他方法被标注为@hide,只有SDK自己的代码可以调用。无参构造方法中调用AsyncTask(@Nullable Looper callbackLooper),并传入一个值为nullLooper

直接看AsyncTask(@Nullable Looper callbackLooper)的代码实现:

/**
 * 创建一个新的异步任务。这个构造函数必须在UI线程上调用。
 * @hide
 */
public AsyncTask(@Nullable Looper callbackLooper) {
	// callbackLooper为空会获取主线程(UI线程)的Looper,所以AsyncTask的构造必须位于UI线程。
	// getMainHandler()获取到的是sHandler,此时,mHandler == sHandler
	mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
		? getMainHandler()
		: new Handler(callbackLooper);

	mWorker = new WorkerRunnable<Params, Result>() {
		public Result call() throws Exception {
			// 工作线程,当调用Executor#execute时,代码会执行到这里。
			// mTaskInvoked被标记为已经调用过execute
			mTaskInvoked.set(true);
			Result result = null;
			try {
				Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
				// 从这里可以知道doInBackground是运行于工作线程(子线程)的
				result = doInBackground(mParams);
				Binder.flushPendingCommands();
			} catch (Throwable tr) {
				// 发生异常时,任务会被标记为取消
				mCancelled.set(true);
				throw tr;
			} finally {
				// doInBackground执行完毕后发送结果, 如果发生异常仍然会发送,只不过此时result==null
				postResult(result);
			}
			return result;
		}
	};
	// FutureTask是Future和Runnable的子接口, mFuture中持有mWorker对象,
	// 当任务被执行时会执行mFuture.run()方法,并在这个方法中调用mWorker.call()
	// 在此处通过调用mFuture.get()方法将会得到mWorker.call()返回的结果
	mFuture = new FutureTask<Result>(mWorker) {
		// 在三种情况下会执行FutureTask.done():
		// 1,任务被取消;2,任务正常执行完毕;3,任务处理过程中出现异常。
		@Override
		protected void done() {
			try {
				// postResultIfNotInvoked方法的作用是:判断是否已经执行过postResult,如果没有就执。
				// 完整流程下postResult到这里是已经被调用过的(第30行)。
				postResultIfNotInvoked(get());
			} catch (InterruptedException e) {
				android.util.Log.w(LOG_TAG, e);
			} catch (ExecutionException e) {
				throw new RuntimeException("An error occurred while executing doInBackground()",
						e.getCause());
			} catch (CancellationException e) {
				postResultIfNotInvoked(null);
			}
		}
	};
}

3, 任务从提交到完成

通常使用的时候,我们都是调用AsyncTask#execute(),在execute()中是调用executeOnExecutor(Executor exec, Params... params)并传入默认的ExecutorsDefaultExecutorSERIAL_EXECUTOR)的。

想要并行执行,可以使用:executeOnExecutor(THREAD_POOL_EXECUTOR, Params[])

3.1,提交

/**
 * 使用指定的参数执行任务。任务返回自身(this),以便我们可以保留对它的引用。
 * 
 * 这个方法通常与THREAD_POOL_EXECUTOR一起使用,以允许在AsyncTask管理的线程池中并行运行多个任务,
 * 但是我们也可以使用自己的Executor来定制行为。(串行任务调用`AsyncTask#execute()`)
 * 
 * 这个方法必须在UI线程上调用。
 */
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
		Params... params) {
	if (mStatus != Status.PENDING) {
		switch (mStatus) {
			case RUNNING:
				throw new IllegalStateException("Cannot execute task:"
						+ " the task is already running.");
			case FINISHED:
				throw new IllegalStateException("Cannot execute task:"
						+ " the task has already been executed "
						+ "(a task can be executed only once)");
		}
	}

	mStatus = Status.RUNNING;

	onPreExecute();

	mWorker.mParams = params;
	exec.execute(mFuture);

	return this;
}

我们知道串行任务方法AsyncTask#execute()也是调用了上面的方法并传入SERIAL_EXECUTOR,那我们来看一下SERIAL_EXECUTOR的类型:

private static class SerialExecutor implements Executor {
	// Deque接口的可调整大小数组实现。阵列容量无限制; 它们会根据需要增长以支持使用。
	// 它们不是线程安全的; 在没有外部同步的情况下,它们不支持多线程并发访问。
	// AsyncTask的串行特点就表现在这。这里不做多余解释,知道是干什么的就行了。
	final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
	Runnable mActive;

	public synchronized void execute(final Runnable r) {
		mTasks.offer(new Runnable() {
			public void run() {
				try {
					r.run();
				} finally {
					scheduleNext();
				}
			}
		});
		if (mActive == null) {
			scheduleNext();
		}
	}

	protected synchronized void scheduleNext() {
		if ((mActive = mTasks.poll()) != null) {
			// 并行任务的执行器
			THREAD_POOL_EXECUTOR.execute(mActive);
		}
	}
}

可以看到scheduleNext()方法中使用的是并行任务的执行器,也就是说,不论是串行还是并行,最后的execute(mFuture)都会走向ThreadPoolExecutorTHREAD_POOL_EXECUTOR的类型)。

有兴趣了解这个类的朋友可以去看看: ThreadPoolExecutor。因为我们这篇文章主要是将AsyncTask原理的,所以对ThreadPoolExecutor只说过程,不讲源码。

3.2,入栈

ThreadPoolExecutor#execute(Runnable):在将来的某个时候执行给定的任务。

public void execute(Runnable command) {
	>>>
	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);
}

ThreadPoolExecutor#addWorker(Runnable , false):将任务加入工作队列。

3.3,轮询

ThreadPoolExecutor#runWorker(Worker):主工作程序运行循环,从队列中反复获取任务并运行它们。

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();
			>>>
			try {
				beforeExecute(wt, task);
				Throwable thrown = null;
				try {
					task.run();
				} catch >>>
				} finally {
					afterExecute(task, thrown);
				}
			} finally {
				task = null;
				w.completedTasks++;
				w.unlock();
			}
		}
		completedAbruptly = false;
	} finally {
		processWorkerExit(w, completedAbruptly);
	}
}

第15行的task.run()即是前文构造方法中提到的 FutureTask#run(),跟着源码我们接着再看这个方法都做了些什么。

3.4,处理

FutureTask#run()

public void run() {
	>>>
	try {
		Callable<V> c = callable;
		if (c != null && state == NEW) {
			V result;
			boolean ran;
			try {
				result = c.call();
				ran = true;
			} catch (Throwable ex) {
				result = null;
				ran = false;
				setException(ex);
			}
			if (ran)
				set(result);
		}
	} finally {
		>>>
	}
}

第9行的WorkerRunnable#call()的返回值会通过第17行的 FutureTask#set(result) 保存在mFuture中。

FutureTask#set(result)

protected void set(V v) {
	if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
		outcome = v;
		U.putOrderedInt(this, STATE, NORMAL); // final state
		finishCompletion();
	}
}
private void finishCompletion() {
	// assert state > COMPLETING;
	for (WaitNode q; (q = waiters) != null;) {
		if (U.compareAndSwapObject(this, WAITERS, q, null)) {
			for (;;) {
				Thread t = q.thread;
				if (t != null) {
					q.thread = null;
					LockSupport.unpark(t);
				}
				WaitNode next = q.next;
				if (next == null)
					break;
				q.next = null; // unlink to help gc
				q = next;
			}
			break;
		}
	}

	done();

	callable = null;        // to reduce footprint
}

任务执行完毕之后,会调用 FutureTask#done() ,通过调用查找可以知道done()方法只会在如下三种情况下被执行:

  1. 任务被取消;
  2. 任务正常执行完毕;
  3. 任务处理过程中出现异常。
    在这里插入图片描述

至此,章节 【2.,类初始化】WorkerRunnableFutureTask的注释描述就与实际代码串联上了。

3.5,结束

从章节 【2.,类初始化】 可以看到,WorkerRunnable#call()FutureTask#done()方法中的最后都调用了postResult(Result),这个方法的作用是通过Handler将执行结果发送到Handler所属的线程中。

private Result postResult(Result result) {
	@SuppressWarnings("unchecked")
	Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
			new AsyncTaskResult<Result>(this, result));
	message.sendToTarget();
	return result;
}

getHandler()返回mHandler,前面我们说过在我们的使用中mHandler==sHandler即主线程(UI线程),看一下他的实现:

private static class InternalHandler extends Handler {
	public InternalHandler(Looper looper) {
		super(looper);
	}

	@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
	@Override
	public void handleMessage(Message msg) {
		AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
		switch (msg.what) {
			case MESSAGE_POST_RESULT:
				// There is only one result
				result.mTask.finish(result.mData[0]);
				break;
			case MESSAGE_POST_PROGRESS:
				result.mTask.onProgressUpdate(result.mData);
				break;
		}
	}
}

mTask.finish(result)

private void finish(Result result) {
	if (isCancelled()) {
		onCancelled(result);
	} else {
		onPostExecute(result);
	}
	mStatus = Status.FINISHED;
}

mHandler是运行于主线程的,从这里也就明白了onCancelled、onPostExecute、onProgressUpdate是如何在异步的doInBackground执行完之后又被运行到主线程中去的。

PS:如果有对Handler的原理不是很了解的可以看这里:《Android 从源码了解Handler(Looper 、Message、MessageQueue)的实现原理》

4, 任务取消

在任务从提交到结束的过程中,我们是可以随时通过调用 cancel(boolean mayInterruptIfRunning)来取消任务的。

public final boolean cancel(boolean mayInterruptIfRunning) {
	mCancelled.set(true);
	return mFuture.cancel(mayInterruptIfRunning);
}

FutureTask.cancel(boolean)

public boolean cancel(boolean mayInterruptIfRunning) {
	// 如果已经处于中断或者取消,直接return
	if (!(state == NEW &&
		  U.compareAndSwapInt(this, STATE, NEW,
			  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
		return false;
	try {    // 如果调用中断抛出异常
		if (mayInterruptIfRunning) {
			// 如果需要中断正在执行的任务,直接使执行任务的工作线程interrupt
			try {
				Thread t = runner;
				if (t != null)
					t.interrupt();
			} finally { // final state
				U.putOrderedInt(this, STATE, INTERRUPTED);
			}
		}
	} finally {
		finishCompletion();
	}
	return true;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值