Android AsyncTask

简介: 本文简单介绍Android中的AsyncTask,并从源码角度分析它的流程和特点。AsyncTask有助于使用UI线程。这个类能让你不主动使用多线程或Handler,在UI线程进行后台操作并发布结果。

本文简单介绍Android中的AsyncTask,并从源码角度分析它的流程和特点。

AsyncTask有助于使用UI线程。
这个类能让你不主动使用多线程或Handler,在UI线程进行后台操作并发布结果。
是一个在不用多线程和Handler的情况下的帮助类。AsyncTask适用于短时间的操作(最多几秒)。
如需长时间的线程操作,建议使用多线程包java.util.concurrent中的API,比如ExecutorThreadPoolExecutorFutureTask

AsyncTask任务的构成:

  • 3种泛型:ParamsProgressResult
  • 4个步骤:onPreExecute, doInBackground, onProgressUpdateonPostExecute

Google文档

用法简介

虚构一个计算任务

    /**
     * 虚拟的计算任务
     */
    private class CalculationTask extends AsyncTask<Float, Integer, Float> {
        protected Float doInBackground(Float... inputs) {
            Log.d(TAG, "doInBackground thread ID = " + Thread.currentThread().getId());
            long step = 0;
            float result = 0;
            for (float f : inputs) {
                // 假设这里有一些耗时的操作
                result += f;
            }
            while (step < 5) {
                result += step;
                step++;
                publishProgress((int) step);
            }
            return result;
        }

        protected void onProgressUpdate(Integer... progress) {
            Log.d(TAG, "onProgressUpdate thread ID = " + Thread.currentThread().getId());
            Log.d(TAG, "onProgressUpdate: " + progress[0]);
        }

        protected void onPostExecute(Float result) {
            Log.d(TAG, "onPostExecute thread ID = " + Thread.currentThread().getId());
            Log.d(TAG, "任务执行完毕");
        }
    }
    // 执行任务
    new CalculationTask().execute(1.2f, 2.3f, 6.3f);
/*
logcat
Main thread ID = 1
doInBackground thread ID = 8089
onProgressUpdate thread ID = 1
onProgressUpdate: 1
...
onProgressUpdate thread ID = 1
onProgressUpdate: 5
onPostExecute thread ID = 1
任务执行完毕
*/

AsyncTask 使用的的泛型

AsyncTask使用的3种泛型

  • Params 送去执行的类型
  • Progress 后台计算的进度类型
  • Result 后台计算的结果

不用的泛型可以用Void表示。例如

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

异步任务的4个步骤

异步任务执行时经过4个步骤

  • onPreExecute() UI线程在任务开始前调用这个方法。此方法常用来设置任务,比如在屏幕上显示一个进度条。
  • doInBackground(Params...) onPreExecute()执行完毕后立即在后台线程中执行。这一步用来执行耗时的后台计算。
    这个方法接受异步任务的参数,返回最后的任务结果。这一步可以调用publishProgress(Progress...)通知出去一个或多个进度。这些进度值会被onProgressUpdate(Progress...)在UI线程收到。
  • onProgressUpdate(Progress...) 调用publishProgress(Progress...)后会在UI线程中执行。用来显示执行中任务的UI。
  • onPostExecute(Result) 后台任务执行完毕时被调用。最终结果会被传入这个方法。

取消任务

调用cancel(boolean)可随时取消任务。取消任务后isCancelled()会返回true。
调用这个方法后,后台任务doInBackground(Object[])执行完毕后会调用onCancelled(Object)而不再是onPostExecute(Object)
为保证任务能被及时地取消,在doInBackground(Object[])中应该经常检查isCancelled()返回值

线程规则 Threading rules

一些线程规则

  • 异步任务必须从UI线程启动
  • 必须在UI线程实例化AsyncTask类
  • 必须在UI线程调用execute(Params...)
  • 不要手动调用onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)
  • 同一个异步任务实例只能被执行一次。重复执行同一个异步任务实例会抛出异常(IllegalStateException)。

源码简析

需要解决的问题:
AsyncTask是如何调用后台线程完成任务的?线程是如何调度的?

AsyncTask使用Executor,利用WorkerRunnableFutureTask来执行后台任务

    private final WorkerRunnable<Params, Result> mWorker; // 实现了 Callable
    private final FutureTask<Result> mFuture;

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

使用Handler来进行线程调度。内部定义了一个类InternalHandler

execute(Params... params)方法切入

先看方法execute(Params... params),使用默认执行器,并传入参数
调用xecuteOnExecutor(Executor exec, Params... params)

    @MainThread // 指定在主线程执行
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

先判断当前状态,如果状态不是Status.PENDING,则抛出异常。
否则进入Status.RUNNING状态,执行onPreExecute(),再由执行器启动任务。

    @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;
    }

mWorker带着传进来的参数,mFuture实例化时已经将mWorker注入。参看构造函数

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                // 在后台线程进行自定义的操作  这里面可以调用publishProgress方法
                Result result = doInBackground(mParams); 
                Binder.flushPendingCommands();
                return postResult(result); // 发送最终结果
            }
        };

        mFuture = new FutureTask<Result>(mWorker) { // 依赖 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);
                }
            }
        };
    }

publishProgress方法通过主线程的Handler向外通知进度

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

后台任务执行完毕,postResult发送最终结果

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget(); // 会走到finish方法
        return result;
    }

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result); // 如果任务已经被取消了
        } else {
            onPostExecute(result); // 通知任务执行完毕
        }
        mStatus = Status.FINISHED;
    }

关于默认执行器 sDefaultExecutor 和线程池

源码中构建了一个线程池和一个自定义的执行器SerialExecutor。靠它们来执行后台任务。

参考源代码

public abstract class AsyncTask<Params, Progress, Result> {
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // 核心线程至少2个,最多4个
    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;

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

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

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

    // 默认执行器的类
    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);
            }
        }
    }
}

backup at http://rustfisher.github.io/2017/06/22/Android_note/Android-AsyncTask/

目录
相关文章
|
10月前
|
Java 数据库 Android开发
Android异步之旅:探索AsyncTask
Android异步之旅:探索AsyncTask
77 0
|
6月前
|
Android开发 开发者 Kotlin
告别AsyncTask:一招教你用Kotlin协程重构Android应用,流畅度飙升的秘密武器
【9月更文挑战第13天】随着Android应用复杂度的增加,有效管理异步任务成为关键。Kotlin协程提供了一种优雅的并发操作处理方式,使异步编程更简单直观。本文通过具体示例介绍如何使用Kotlin协程优化Android应用性能,包括网络数据加载和UI更新。首先需在`build.gradle`中添加coroutines依赖。接着,通过定义挂起函数执行网络请求,并在`ViewModel`中使用`viewModelScope`启动协程,结合`Dispatchers.Main`更新UI,避免内存泄漏。使用协程不仅简化代码,还提升了程序健壮性。
195 1
|
9月前
|
Android开发
40. 【Android教程】AsyncTask:异步任务
40. 【Android教程】AsyncTask:异步任务
189 2
|
10月前
|
数据库 Android开发
android AsyncTask
android AsyncTask
54 1
|
10月前
|
Java Android开发
Android 中的AsyncTask的使用心得
Android 中的AsyncTask的使用心得
47 1
|
10月前
|
Android开发 开发者
Android AsyncTask 的使用
Android AsyncTask 的使用
71 1
|
10月前
|
安全 API 数据库
【转】Android线程模型(AsyncTask的使用)
【转】Android线程模型(AsyncTask的使用)
54 1
|
10月前
|
Android开发
Android中的多线程及AsyncTask的引入,最终入职阿里
Android中的多线程及AsyncTask的引入,最终入职阿里
|
10月前
|
Java Android开发
android AsyncTask入门
android AsyncTask入门
44 0
|
安全 Java Android开发
Android 中AsyncTask后台线程,异步任务的理解
Android 中AsyncTask后台线程,异步任务的理解
177 0

热门文章

最新文章

  • 1
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 2
    Android历史版本与APK文件结构
  • 3
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
  • 4
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 5
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 6
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 8
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 9
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
  • 10
    escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥