CrazyDailyQuestion icon indicating copy to clipboard operation
CrazyDailyQuestion copied to clipboard

2019-08-22: 说一说对AsyncTask的理解,它是同步的还是异步的?有没有读过相关源码?

Open liyilin-jack opened this issue 6 years ago • 9 comments

liyilin-jack avatar Aug 22 '19 01:08 liyilin-jack

doInBackground()这个方法是异步的,其他几个方法是在主线程回调的。

内部维护了两个线程池。

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;

// 注意这点,公有的,静态的
public static final Executor THREAD_POOL_EXECUTOR;

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, 
                KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
// 把这个值赋给了 THREAD_POOL_EXECUTOR
THREAD_POOL_EXECUTOR = threadPoolExecutor;

2.任务调度的线程池,姑且这么说吧,其实他是串行的


// 注意这点,公有的,静态的
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

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

上边代码中注释标记注意的地方大家都看到了,静态的变量,也就是说,所有的AsyncTask都是用的这两个线程池。

1.AsyncTask不适合做耗时的任务,上边已经分析了,不然别人都得等你。当然,你可以自己指定线程池。这样就可以并行了。但是它默认不这么做了,是什么原因呢?这个还是没想的太明白。 2.使用的时候注意内存泄漏问题。主要说的是做为内部类(所有使用内部类的时候都要注意,因为会持有外部类的引用)使用的时候。

回调简单说一下,就是通过一个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;
        }
    }
}

rainbow7 avatar Aug 22 '19 03:08 rainbow7

AsyncTask

一.概念

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

二.组成

a.组织架构

   AsyncTask = Handler + ThreadPoolExecutor

b.参数

abstract class AsyncTask<Params, Progress, Result>
  • 泛型类,提供 Params,ProgressResult三个泛型参数.
    • Params: 参数类型
    • Progress: 后台任务执行类型
    • Result: 后台任务返回结果类型
      Void 可以代替这三个参数, 免除参数必传因子

c.核心方法

public class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    @Override
    protected Long doInBackground(URL... urls) {
        final int length = urls.length;
        long totalSize = 0;

        for (int i = 0; i < length; i++) {
         
            // 执行文件多线程下载操作
            // TODO: totalSize += Downloader.downloadFile(url[i]);
            
            publishProgress((int) ((i / (float) length) * 100));

            if (isCancelled()) {
                break;
            }
        }
        return totalSize;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        // TODO: 获取下载进度,用Progress展示
    }

    @Override
    protected void onPostExecute(Long aLong) {
        // TODO: 显示现在结果操作
    }

}
方法
意义
onPreExecute
在主线程执行,在异步任务执行之前,该方法会调用
doInBackground
执行异步任务,publicProgress更新任务进度
onProgressUpdate
在主线程执行,后台任务的执行进度法神改变时候此方法会被调用
onPostExecute
在主线程中执行,在异步任务执行之后,此方法会被调用
onCancle
在主线程中执行,异步任务取消的时候,被调用
1.执行顺序:

onPreExecute -> doInBackground -> onProgressUpdate

2.开启任务方式:
new DownloadFilesTask().execute(url1,url2,url3);

三.作用

  简化子线程访问 UI 的过程.

四.工作原理

  源码分析,我们之前说过,首先看调用,AsyncTask执行操作是execute,我们进入execute查看一下:

    @MainThread
    public static void execute(Runnable runnable) {
    // 串行线程池,一个进程中所有的 AsyncTask 全部在这个串行的线程池排队执行
        sDefaultExecutor.execute(runnable);
    }
        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 最先执行
        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

  接下来我们分析一下线程池的执行操作:

 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
 
 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
 // AsyncTask 有两个线程池, SerialExecutor 使用于任务排列
 private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
// 2.FutureTask  会交给 SerialExecutor 的 execute 执行
        public synchronized void execute(final Runnable r) {
        // 同时当一个 AsyTask 任务执行完之后,AsyTask 会继续执行到所有任务被执行为止
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                   
                        scheduleNext();
                    }
                }
            });
            
            // 4. 如果里面没有执行活动会调用 scheduleNext 方法
            if (mActive == null) {
                scheduleNext();
            }
        }
// 3. execute 会把 FutureTask 对象插入任务队列mTask中
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
    
      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);
                }
            }
        };

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();
// 1.AsyncTask 的 params 参数封装给 FutureTask,FutureTask是一个并发类,和Runnable等效
        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

THREAD_POOL_EXECUTOR 是用于真正执行任务的,

        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. 不要在程序中直接调用onPreExecute , doInBackground ,onProgressUpdateonProgressUpdate方法。
  3.一个AsyncTask对象只能执行一次,即只能用一次execute,否则会抛异常。
  4. 并行任务请使用excuteOnExecutor,excute会串行。

六.竞品

  • HandlerThread
  • IntentService
  • ThreadPoolExecutor
  • ThreadFactory
  • Callable 和 Future
  • Runnable
  • Thread

MicroKibaco avatar Aug 22 '19 11:08 MicroKibaco

为什么AsyncTask只能执行一次,只能用一次execute?

我们点开execute(),可以看到调用了executeOnExecutor()

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

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

可以看到这里有一个枚举状态的校验Status共有三种状态,对象初始化时默认为Status.PENDING,其他时候均没有赋值Status.PENDING imageexecuteOnExecutor一旦被调用一次mStatus状态就会被改变,之后再调用就会抛对应状态的异常了。

为什么AsyncTask默认是串行的?

前面看到默认状态下调用execute方法是带着sDefaultExecutor去调用executeOnExecutor,我们跟一下sDefaultExecutor这个线程池是什么

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    public static final Executor SERIAL_EXECUTOR = new 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);
            }
        }
    }

可以看到,每次调用sDefaultExecutor#execute

  1. 先把runnable包一层(装饰模式)入队

  2. 如果目前没有正在执行的任务,即mActive,就尝试从队列中取一个交给另一个线程池去执行

  3. 每次执行完实际的任务,顺便尝试从队列中取一个交给另一个线程池去执行

由此可见sDefaultExecutor这个静态的线程池就是负责保证任务顺序执行的。 接下来我们看下execute方法是否真的使用了sDefaultExecutor这个线程池去执行任务,上面代码可以看到executeOnExecutor方法在执行onPreExecute()之后就使用sDefaultExecutor去执行任务mFuture,我们看下mFuture是什么:

public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

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

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

可以看到mFuture包着mWorker是在构造函数中创建的,看到mFuturerun()

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
...省略代码...

mFuture#run()执行了callable的call方法,而callable正是通过构造带进去的mWorker,其call()方法中赫然就有result = doInBackground(mParams),至此,我们知道了默认情况下用sDefaultExecutor来实现串行,如果你想并行,那就要自己做一个线程池直接调用executeOnExecutor()传进去。

此外,也不难看到AsyncTask四个回调方法中的onPreExecute()是一开始就在调用线程中执行了onPreExecute中又可能做一下跟UI有关的事情,所以规范上execute()必须要主线程调用(如果你的onPreExecute中不需要做一些跟UI有关的事情那就不强求一定要在UI线程调用),而doInBackground()则是在子线程中调用的。

如何确保结果回调onPostExecute()以及进度回调onProgressUpdate()在主线程中执行?

看到mWorker执行完成时调用了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;
    }

这里的Handler跟一下不难发现

private Handler getHandler() {
        return mHandler;
    }
public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

 private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

除非你在AsyncTask创建时传入指定的Looper,不然都是默认Looper.getMainLooper() 看到具体实现InternalHandler

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

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

可以明显看到InternalHandler在这里的作用时用来切线程,不出意外都是切回主线程的,

AsyncTask真的必须在主线程中加载吗?为什么大家都这么说?

旧的AsyncTask源码中sHandler的默认创建是这样的

private static final InternalHandler sHandler=new InternalHandler();

不指定Looper的Handler用的是当先线程的Looper,所以如果你第一次使用在子线程中的话,类加载时,静态变量一并创建,那么sHandler的Looper就不在主线程了,后面的onPostExecute()以及onProgressUpdate()就不在主线程中执行了。

但是,我在28的源码中看到的已经不是这种默认创建的方法了,而是摆明车马指定了Looper.getMainLooper(),那么不管你第一次在哪里加载使用,都不影响切换回主线程了。

private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

chinwetang avatar Aug 22 '19 12:08 chinwetang

在Android中,由于主线程的诸多限制,像网络请求等一些耗时的操作我们必须在子线程中运行。我们往往会通过new Thread来开启一个子线程,待子线程操作完成以后通过Handler切换到主线程中运行。这么以来我们无法管理我们所创建的子线程,并且无限制的创建子线程,它们相互之间竞争,很有可能由于占用过多资源而导致死机或者OOM。所以在Java中为我们提供了线程池来管理我们所创建的线程。

而AsyncTask就是这么个环境下产生的。它本质上是一个静态的线程池(封装了THREAD_POOL_EXECUTOR异步线程池和SERIAL_EXECUTOR同步线程池和Handler),AsyncTask派生出的子类可以实现不同的异步任务,这些任务都是提交到静态的线程池中执行。线程池中的工作线程执行doInBackground(mParams)方法执行异步的任务。当任务状态改变后,工作线程向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数。

AsyncTask使用不当的后果

1.)生命周期

AsyncTask不与任何组件绑定生命周期,所以在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);

2.)内存泄漏

如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。

3.) 结果丢失

屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

AsyncTask里面的两个线程池:

1AsyncTask里面有THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR两种方式来异步执行任务;THREAD_POOL_EXECUTOR是异步的,而SERIAL_EXECUTOR任务是顺序执行的。 2.    THREAD_POOL_EXECUTOR如果添加的任务过多,没有及时处理的话,会导致程序崩溃,它的队列size是128;它的调度规则是核心池大小,队列大小,以及最大线程数和异常处理Handler来决定的。 3.    SERIAL_EXECUTOR本质是在THREAD_POOL_EXECUTOR的基础上添加一个mTasks的集合来保证任务的顺序执行。

liu1813565583 avatar Aug 22 '19 13:08 liu1813565583

大佬们都回答的差不多了,我也简单说一下吧。

AsyncTask主要是用来处理子线程和UI线程互相切换的情景。于执行耗时任务而言,它是执行在子线程中的(内部由一个线程池维护);于多个任务之间的,添加到任务队列而言,如果是直接执行execute方法,它则是同步的,就是说,每一个任务的执行,都必须等上一个任务完成。

当然,你也可以执行executeOnExecutor来接管从入队到到执行所有的实现,让他变成一个可以异步添加,并且异步执行的AsyncTask。

(未完待续...)

liyilin-jack avatar Aug 22 '19 15:08 liyilin-jack

AsyncTask主要用来执行耗时操作,同时它把执行进度和结果传递给UI线程,因此很适合一些需要在执行完耗时操作后更新UI或者执行耗时操作过程在UI上显示进度的场景。

AsyncTask内部封装了Handler和线程池,只暴露出五个主要方法给使用者,使用者可以方便地在对应的方法内去做耗时操作或者更新UI。

主要有五个方法:

  • onPreExecute:主线程中执行,可以用来做一些执行耗时操作之前的准备工作。
  • doInBackground :在子线程中执行,必须实现的方法,用来做耗时操作。可以通过publishProgress方法调用onProgressUpdate显示下载进度。下载完成后会调用onPostExecute方法。
  • onProgressUpdate:主线程中执行,调用publishProgress才会触发此方法。
  • onPostExecute:主线程中执行,任务执行完成后会调用此方法。
  • onCancelled:主线程中执行,任务取消时被调用。

AysncTask对象可以在主线程中创建,但是execute方法按照规范必须在主线程中执行。因为onPreExecute方法是在execute的线程中执行的。而构造函数内部的Handler则默认使用了主线程了Looper,所以AysncTask对象创建的线程不做要求。

AysncTask内部主要有两个线程池,SerialExecutor负责将任务同步加入队列中,ThreadPoolExecutor负责实际执行任务。

skylarliuu avatar Aug 22 '19 15:08 skylarliuu

大佬们说了很多,我加一点: 任何时候调用cancel(boolean)方法,都可以取消任务。这个方法会产生的影响是:之后调用的iscancelled()会返回true。 这个方法调用之后,onCancelled(Object)方法会在doInBackground(Object[]) 方法返回后执行,而不是onProgressUpdate(Progress... values)方法 鉴于上述据说,为了确保任务是尽快取消,你应该定期在doInBackground()方法中始终检查iscancelled()方法的返回值。如果可以的话,放在一个循环当中。

zhengjunke avatar Aug 22 '19 15:08 zhengjunke

好久没用过AsyncTask了,不过研究一下内部的代码还挺有意思的。 上边的小伙伴都分析的很厉害。 AsyncTask内部其实就是对线程池和Handler进行了封装。 下边我简单的分析一下代码加深下印象: 1.我们继承了AsyncTask的子类对象调用execute。

    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
       //下边这一段的意思就是说,一个asyncTask对象只能调用一次execute方法
        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;
    }

上边的mFuture其实就是线程的执行体,内部在执行的时候放入callable对象进行回调,回调的结果重新传到AsyncTask进行操作。因为mFuture是子线程的线程实体,那结果传到AsyncTask也是在子线程的。这时候我们需要使用Handler将结果从子线程传到主线程。 2.那这个mFuture是怎样放到子线程执行的呢?

private static class SerialExecutor implements Executor {
       //双向队列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        //synchronized的修饰保证了列表中的任务被顺序的加入,也就是会被顺序的执行
        public synchronized void execute(final Runnable r) {
           //这里加入的就是mFuture,
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        //队列中的一个任务执行完毕之后,我继续执行下一个
                        scheduleNext();
                    }
                }
            });
            //往队列里塞了一个任务之后,试着执行一下
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //从队列中取任务,如果不为空,我就把这个任务放到线程池中
            //THREAD_POOL_EXECUTOR是线程池对象,
            if ((mActive = mTasks.poll()) != null) {
                //至此mfuture被扔到子线程中执行,当然是大致这样说,其实后边还有操作,细节就不说了
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

上边我们看到一个AsyncTask对象只能被执行一次,为什么只能被执行一次呢?普遍的说法是AsyncTask是一个轻量级的执行后台任务的类,Google对它进行了限制,一个任务只能执行一次。


AsyncTask的缺点

不够灵活

我感觉最大的缺点就是不能和Activity的生命周期保持同步,即使在ondestory方法对任务进行cancel,doInBackground这个方法也不会立马停止,也就是说子线程还是有可能会执行结束。 下边是Future中的cancel方法:

  public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              U.compareAndSwapInt(this, STATE, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        //使用interrupt并不能中断线程的执行,只是改变了线程的中断状态
                        t.interrupt();
                } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }

不够灵活,我感觉是它最大的缺点。 应付这样的问题,就是避免在AsyncTask做耗时较长的任务,它是一个轻量级的工具。

局限性

能在主线程和子线程执行,但是最后的执行结果都是传到主线程。也就是说只能给主线程服务。而且一个AsyncTask对象只能执行一次, 这个不算是缺点吧,他本来就是一个被定位为轻量级的东西,所以功能少点也正常。

其他

比如内存泄漏;屏幕旋转之后,AsyncTask内引用的Activity对象改变等

gys0000 avatar Aug 22 '19 15:08 gys0000

请教一下楼主。既然AsyncTask只能执行一次,为什么它还需要一个THREAD_POOL_EXECUTOR这个线程池呢?THREAD_POOL_EXECUTOR难道是所有AsyncTask共用的吗?谢谢。

drogbababa avatar Nov 14 '19 12:11 drogbababa