AsyncTask缺点,排队原理及替代方案

本文目的

阐述AsyncTask应用的缺点,通过代码分析其内部任务执行的排队原理,线程池原理。并且在文章末尾提出一个针对AsyncTask的替代建议。

AsyncTask介绍

AsyncTask是Android LEVEL 3引入的用于执行异步任务的工具类,经常被用来执行子线程任务。因为其使用方法非常简单,并且内部有着良好的线程池管理机制,因此在过往的项目中使用较为频繁。
简要用法举例

new AsyncTask(){
    @Override
    protected Object doInBackground(Object[] objects) {
        //执行耗时任务
        ……
        return null;
    }
}.execute();

AsyncTask缺点盘点

其存在的缺点盘点如下

序号缺点描述应对方案注意事项
1在MainActivity,Fragment有生命周期的类中创建AsyncTask对象会引起内存泄漏使用弱引用包裹AsyncTask或者即将执行的对象,或者使用静态内部类重写AsyncTask弱引用对象在系统GC时会被回收,可能导致待执行的异步任务未被执行
2在视图层基于软引用包裹创建AsyncTask对象后,当画面关闭后异步任务可能还在执行。任务完成后如果内部调用了view对象,则会产生空指针的风险在页面退出时,主动取消AsyncTask待执行的任务 ; 内部调用view对象前先判空注意事项:所谓取消任务只是通过return提前结束方法,但是正在进行的耗时操作无法停止,比如解码
3AsyncTask执行默认使用的是单线程执行,当短时间创建的AsyncTask任务过多时,会产生任务排队的情况可以适当采用AsyncTask多线程的execute方法

AsyncTask任务排队原理

以文章开头举例的例子为例,请内部使用的是单线程执行任务队列

//调用执行方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
//使用sDefaultExecutor执行任务
    return executeOnExecutor(sDefaultExecutor, params);
}

那么看下sDefaultExecutor创建过程,可以看到,该情况下AsyncTask内部创建了一个单线程对象

@UnsupportedAppUsage
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
……
@Deprecated
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

// SerialExecutor对象
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;
    ……
    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
//执行任务
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

// THREAD_POOL_EXECUTOR创建过程
private static final int CORE_POOL_SIZE = 1;
@Deprecated
public static final Executor THREAD_POOL_EXECUTOR;

static {
//创建的线程池大小为1
    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;
}

看看AsyncTask内部是如何进行任务的队列管理的,所有添加的任务均加入了一个任务队列,按照先进先出的方式进行队列任务管理。每一个添加的任务中均设置了自动执行下一个任务,这样实现了任务的排队。相应代码的介绍如下

private static class SerialExecutor implements Executor {
    //管理待执行任务的队列,执行原则是先进先出
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    //当前处理的任务
    Runnable mActive;
    
    public synchronized void execute(final Runnable r) {
        //offer方法将在mTasks任务队列的队尾添加新增任务,即进入排队环节
        mTasks.offer(new Runnable() {
            public void run() {
                //所有排队的任务的逻辑上执行结束后自动切换至下一个任务执行
                try {
                    //执行该任务
                    r.run();
                } finally {
                    //最终需执行下一个任务
                    scheduleNext();
                }
            }
        });
        //如当前无执行的任务,直接执行下一个
        if (mActive == null) {
            scheduleNext();
        }
    }

    //执行下一个任务
    protected synchronized void scheduleNext() {
        //poll方法将取出队列的头部的第一个任务,并出队
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

AsyncTask替代方案

在AsyncTask源码的官方注释中,提供了其替代方案的建议。相应提示如下


* <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
* and does not constitute a generic threading framework. AsyncTasks should ideally be
* used for short operations (a few seconds at the most.) If you need to keep threads
* running for long periods of time, it is highly recommended you use the various APIs
* provided by the <code>java.util.concurrent</code> package such as {@link Executor},
* {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
*

基于ThreadPoolExecutor采用单例模式实现线程池,实现代码如下。避免内存泄漏需要避免在有生命周期的View层直接使用,建议在Model层使用

public class ThreadExecutor extends ThreadPoolExecutor {
    private static final int CORE_POOL_SIZE = 1;
    //以CPU总数*2作为线程池上限
    private static final int MAXI_MUM_POOL_SIZE = Runtime.getRuntime().availableProcessors() *2;
    private static final int KEEP_ALIVE_TIME = 3;
    private static ThreadExecutor executor;

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

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

    public ThreadExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    //单例模式
    public static ThreadExecutor getInstance(){
        if(null == executor){
            synchronized (ThreadExecutor.class){
                if(null == executor){
                    executor = new ThreadExecutor(CORE_POOL_SIZE , MAXI_MUM_POOL_SIZE , KEEP_ALIVE_TIME , TimeUnit.SECONDS , new SynchronousQueue<Runnable>() , sThreadFactory);;
                }
            }
        }
        return executor;
    }
}

使用方法示例

ThreadExecutor.getInstance().execute(new Runnable() {
    @Override
    public void run() {
        //执行任务
        Picture picture = new Picture();
        picture.setName(names[0]+System.currentTimeMillis());
        picture.setUrl(pictures[0]);
        PictureHelper.getInstance().insert(picture);
    }
});

学习心得

通过深入学习AsyncTask源码学习了线程池的用法,以及多线程任务的队列管理机制。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值