进阶 | AsyncTask 源码浅析

官方介绍

AsyncTask方便我们从异步工作切换到UI线程。虽然是异步的但是依然不建议让AsyncTask执行过大耗时操作(超过10秒),如果需要让一个线程长时间保持运行,请用线程池。AsyncTask有三个泛型四个方法需要注意。

简单介绍

三个泛型Params用于初始化,Progress用于设置进度,Result返回的结果,如果没有参数则设置为Void

四个方法:

  1. onPreExecute(),任务执行之前操作
  2. doInbackground(Params),后台线程执行
  3. onProgressUpdate(Progress),任务执行过程中,主线程
  4. onPostExecute(),任务执行完成
  5. 补充, onCancelled(),取消任务

简单使用:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
 
	@Override
	protected void onPreExecute() {
		progressDialog.show();
	}
 
	@Override
	protected Boolean doInBackground(Void... params) {
		try {
			while (true) {
				int downloadPercent = doDownload();
				publishProgress(downloadPercent);
				if (downloadPercent >= 100) {
					break;
				}
			}
		} catch (Exception e) {
			return false;
		}
		return true;
	}
 
	@Override
	protected void onProgressUpdate(Integer... values) {
		progressDialog.setMessage("当前下载进度:" + values[0] + "%");
	}
 
	@Override
	protected void onPostExecute(Boolean result) {
		progressDialog.dismiss();
		if (result) {
			Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
		} else {
			Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
		}
	}
}

源码

两个线程池:

  1. SerialExecutor :任务排队
  2. THREAD_POOL_EXECUTOR:执行任务

注意点:

  1. AsyncTask的类加载必须在主线程中完成,因为AsyncTask类中有静态域sHandler,用于切换主线程和子线程。sHnadler必须获取主线程的Looper,所以sHandler必须在主线程中创建。而静态变量的创建在类加载的时候完成,所以AsyncTask必须在主线程中加载。Android4.1之后系统自动完成这个操作。

  2. AsyncTask对象创建和execute方法必须在主线程中调用
    尝试在子线程中使用AsyncTask
    但是为啥呢?大都数帖子都说,因为sHandler必须在主线程中创建,但是sHandler为静态变量,在类加载的时候就创建了,而不是在对象生成的时候创建。这个解释是错误的。如果您知道为啥,还请指点一二。

  3. 一个AsyncTask只能被执行一次,不然会被报异常。查看源码,在执行 executor()方法之后,首先会检测AsyncTask的Status,如果 Status为RUNDING或者FINISHED则会抛出异常。在检测完Status之后将其设置为RUNNING

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;
}
  1. Android3.0 之后AsyncTask任务由一个线程串行执行,如果是调用executeOnExecute则是并行执行任务。查看源码
private static class SerialExecutor implements Executor {
	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);// 执行任务
		}
	}
}

初始状态mActive为null,调用scheduleNext(),出队列让THREAD_POOL_EXECUTOR线程池去执行任务。r.run()执行完成之后运行finaly{}语句,调用下一个任务,依次继续。

参考资料

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值