Android源码系列(15) -- AsyncTask

栏目: IOS · Android · 发布时间: 5年前

内容简介:AsyncTask令主线程的正确使用变得简单。无需维护线程或AsyncTask设计为一个围绕着通过一个执行在后台线程的运算来定义任务,其执行结果发布到主线程。异步任务的构成:

一、类签名

1.1 作用

AsyncTask令主线程的正确使用变得简单。无需维护线程或 Handler ,即能让任务在后台线程运算,并把结果提交到主线程。

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask设计为一个围绕着 ThreadHandler ,也不要构造普通线程框架的帮助类。合适执行(最多运算几秒的)短任务。如果任务导致线程长时间执行,强烈建议用由 java.util.concurrent 包下 ExecutorThreadPoolExecutorFutureTask 提供的APIs。

1.2 组成

通过一个执行在后台线程的运算来定义任务,其执行结果发布到主线程。异步任务的构成:

  • 3个类型: ParamsProgressResult
  • 4个步骤: onPreExecutedoInBackgroundonProgressUpdateonPostExecute

AsyncTask由子类继承,至少重写方法 doInBackground() ,也会基本重写另一个方法 onPostExecute()

用法示例:

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);

1.3 3个参数类型

有以下三个被异步任务使用的AsyncTask参数类型:

  • Params: 送到任务执行参数的类型;
  • Progress: 任务在后台计算时进度单元的类型,如Integer;
  • Result: 后台计算结果返回类型;

三个参数不需全部用上,不需要的用 Void 代替。例如:

private class MyTask extends AsyncTask<Void, Void, Void> { ... }

1.4 4个方法

分别是 onPreExecutedoInBackgroundonProgressUpdateonPostExecute

Android源码系列(15) -- AsyncTask

  1. onPreExecute() 在任务执行前在主线程调用,起配置任务的作用,如在界面上显示进度条;
  2. 随后,在后台线程调用 doInBackground 。本步骤用来执行很长时间的后台计算任务,参数在此步骤传递到异步任务。计算结果也在这步骤返给上一步骤。在计算子线程过程中,能通过方法 publishProgress 传递进度到主线程;
  3. 在子线程执行 publishProgress 会触发主线程调用 onProgressUpdate ,并向界面传递最新进度值;
  4. 后台线程执行完毕后,计算结果作为参数在主线程传给方法 onPostExecute

1.5 取消任务

任何时候都可通过 cancel(boolean) 取消任务,方法会继续调起 isCancelled() 并返回 true

调用 cancel(boolean) 后, doInBackground(Object[])__返回后的下一个执行方法是 __onCancelled(Object) ,而不是 onPostExecute(Object)

如果可以,为了保证任务能尽早被取消,需周期性地在 doInBackground(Object[]) 中检查 isCancelled() 方法的返回值。

1.6 线程规则

为保证类正常运行,有些线程规则需要遵守:

  • AsyncTask 必须在主线程载入。VERSION_CODES.JELLY_BEAN中此过程自动完成;
  • 任务实例必须在主线程中创建;
  • execute 方法必须在主线程调用;
  • 不得手动调用 onPreExecute()onPostExecute()doInBackground()onProgressUpdate()
  • 每个任务仅能执行一次,任务尝试多次调用会抛出异常;

1.7 内存可观察能力

AsyncTask保证所有回调通过以下安全、不需显式同步的方式调用:

  • 构造方法onPreExecute 设置成员变量,在 doInBackground 引用;
  • doInBackground 设置成员变量,在 onProgressUpdateonPostExecute 引用;

1.8 执行的顺序

历史版本:

  • 首次引入 AsyncTasks 类时,任务在单个串行后台线程中执行;

  • VERSION_CODES.DONUT 开始改为线程池,并在多线程同时并行执行;

  • 因为避免并行计算导致错误,从 VERSION_CODES.HONEYCOMB 始任务回到单后台线程执行;

如果确实需要并行执行,可以通过 executeOnExecutor(java.util.concurrent.Executor, Object[]) 达到使用 THREAD_POOL_EXECUTOR 的目的。

1.9 关于系统版本

本文介源码来自 Android 28 。但不同历史版本源码实现方法差别非常大,也会出现不同结果。所以在实际运行过程中,务必关注运行时系统版本。

二、常量

2.1 并行线程池

获取设备处理器核心数

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

线程池核心线程数最少2个线程、最多4个线程。在遵循此前提下,更倾向核心线程数比处理器实际核心数少1个,避免完全占用处理器而影其他后台任务的执行

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;

非核心线程存活时间

private static final int KEEP_ALIVE_SECONDS = 30;

线程工厂,为并行任务的Executor构建线程

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);  // 原子变量,从1开始递增

    public Thread newThread(Runnable r) { // 设置线程名称
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

执行任务Executor的阻塞队列,队列长度128

private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

并行任务Executor

public static final Executor THREAD_POOL_EXECUTOR;

初始化并行线程池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;
}

2.2 串行线程池

同一进程共用一个Executor顺序执行任务,任务每次执行一个

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

2.3 消息类型

消息类型为任务执行结果

private static final int MESSAGE_POST_RESULT = 0x1;

消息类型为主线程进度通知

private static final int MESSAGE_POST_PROGRESS = 0x2;

三、数据成员

// 顺序任务执行的Executor
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

// 保存InternalHandler,内部使用主线程Looper或自定义Looper
private static InternalHandler sHandler;

private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;

// 任务状态,任务构建后默认处于Status.PENDING
private volatile Status mStatus = Status.PENDING;

// 任务是否已被取消
private final AtomicBoolean mCancelled = new AtomicBoolean();

// 任务是否已被触发
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

private final Handler mHandler;

四、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();
                }
            }
        });
        
        // 顺序执行线程池首次执行,mActive为空
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        // 从任务队列获取下一任务
        if ((mActive = mTasks.poll()) != null) {
            // 向并行执行线程池添加任务
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

五、状态枚举

任务的状态枚举,用于表示指定任务当前处理状态

public enum Status {
    PENDING, // 任务正在尚未被执行,正在排队等待

    RUNNING, // 任务正在执行(运算)标志

    FINISHED, // 任务已经执行完毕:先调用onPostExecute(),再把状态置为此值
}

所有任务按照此生命周期单向前进,每个状态只允许设置一次。

Android源码系列(15) -- AsyncTask

// 获取主线程Handler
private static Handler getMainHandler() {
    // AsyncTask共用同一InternalHandler
    synchronized (AsyncTask.class) {
        // 初始化InternalHandler
        if (sHandler == null) {
            // 向InternalHandler传递主线程的Looper
            sHandler = new InternalHandler(Looper.getMainLooper());
        }
        return sHandler;
    }
}

// 设置默认Executor
public static void setDefaultExecutor(Executor exec) {
    sDefaultExecutor = exec;
}

六、构造方法

构建新异步任务,所有构造方法必须在主线程调用

public AsyncTask() {
    this((Looper) null);
}

通过指定Handler构建实例

public AsyncTask(@Nullable Handler handler) {
    this(handler != null ? handler.getLooper() : null);
}

通过指定Looper构建实例

public AsyncTask(@Nullable Looper callbackLooper) {
    // 若没有传入其他Looper,构造方法会自动获取主线程Looper,用获取的Looper创建Handler
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);

    // 构建WorkerRunnable
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            // 设置任务已被触发
            mTaskInvoked.set(true);
            // 任务执行结果
            Result result = null;
            try {
                // 设置线程优先级
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                // 把任务所需参数传入到后台线程执行并获取结果
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                // 任务执行出现异常,则取消任务
                mCancelled.set(true);
                throw tr;
            } finally {
                // 任务执行完成把结果传递给postResult
                postResult(result);
            }
            // 返回结果
            return result;
        }
    };

    // 把WorkerRunnable封装到FutureTask
    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);
            }
        }
    };
}

七、成员方法

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}
private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
private Handler getHandler() {
    return mHandler;
}
// 获取当前任务的执行状态
public final Status getStatus() {
    return mStatus;
}

重写方法实现后台线程的计算逻辑。任务调用者提供参数 paramsexecute()execute() 传递给本方法。在方法内可调用 publishProgress() 向主线程发布实时更新值

@WorkerThread
protected abstract Result doInBackground(Params... params);

doInBackground() 调用前,先在主线程执行此方法

@MainThread
protected void onPreExecute() {
}

doInBackground()完成后在主线程调用此方法, resultdoInBackground() 返回的结果。如果任务被取消,此方法不会触发

@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}

publishProgress()运行后在主线程调用此方法, value 表示任务处理的进度,参数 values 是传递给__publishProgress()__ 的 values

@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}

cancel(boolean)被调用,且 doInBackground(Object[]) 结束后在主线程调用此方法。

方法默认简单回调 onCancelled() 并忽略结果。如果要在子类重写其他实现,不要在重写方法内调用 super.onCancelled(result) 。注意, result 可为 null

@SuppressWarnings({"UnusedParameters"})
@MainThread
protected void onCancelled(Result result) {
    onCancelled();
}

应用最好能重写本方法,以便在任务被取消后做出反应。此方法由 onCancelled(Object) 的默认实现调用。 cancel(boolean) 被调用且 doInBackground(Object[]) 已结束后,方法在主线程上调用。

@MainThread
protected void onCancelled() {
}

若任务在正常完成前被取消,此方法返回true。

public final boolean isCancelled() {
    return mCancelled.get();
}

尝试取消任务,如果任务已执行完毕、取消、由于其他原因不能取消的,则取消失败。任务取消成功,且在 cancel() 调用是尚未开始,任务不会执行。

如果任务已经开始,参数 mayInterruptIfRunning 决定任务执行线程是否该被中断。调用此方法,且 doInBackground(Object[]) 结束后,会在主线程调用 onCancelled(Object) 。调用此方法能保证 onPostExecute(Object) 不会执行。

此方法调用后,需从 doInBackground(Object[]) 周期性检查由 isCancelled() 返回的值并尽快结束任务。参数 mayInterruptIfRunningtrue ,执行任务线程需被中断,否则等任务执行直至完成。

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

等待计算完毕后获取执行结果:

  • 任务被取消后调用此方法,抛出CancellationException;

  • 当前线程在等待结果过程被中断,抛出InterruptedException;

public final Result get() throws InterruptedException, ExecutionException {
    return mFuture.get();
}

等待计算完毕后获取执行结果,设置timeout作为等待超时时间:

  • 任务被取消后调用此方法,抛出CancellationException;

  • 任务执行过程中出现异常,抛出ExecutionException;

  • 当前线程等待结果过程中被中断,抛出InterruptedException;

  • 等待结果超时,抛出TimeoutException;

public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
        ExecutionException, TimeoutException {
    return mFuture.get(timeout, unit);
}

用指定参数执行任务,任务返回自身以便调用者获取引用。功能在单个后台线程执行,或根据具体平台版本决定。

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

用指定参数执行任务,任务返回自身以便调用者获取引用。方法用 THREAD_POOL_EXECUTOR 实现多任务并行处理,也可以用自定义Executor实现定制。并行执行不能保证任务运行顺序的先后,如果多个任务需有序执行,请使用顺序任务。

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    // 若任务默认状态不是PENDING状态,直接抛出异常
    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;
}

通过默认Executor执行单个Runnable

@MainThread
public static void execute(Runnable runnable) {
    sDefaultExecutor.execute(runnable);
}

doInBackground()在后台线程运行过程可(多次)调用此方法。每次调用方法都会在子线程向主线程发布更新进度的 Message ,并在主线程触发 onProgressUpdate() 。如果任务被取消, onProgressUpdate 不会调用。

@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

任务执行完成,根据完成状态执行对应分支逻辑

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

八、InternalHandler

private static class InternalHandler extends Handler {
    // 构造AsyncTask实例时默认主线程Looper
    public InternalHandler(Looper looper) {
        super(looper);
    }

    // 消息通过handleMessage在主线程执行分发运行逻辑
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        // 从Message中获取异步任务执行结果
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        // 按照消息不同类型执行逻辑
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // 唯一结果传入finish(),内部再调用onPostExecute()
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                // 通知主线程更新进度
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

九、WorkerRunnable

WorkerRunnable实现Callable接口。相比Runnable接口,Callable会在运行成功完成后返回运行结果。此类是抽象类,子类需实现 call() 抽象方法

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

十、AsyncTaskResult

异步任务执行后结果

@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;

    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Amazon Web Services

Programming Amazon Web Services

James Murty / O'Reilly Media / 2008-3-25 / USD 49.99

Building on the success of its storefront and fulfillment services, Amazon now allows businesses to "rent" computing power, data storage and bandwidth on its vast network platform. This book demonstra......一起来看看 《Programming Amazon Web Services》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具