Android AsyncTask 重难点解析

一、前言


在android开发中相信大家或多或少都有使用过AsyncTask来执行异步任务然后更新Ui,在AsyncTask刚出来的时候一度受到了大家的追捧,因为可以告别使用Thread+Handler的线程模式。但是在不同SDK版本上AsyncTask有着比较大的差异,今天我们就来分析一下AsyncTask的源码以及各版本之间的差异,让你彻底理解AsyncTask。

 

二、AsyncTask 使用方法


我们先一起回顾一下AsyncTask的基本用法,AsyncTask是一个抽象类,我们必须创建一个子类去继承它。

public class DownloadTask extends AsyncTask<String,Integer,Boolean>
{
    private static final String TAG=DownloadTask.class.getSimpleName();
    private ProgressDialog mDialog;
    private ImageView mImageView;
    private Context mContext;

    public DownloadTask(Context context,ProgressDialog dialog,ImageView imageView)
    {
        mContext=context;
        mDialog=dialog;
        mImageView=imageView;
    }
    /**
     * 在doInBackground前执行,该方法在主线程中执行
     * */
    @Override
    protected void onPreExecute()
    {
        super.onPreExecute();
        mDialog.show();
    }

    /**
     * 该方法会在工作线程中执行,在此方法中要添加程序是否被终止的判断逻辑
     * */
    @Override
    protected Boolean doInBackground(String... params)
    {
        String url=params[0];
        boolean flag=true;
        while(flag)
        {
            if(isCancelled())
            {
                return false;
            }
            int progress = downloadFile(url);
            publishProgress(progress);
            if(progress>=100)
            {
                flag=false;
            }
        }
        return true;
    }

    /**
     * 如果任务没有被取消,执行完doInBackground后会执行该方法
     * */
    @Override
    protected void onPostExecute(Boolean aBoolean)
    {
        mDialog.dismiss();
        if(isCancelled())
        {
            Log.i(TAG,"任务被取消!");
        }
        else
        {
            if (aBoolean)
            {
                //这里模拟把下载好的图片显示在ImageView上面
                mImageView.setImageResource(R.mipmap.ic_launcher);
                Toast.makeText(mContext, "下载成功!", Toast.LENGTH_SHORT).show();
            } else
            {
                Toast.makeText(mContext, "下载失败!", Toast.LENGTH_SHORT).show();
            }
        }
    }

    /**
     *如果在doInBackground方法中调用了publishProgress,就会执行该方法,该方法可以对UI进行更新操作
     * */
    @Override
    protected void onProgressUpdate(Integer... values)
    {
        mDialog.setProgress(values[0]);
    }

    /**
     * 在doInBackground中如果调用了cancel方法,则不执行onPostExecute方法而执行onCancelled方法
     * */
    @Override
    protected void onCancelled(Boolean aBoolean)
    {
        mDialog.dismiss();
        Log.i(TAG, "任务被取消!");
    }
}

DownloadTask 模拟一个下载图片任务,下载之前会显示进度条,进度条显示下载过程,下载完成以后把图片显示在 ImageView 上面并关闭进度条。需要注意的是 AsyncTack 的子类最好不要写在 Activity 的内部,容易造成内存泄漏,如果一定要写,请写出静态内部类并使用弱引用。AsyncTaskd 的 cancel 方法只是简单把标志位改为 true,最后使用 Thread.interrupt 去中断线程执行,但是这并不保证会马上停止执行,所以可以在doInBackground中使用isCancelled来判断任务是否被取消,如果被取消则停止任务。任务被取消以后不会调用onPostExeute方法,而是调用onCancelled方法,要在该方法里面做好任务被取消后的相关逻辑。在onPostExecute中也要判断任务是否被取消,虽然耗时任务已经执行完成,但是在用户依然选择了取消操作,那么接下来的逻辑就没必要执行下去,在本段例子中就是不用在把下载好的图片显示在ImageView上。接着我们使用execute来执行任务:

new DownloadTask(getApplicationContext(), mdialog, mImageView).execute(url);

 

三、AsyncTask 源码分析(android 8.0)


接下来我们就一起来看看AsyncTask的源码吧,我们对上面的代码进行跟踪。首先AsyncTask可以指定三个泛型参数<Params, Progress, Result>,Params为执行AsyncTask时需要传入的参数类型,Progress用于显示进度的类型,Result指定返回结果的类型。由于DownloadTask继承于AsyncTask,所以new DownloadTask时会先执行AsyncTask的构造方法。 因此我们就先来看一看 AsyncTask 构造函数中的源码,代码如下:

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

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

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

这段代码虽然看起来有点长,但实际上并没有任何具体的逻辑会得到执行,只是初始化了两个变量,mWorker和mFuture,并在初始化mFuture的时候将mWorker作为参数传入。mWorker是一个Callable对象,mFuture是一个FutureTask对象,这两个变量会暂时保存在内存中,稍后才会用到它们。

接着如果想要启动某一个任务,就需要调用该任务的 execute() 方法,因此现在我们来看一看 execute() 方法的源码,如下所示:

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

简单的有点过分了,只有一行代码,仅是调用了 executeOnExecutor() 方法,那么具体的逻辑就应该写在这个方法里了,我们接着看 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;
    }

方法很简单,首先判断当前的状态,如果是在运行当中或者已经任务结束都会抛出异常。接着就会执行 onPreExecute() 方法,是不是很眼熟?没错就是我们在 DownloadTask 中重写的方法,我们当时的注释是在 doInBackground() 前执行,现在我们知道这个方法在什么时候被回调了吧?接着把我们传入进来的参数(也就是url)赋值给 mWorker.mParams,mWorker 我刚刚已经看了其定义,内部有一个 Params 的数组来存放传入的参数。

接着我们就使用 exec.execute 方法了,这里的 exec 就是我们刚才传入的 sDefaultExecutor 对象,现在调用它的 exeute 方法并把我们在 AsyncTask 构造方法中创建的 mFuture 对象传入,还记得 mFuture 对象吧?它实现了 Runnable 接口。

我们来看看sDefaultExecutor对象的定义:

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

sDefaultExecutor 实际上就是 SerialExecutor 对象,SerialExecutor 实现了 Executor 接口,在SerialExecutor 内部定义了一个双端队列 ArrayDeque,ArrayDeque 的内部是使用数组形式来实现双端队列的,我们知道队列是 FIFO 的,只能在队头删除元素,队尾添加元素,而双端队列是在队头和队尾都能够删除和添加元素。需要注意的是 ArrayDeque 没有容量的限制,队列满了以后会自动进行扩充。

在 execute 方法接收一个 Runnable 对象,我们传入的 mFuture 正好实现了该接口,方法内部首先通过双端队列 ArrayDeque 的 offer 方法把一个新创建的 Runnable 对象入队。然后判断 mActive 是否为空?第一次执行肯定是为空的,所以会调用 scheduleNext 方法,scheduleNext 方法首先从双端队列中得到一个元素,如果不为空,就调用 THREAD_POOL_EXECUTOR.execute 方法,THREAD_POOL_EXECUTOR 是一个线程池,也就是说我们把从双端队列中得到的任务交给线程池去执行(接下来的代码都在工作线程中执行),那么肯定就要执行到 Runnable 对象的 run 方法,该方法中的内容就是调用 mFuture 中的 run 方法:

    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();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

该 run 方法为 FutureTask 类中的 run 方法,不要搞混淆了。这个方法我们不细讲,我们只看重点,方法中的 callable 就是我们在 AsyncTask 构造方法中创建 FutureTask 时传入的 mWorker 对象,我们知道 mWorker 是实现了 callable 接口的。然后调用了 mWorker 的 call 方法:

    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

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

还记得这段代码吗?这是 AsyncTask 构造方法中创建 WorkerRunnable 对象的代码。我们看它的 call 方法。首先将 mTaskInvoked 这个标志位设置为 true,然后设置了线程优先级,接着我们看见在这里回调了 doInBackground 方法,所以 doInBackground 方法是在子线程中执行的,doInBackground 方法中会有一个返回值,这个返回值会传入 postResult 方法中。

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

postResult() 中的逻辑就是获取一个 Message,然后发送该 Message 给 这个Handler 处理。我们看看这个 getHandler():

    private Handler getHandler() {
        return mHandler;
    }

    //构造方法中
    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;
        }
    }

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

这个 Handler 其实就是 InternalHandler,是在 AsyncTask 中的定义,很显然它是在主线程中定义的。在 Loop 从 MessageQueue 中获取到 Message 后会分发到 handleMessage 这个方法中来处理,我们可以看见在 Switch 代码块中有两个分支,一个是用来处理结果的,一个是用来更新进度的。我们先看处理结果的分支,处理结果的分支会调用 AsyncTask 中的 finish() 方法并把任务的结果传入。

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

finish() 方法中的逻辑很简单,判断用户是否调用了 cancel() 方法,如果调用了那么就回调 onCancelled() 方法,如果没有调用就回调 onPostExecute() 方法,这跟我们在讲 AsyncTask 基本用法时说的逻辑一样。最后标志任务状态为完成。(注意:以上代码是在主线程中执行)

我们接着看更新进度的分支,我们先想一下更新进度时在什么时候被调用?是不是在 doInBackground() 中调用了 publishProgress() 方法?所以我们先看看 publishProgress() 方法:

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

从 publishProgress() 方法我们知道在 doInBackground() 中调用了 publishProgress() 方法传入值后,如果任务没有被取消,那么就会发送一个 Message,接着就回到 handleMessage() 方法中的处理进度代码块,回调 onProgressUpdate() 方法,这时候我们就可以对进度条进行更新。

我们回到 FutureTask 类的 run() 方法中,当调用了 callable.call() 以后是会有一个返回值的,得到该返回值以后,我们使用 set(result) 方法把返回值保存。这样当使用 FutureTask 的 get() 方法就可以得到任务的结果了。由此我们可以看出 Callable、Runnable、Future 之间的关系,以及 FutureTask 是如何把这三者结合在一起的。我们看看 set() 方法:

    protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;
            U.putOrderedInt(this, STATE, NORMAL); // final state
            finishCompletion();
        }
    }

set() 方法中把传入的参数交给 outcome 保管,当调用 get() 方法时就把这个 outcome 返回。之后调用了 finishCompletion 方法:

    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (U.compareAndSwapObject(this, WAITERS, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

        done();

        callable = null;        // to reduce footprint
    }

该方法其他的我们都不用看,只要看倒数第二句,会回调 done() 方法,这个方法是不是很熟悉?没错,在创建 FutureTask 的时候我们重写了该方法。我们看看 done() 方法的实现:

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

done() 方法中就使用了 FutureTask 的 get() 方法得到返回结果,并传入 postResultIfNotInvoked() 方法中, postResultIfNotInvoked() 中我们判断 mTaskInvoked 是否为 false?还记得在创建 WorkerRunnable 对象时 call() 方法中我们把 mTaskInvoked 设置为了 true,mTaskInvoked 是 AtomicBoolean 类型。如果 mTaskInvoked 为 false 说明没有调用,那么我们就调用 postResult() 方法,如果为 true 则说明已经调用过了,不需要再调用。注意 done() 方法中的代码是在子线程中执行。

到目前为止,AsyncTask 的执行流程我们就跟踪解析完了,是不是很晕?我自己都晕了,没关系,画图一向很糟糕的我,决定画一张流程图帮助大家理解。

 

四、各版本AsyncTask之间的差异


各版本 AsyncTask 之间的差异主要集中在线程池的使用这一块。主要的分界点有2个,分为3个阶段。第一阶段是 3.0 以前的版本,第二是 3.0~4.4 版本的阶段,第三个是 4.4 版本以后的阶段。那我们一个一个阶段来看。

4.1 Android 3.0 之前版本的 AsyncTask

    private static final int CORE_POOL_SIZE = 5;
    private static final int MAXIMUM_POOL_SIZE = 128;
    private static final int KEEP_ALIVE = 1;

    private static final BlockingQueue<Runnable> sWorkQueue =
            new LinkedBlockingQueue<Runnable>(10);

    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 ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

在 3.0 版本以前 AsyncTask 只有1个线程池,核心线程数为5,最大线程数为128,任务队列容量为10。也就是说当线程池中的线程数量没到5个,那么有新的任务会直接启动一个核心线程来执行任务,如果线程池中的线程数量达到了5个,然后任务会被插入到任务队列中等待执行,要是任务队列也满了,就会判断线程池中的数量是否已经达到最大线程数128,如果没有达到就会立刻启动一个非核心线程来执行任务。如果线程数量已经达到线程池规定的最大值,那么就会拒绝执行该任务。也就是说该线程池最多能同时接纳138个任务,其中有128个任务可以同时执行。而且该版本只有一个execute(Params... params) 方法,说明不能自定义线程池来执行任务。

 

4.2 Android 3.0 ~ 4.4 之间版本的 AsyncTask

    private static final int CORE_POOL_SIZE = 5;
    private static final int MAXIMUM_POOL_SIZE = 128;
    private static final int KEEP_ALIVE = 1;

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

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
 //以下为新增部分
  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);
            }
        }
    }

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

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

这个版本的线程池与上个版本并没有什么不同,只是新增加了一个SerialExecutor,从代码中我们可以看到这是一个顺序执行任务的Executor,虽然最后任务还是交给了THREAD_POOL_EXECUTOR来执行,但是使用这个Executor可以保证任务时按先进先出的顺序来执行。同时新增加了executeOnExecutor(Executor exec,Params... params) 方法,这个方法被声明是public的并且接受一个Executor参数,说明我们可以自定义线程池来执行任务。如果使用execute() 方法的话,默认会使用SerialExecutor来执行任务。

 

4.1 Android 4.4 之后版本的 AsyncTask

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;

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

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

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

4.4版本以后的线程池数量改为了动态的,以双核心为例,先获取CPU的核心数为2,线程池的核心线程为3,最大线程数为5,而阻塞队列的容量变为了128。为什么会有这样的改动?可能谷歌公司觉得开启的线程数过多会影响效率吧。而阻塞队列从容量为10变为了128是一个很有意思的事情。在4.4以前的版本,如果已经达到了线程池的核心线程数5,切阻塞队列也达到了10,再有任务加入,就会启动新的非核心线程,也就是说只要同时又16个任务进入就会开启非核心线程。而现在需要132(3+128+1)个任务加入才会开启非核心线程。也就是说要开启新的线程的成本更大了。

 

五、AsyncTask 的各种坑

1.在Activity中定义AsyncTask导致内存泄漏
由于AsyncTask是Activity的内部类,所以会持有外部的一个引用,如果Activity已经退出,但是AsyncTask还没有执行完毕,那么Activity就无法释放导致内存泄漏。对于这个问题我们可以把AsyncTask定义为静态内部类并且采用弱引用。

2.各版本对AsyncTask的实现不一样
对于这个问题,可以自己扩展一下AsyncTask在其内部也对版本做出判断,对于不同版本做一些不同的处理。

3.不能及时取消任务
以4.4版本双核手机为例,如果用户在A界面发起5个任务,由于使用SerialExecutor来执行任务,那么任务将一个一个顺序执行,由于第一个任务执行时间过长,其他任务只能在队列中等待,导致阻塞,所以可以考虑把执行时间较短的任务优先加入。如果在第一个任务执行过程中,用户跳转到了B界面,而A界面发起的任务已经没有必要执行,所以我们要在Activity的生命周期结束的时候取消掉任务。如果任务没取消掉,B界面又发起新的任务,就会导致B界面的所有请求阻塞。如果有需要我们可以直接使用executeOnExecutor方法,然后直接使用THREAD_POOL_EXECUTOR线程池来执行,这样可以3个线程同时执行,并且在doInBackground方法中判断任务是否被取消,这样可以提高效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值