前言: 在上一篇Android中的线程和线程池的分析中我们可以看到,异步任务的线程池的一些配置.
连接地址:Android中的线程和线程池
一.在Android中AsyncTask的使用
首先: AsyncTask类是一个轻量级的异步任务类,他可以在线程池中执行后台任务;将执行的进度和任务传递到主线程中来更新UI.
缺点: 是一个轻量级的异步任务类,并不适合与特别耗时的后台操作.
AsyncTask使用的注意事项:
- AsyncTask类必须在主线程中加载,也就是第一次访问AsyncTask必须发生在主线程中,在Android4.1版本以上,主线程的main方法中调用了AsyncTask的init()方法,也就是替我们在主线程中加载了异步任务类.这一点不需要注意.
- 我们在主线程中创建异步任务的对象
- 异步任务的execute()方法必须在UI线程中调用
- 一个异步任务对象只能执行一次execute()方法,执行多次会报运行时错误.
- 不要直接调用异步任务中的方法,例如 onPreExecute()等方法
- 我们也可以使用 executeOnExecute()方法来并行的执行任务,默认异步任务类是串行执行任务的.
AsyncTask的几个参数:
由于AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:
1. Params
在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
2. Progress
后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
3. Result
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
AsyncTask的几个方法:
1. onPreExecute()
这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
2. doInBackground(Params...)
这 个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。
任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果 AsyncTask的第三个泛型参数指定的是Void
,就可以不返回任务执行结果。
注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比 如说反馈当前任务的执行进度,可以调用
publishProgress(Progress...)方法来完成。
3. onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在
后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4. onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法
中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
我们根据注意事项来写一个异步任务使用:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建异步任务对象
DownFileTask downFileTask = null;
downFileTask = new DownFileTask();
//启动异步任务
downFileTask.execute();
}
// 三个参数:
// 第一个参数是指: doInBackground执行时候的输入参数
// 第二个参数; 异步任务在执行的时候的进度的类型
//第三个参数: 是结束后返回给 onPostExectue()的结果
private class DownFileTask extends AsyncTask<URL, Integer, Long> {
//运行在主线程中,主要是异步任务之前的一些准备工作
@Override
protected void onPreExecute() {
super.onPreExecute();
}
// 主要是用来执行异步任务的,运行在子线程中
//三个省略号标识的是可变参数
//在线程池中调用
@Override
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
//totalSize += Downloader.downloadFile(urls[i]);
// 在doInBackgroun中会通过 publishProcess()来调用onProgressUpdate()方法
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled())
break;
}
return totalSize;
}
//运行在主线程中,用于展示异步任务的进度;比如下载的进度
@Override
protected void onProgressUpdate(Integer... values) {
//在主线程中代用,来更新进度
super.onProgressUpdate(values);
}
//在异步任务完成之后会被调用,主要是返回的结果
// 在主线程中调用
@Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
}
}
}
二 .根据执行过程进行源码分析:
首先是异步任务的execute()来启动的,我们开看看源码:
便于看的明白,明白几个参数的类型:
第一个用于任务排队的一个线程池
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
第二个是sDefaultExecutor指的就是这个线程池(串行线程池)
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
具体的开启的方法:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
跟进:
在看几个参数的类型:
private final FutureTask<Result> mFuture;首先FutureTask是一个并发类,最后追查到,这个类继承了Runable和Future两个接口,用来将传递进来的参数params封装为FutureTask对象,之后见mFuture交给SerialExector的exectue()处理.
@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;
}
观看上面的代码,我们看到了一个非常熟悉的方法,对就是onPreExecute(),他首先会被调用,所以我们可以在onPreExecute()完成异步任务的一些准备工作.
再看一下子串行线程池源码:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static InternalHandler sHandler;
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
private volatile Status mStatus = Status.PENDING;
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
private static class SerialExecutor implements Executor {
// 任务队列 mTask中会
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();
}
}
});
// 判断有没有正在活动的异步任务,没有会调用scheduleNext()
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
将FutureTask插入到任务队列中,mTask,接着判断有没有活动的异步任务,没有调用schueduleNext(),也就是说异步任务执行完毕后,才会继续执行其他任务,也就是为什么说异步任务是串行执行的.上面的线程池只是对异步任务进行排队,真正处理任务的线程池是 THREAD_POOL_EXECUTOR中执行的,我们来看看这个线程池的源码:
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
我们可以看出来,在上一篇中都有很详细的说明,核心线程数是cpu数量+1;非核心线程是cpu核心线程数的2倍+1;
核心线程没有超时策略,非核心线程有超时策略.
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
mWorker实际上是一个Callable对象,会调用他的call()方法;在望下面看.
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
private final WorkerRunnable<Params, Result> mWorker;我们突然看到了一个熟悉的方法,就是后台任务doInBackground()方法,并且获取了结果result,并将这个结果作为postResult()的参数传递,并且设置一个参数的为true就是mTaskInvoked,表示的就是当前的线程已经调用过了!
mTaskInvoked.set(true);
我们接着postResult(参数结果后台任务的)
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }看到了什么?对的就是一个getHandler(),并且使用它发送了一个消息,再看看这个消息就是message_post_result这个方法;
继续看看,这个Handler,异步任务是封装了两线程池和一个Handler,我们需要看看这个Handler;
private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } }看到一个InternalHnadler对象.
private static class InternalHandler extends Handler { public InternalHandler() { super(Looper.getMainLooper()); }这个就是我们所谓的Handler,发送了一个消息
private static class InternalHandler extends Handler { public InternalHandler() { super(Looper.getMainLooper()); } @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; } } }这个类是在异步任务中创建好的一个Handler,也就是说 handlerMessage()是在主线程中执行的,我们在细看这个Handler这个类,我们看到是一个static 修饰是一个静态的变量,sHandler是 static final修饰会在加载类的时候初始化.结果会发送到主线程中,再次做了一个判断; 如果是异步执行的进度等对ui的更新,会调用
onProgressUpdate()方法如果是message_post_result的话,会执行finish()方法
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }如果是取消(isCancelled)会执行onCancelled(),附则执行onPostExecute(),状态设置为完成状态.
注意点: mWorker的call()会调用异步任务的后台任务的方法,之后获取任务的返回值,作为参数传递给postResult(),在这个方法里面使用sHandler发送了一条消息,也就是发送到InternalHnadler的handlerMessage()中处理这个消息,完成了线程池切换到主线程的操作.