您的位置:首页 > 移动开发 > Android开发

Android中AsyncTask的使用和源码分析

2017-03-16 10:08 302 查看

前言: 在上一篇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()中处理这个消息,完成了线程池切换到主线程的操作.


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息