Android源码系列(15) -- AsyncTask

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

内容简介: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;
    }
}

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

查看所有标签

猜你喜欢:

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

PHP程序设计

PHP程序设计

Kevin Tatroe、Rasmus Lerdorf / 邓云佳 / 中国电力出版社 / 2003-7-1 / 68.00

本书涵盖了创建一个高效PHP Web应用程序所需要的所有技术,其内容包括:PHP语言基础的详细信息,包括数据类型、变量、操作符和流控制语句。用专门章节讨论关于函数、字符串、数组和对象的基本内容。涵盖通用的PHP Web应用程序设计技术,如表单处理和验证、会话跟踪以及cookie。用和数据库无关的PEAR DB库与关系数据库(如MySQL和Oracle)进行交互的内容。介绍用PHP生成动态图像、创建一起来看看 《PHP程序设计》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具