• Android随笔-AsyncTask


    概述

    AsyncTask是一种轻量级的异步任务类,可以在线程池中执行后台任务,然后把执行的进度和执行的结果传递给主线程,并在主线程中更新UI。

    结构

    类型

    public abstract class AsyncTask<Params, Progress, Result> {
    
    • 1

    AsyncTask是一个抽象类,有Params、Progress、Result三个参数。

    • Params,表示后台任务需要的参数类型。
    • Progress,表示后台任务执行进度。
    • Result,表示后台任务的返回结果。

    这三个参数都可以不传递,不传递的时候使用Void代替。Void相当于void的包装类,可以理解为和Integer与int的关系一样。

    方法

    • onPreExecute()
     @MainThread
        protected void onPreExecute() {
        }
    
    • 1
    • 2
    • 3

    该方法在主线程中执行,用于异步任务执行之前做一些准备工作。

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

    Params:参数,也就是创建异步任务继承AsyncTask传递的参数类型。
    “…”:参数数量可以有很多个,表示参数数量不定,是一种数组型参数,相当于类型为Params的数组[ ]。

    工作线程,也是后台执行线程,该方法是抽象方法,使用的时候必须实现,用于在线程池中执行异步任务。
    -onProgressUpdate(Progress... values)

       @MainThread
        protected void onProgressUpdate(Progress... values) {
        }
    
    • 1
    • 2
    • 3

    主线程中调用,用于更新后台任务执行的进度。

    • publishProgress(Progress... values)
        @WorkerThread
        protected final void publishProgress(Progress... values) {
            if (!isCancelled()) {
                getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                        new AsyncTaskResult<Progress>(this, values)).sendToTarget();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    异步任务中的工作线程,用于发布异步任务执行的进度,在doInBackground(Params… params)执行异步任务时,可以调用publishProgress(Progress… values)发布异步任务的执行进度,然后该方法会调用onProgressUpdate(Progress… values)在主线程中更新任务执行进度。

    • onPostExecute(Result result)
        @MainThread
        protected void onPostExecute(Result result) {
        }
    
    • 1
    • 2
    • 3

    在主线程执行,异步任务执行完毕后会调用该方法返回最后的结果result,result是doInBackground方法的返回值,也是使用异步任务是传递进来的Result类型。
    上述方法中,执行流程如下:

    1. onPreExecute()。
    2. doInBackground(Params… params)。
    3. onPostExecute(Result result)。

    除了上面几个方法外,AsyncTask还提供了onCancelled() 用于取消异步任务,在主线程执行,执行了该方法则onPostExecute(Result result)就不再执行。

    使用

    使用Room进行用户查询时,使用了AsyncTask,因为APP中用户量很少,所以用户相关数据的增删改查都可以使用AsyncTask。

    public class QueryUserTask extends AsyncTask<Void, Void, List<User>> {
        private UserDao userDao;
        
        public QueryUserTask(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        protected List<User> doInBackground(Void... voids) {
            Log.d("QueryUserTask", "doInBackground: start");
            List<User> users = userDao.queryAllUser();
            Log.d("QueryUserTask", "doInBackground: end");
            return users == null ? Collections.EMPTY_LIST : users;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    调用:

    List<User> users = new QueryUserTask(userDao).execute(Void);
    
    • 1

    如果是按标准写法,可以这样写:

    List<User> users = new QueryUserTask().execute(userDao);
    
    • 1

    如果是下载类的任务就需要使用进度。在doInBackground方法中进行文件下载,并计算下载文件大小和下载进度,调用publishProgress发布下载进度,紧接着onProgressUpdate就会被调用在主线程更新进度条,下载任务完成后调用onPostExecute方法标志着任务已完成。

    public class DownloadImageFileTask extends AsyncTask<URL, Integer, Long> {
        ...
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
    
        @Override
        protected Long doInBackground(URL... urls) {
            // 已下载字节大小
            long download = Downloader.downloadImage(urls[0]);
            // 发布进度
            publishProgress((int) (download / total * 100));
            return download;
        }
    
        @Override
        protected void onProgressUpdate(Integer... values) {
            // 更新下载进度
            setProgress(values[0]);
        }
    
        @Override
        protected void onPostExecute(Long aLong) {
            showToast("下载关闭,请指示");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    执行:

    new DownloadImageFileTask().execute(url);
    
    • 1

    原理

    • AsyncTask中使用的是线程池中的线程。
      @MainThread
        public final AsyncTask<Params, Progress, Result> execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
        }
    
    • 1
    • 2
    • 3
    • 4

    executeOnExecutor:

        @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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    其中sDefaultExecutor就是一个线程池。也能说明onPreExecute方法是最先执行的。

        private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    
    • 1

    volatile表示sDefaultExecutor加了轻量锁。
    SERIAL_EXECUTOR:

       public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    
    • 1

    而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);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    THREAD_POOL_EXECUTOR就是线程池。

        public static final Executor THREAD_POOL_EXECUTOR;
    
        static {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(), sThreadFactory);
            threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ThreadPoolExecutor :典型的线程池。
    执行过程中,首先会把AsyncTask的Params封装成FutureTask对象mFuture,然后SerialExecutor的对象exe执行exec.execute(mFuture),执行后就会把mFuture加入任务队列ArrayDeque的对象mTasks中,如果没有正在活动的FutureTask,就会执行scheduleNext方法,从ThreadPoolExecutor线程池中获取线程,执行队列中任务,直到执行完为止,也说明了在这种情况下AsyncTask是串行执行任务的,也说明了AsyncTask是异步的。
    SerialExecutor主要用于对需要执行的任务进行排队,而ThreadPoolExecutor才是真正执行任务的线程。

    • Params封装成FutureTask对象mFuture
      首先在executeOnExecutor方法中对参数进行赋值:
        mWorker.mParams = params;
            exec.execute(mFuture);
    
    • 1
    • 2

    赋值完后对mWorker进行包装。

            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);
                    }
                }
            };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    而mWorker就是真正工作的对象。

         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;
                }
            };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    mWorker会执行doInBackground真正干活。

    • 使用Handler进行线程切换
      当调用AsyncTask时,就会创建或获取mHandler。
        public AsyncTask() {
            this((Looper) null);
        }
    
        /**
         * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
         *
         * @hide
         */
        public AsyncTask(@Nullable Handler handler) {
            this(handler != null ? handler.getLooper() : null);
        }
    
        /**
         * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
         *
         * @hide
         */
        public AsyncTask(@Nullable Looper callbackLooper) {
            mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
                ? getMainHandler()
                : new Handler(callbackLooper);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    而这里的getMainHandler获取的handler是InternalHandler。

        private static Handler getMainHandler() {
            synchronized (AsyncTask.class) {
                if (sHandler == null) {
                    sHandler = new InternalHandler(Looper.getMainLooper());
                }
                return sHandler;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    InternalHandler是AsyncTask里面用来处理消息的。

        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;
                }
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    若任务执行完毕,将执行结果作为参数调用finish方法,

        private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    finish方法在取消任务的情况下调用onCancelled,并将结果返回,如果任务没有取消则调用onPostExecute方法将结果返回。
    如果任务还在执行中,调用onProgressUpdate更新进度。

    总的执行任务流程:

    1. 将Params包装成FutureTask。
    2. 从线程池ThreadPoolExecutor中取线程执行FutureTask。
    3. 将执行进度及执行结果通过InternalHandler切换回主线程中。

    总结

    • AsyncTask对象必须在主线程创建。
    • 一个AsyncTask对象只能执行一次,多次调用execute方法回报错。
    • 执行方法 execute必须在主线程中调用。
    • 程序中不建议直接调用AsyncTask中的方法,调用execute回自动执行的。
    • execute执行串行任务。
    • executeOnExecutor可执行并行任务。
    • 使用ThreadPoolExecutor线程池进行任务执行。
    • 使用Handler进行线程切换。
    • AsyncTask不适合特别耗时的后台任务。
    • AsyncTask中没有生命周期管理。
  • 相关阅读:
    自学软件测试不知道该如何学起,【软件测试技能图谱|自学测试路线图】
    图解LeetCode——784. 字母大小写全排列(难度:中等)
    化工管理杂志化工管理杂志社化工管理编辑部2023年第30期目录
    基于前后端分离的增删改查
    小学Python编程 ——迷宫
    【常见开源库的二次开发】基于openssl的加密与解密——Base的编解码(二进制转ascll)(二)
    kafka介绍
    Java日志系统之Log4j
    Java(七)——集合框架---泛型Generic
    JAVA计算机毕业设计手办销售系统源码+系统+mysql数据库+lw文档
  • 原文地址:https://blog.csdn.net/qq_34202054/article/details/124336648