AsyncTask源码解析

本文详细介绍了Android中的AsyncTask类,包括其基本概念、泛型参数、核心方法及使用注意事项。同时深入解析了AsyncTask的源码实现,探讨了其内部线程池及Handler的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、AsyncTask简介

AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类(轻量体现在使用方便、代码简洁),它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。

AsyncTask的内部封装了两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler)。

其中SerialExecutor线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,THREAD_POOL_EXECUTOR线程池才真正地执行任务,InternalHandler用于从工作线程切换到主线程。

1.AsyncTask的泛型参数

public abstract class AsyncTask<Params, Progress, Result>
复制代码
  • Params:开始异步任务执行时传入的参数类型;
  • Progress:异步任务执行的过程中,返回下载进度值的类型;
  • Result:异步任务执行结束后,返回的结果类型。

如果AsyncTask确定不需要传入具体的参数,那么可以用Void代替

2.AsyncTask的核心方法

  • onPreExecute(),该方法在任务开始执行之前调用,在主线程调用,用于初始化界面上的初始化操作,例如显示一个进度框;
  • doInBackground(Void... params),该方法在子线程中运行,我们所有的耗时操作都在这里处理。任务一旦结束可以通过return将执行结果进行返回,如果第三个泛型参数是Void,则不返回任务执行结果。注意,这个方法不可以进行UI操作,可以调用publishProgress(Progress... values)来完成任务的执行进度
  • onProgressUpdate(Integer... values),当后台任务调用publishProgress(Progress... values)方法后,该方法就会被调用,可以用返回的数据进行一些UI操作;
  • onPostExecute(Boolean aBoolean),当doInBackground(Void... params)执行完毕后并通过return返回时,该方法很快就会被调用,返回的数据就会作为参数传递到该方法中,可以利用返回来的数据进行一些UI操作

上面几个方法的调用顺序是:onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()

除了上面四个方法,AsyncTask还提供了onCancelled()方法,它同样在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。

3.AsyncTask的简单使用

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) 
               break;
         }
         return totalSize;
     }
 
     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }
 
     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }
//进行调用
new DownloadFilesTask().execute(url1, url2, url3);
复制代码

4.使用AsyncTask的注意事项

  • 异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。
  • execute(Params... params)方法必须在UI线程中调用。
  • 不要手动调用onPreExecute()doInBackground(Params... params)onProgressUpdate(Progress... values)onPostExecute(Result result)这几个方法。
  • 不能在doInBackground(Params... params)中更改UI组件的信息。
  • 一个任务实例只能执行一次,如果执行第二次将会抛出异常。

二、AsyncTask源码分析

1.成员变量

//CPU数量
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心线程数
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
//最大线程数
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//线程没有任务执行时最多保持30s会终止
private static final int KEEP_ALIVE_SECONDS = 30;
//线程是缓存队列,默认容量为128
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

//线程池工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
 /内置线程计数器
private final AtomicInteger mCount = new AtomicInteger(1);
  public Thread newThread(Runnable r) {
  //创建线程后计数器加1
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};
//串行线程池
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//并行线程池
public static final Executor THREAD_POOL_EXECUTOR;
//默认的线程池,采用的是串行的线程池
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//任务执行完发送的消息标志
private static final int MESSAGE_POST_RESULT = 0x1;
//任务执行过程中更新任务进度的消息标志
private static final int MESSAGE_POST_PROGRESS = 0x2;

//内部持有的Handler,用来切换线程
private static InternalHandler sHandler;
//实现了Callable接口的worker,可以拿到返回值的Runnable
private final WorkerRunnable<Params, Result> mWorker;
//FutureTask,实现了RunnableFuture接口
private final FutureTask<Result> mFuture;
//当前任务的状态,采用volatile关键字修饰,保持内存可见性,默认为待执行状态
private volatile Status mStatus = Status.PENDING;
//任务是否取消的标志,用AtomicBoolean保持可见性
private final AtomicBoolean mCancelled = new AtomicBoolean();
//任务是否开始的标志,用AtomicBoolean保持可见性
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
复制代码

以上都是一些AsyncTask类的成员变量,比较容易理解,需要注意的是三个线程池,一个是串行线程池,一个是并行线程池,还有一个是线程池的引用,串行线程池已经初始化,其他两个在静态代码块里初始化

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;
}
复制代码

2.构造方法

public AsyncTask() {
   	//创建一个Callable对象,将AsyncTask的实现类中的泛型Params,Result传递进去
    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;
        }
    };
    //创建一个FutureTask,将worker传递进去
    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);
            }
        }
    };
}
复制代码

该构造方法只是初始化了两个变量,mWorkermFuture,并把mWorker作为参数传入mFuture,那么这两个参数有什么作用呢?。

如果启动一个任务,就要调用该任务的execute()方法,因此看一下该方法的源码:

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}
复制代码

接着又调用executeOnExecutor(sDefaultExecutor, params)方法:

@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()方法,然后执行耗时任务在exec.execute(mFuture),将已经实例化好的mFuture传递进去了。

那么问题又来了,exec是什么?

从参数传递上可以看出是sDefaultExecutor,再追溯可以看到是SerialExecutor类,具体如下:

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);
        }
    }
}
复制代码

很容易可以看出,SerialExecutor是一个静态内部类,是所有AsyncTask实例化对象所共有的,SerialExecutor内部维持了一个队列,通过锁使得该队列保证AsyncTask是串行执行的,也就是任务要一个个加到该队列中,然后按照顺序一个个执行。

这个方法分为两个步骤:

  • 向队列加入一个新任务,即之前实例化的mFuture对象;
  • 调用scheduleNext()方法,调用THREAD_POOL_EXECUTOR执行头部的任务。

由此可见,SerialExecutor只是为了保证任务执行是串行的,实际执行交给了THREAD_POOL_EXECUTOR

THREAD_POOL_EXECUTOR实际上是一个线程池,开启了一定数量的核心线程和工作线程,然后调用execute()方法就开始执行异步任务了。

那么异步任务执行完了,又是怎么通知主线程更新UI?

我们先看前面构造方法中mWorkercall()方法的内容,先执行了doInBackground(mParams)方法,再执行postResult(result)

private Result postResult(Result result) {
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
复制代码

该方法向Handler发送了一个消息,用来通知主线程

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;
        }
    }
}
复制代码

在InternalHandler 中,

  • 如果收到的消息是MESSAGE_POST_RESULT,即执行完了doInBackground()方法并传递结果,那么就调用finish()方法。
  • 如果任务已经取消了,回调onCancelled()方法,否则回调 onPostExecute()方法。
  • 如果收到的消息是MESSAGE_POST_PROGRESS,回调onProgressUpdate()方法,更新进度。

到此为止,AsyncTask的源码分析完了。

三、AsyncTask出现的问题

  • AsyncTask不与任何组件进行绑定,所以最好在Activity/FragmentonDestory()方法中调用cancel(boolean)方法;
  • 内存泄漏,如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTaskActivity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。
  • 屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值