Android AsyncTask经典问题解析

转载请标明出处:http://blog.csdn.net/EdisonChang/article/details/50283969

AsyncTask,顾名思义,异步任务。相信热衷Android的你一定不陌生,对于AsyncTask的用法或者源码的解析,也有各路大神做了比较详细的分析,博主也从中受益很多,感兴趣的朋友,可以阅读[Android AsyncTask完全解析,带你从源码的角度彻底理解]

博文就不再做重复的解析介绍,这次博主分享的是使用AsyncTask过程中遇到的一些问题和解决方法,希望可以给有同样困惑的朋友带来解决思路。

从Android 3.0(HONEYCOMB_MR2 )开始,AsyncTask实现了SerialExecutor 这个类,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);
            }
        }
    }

代码实现非常简洁,从代码我们也可以看出,首次加入队列的任务会立即得到执行,然后必须等待任务执行完毕,才能在finally块中执行下一个任务,显然在3.0以后的AsyncTask的execute方法,默认实现的是串行操作。

3.0之前的AsyncTask同时可以执行5个任务,而3.0之后默认却只能有一个任务在执行,似乎有点不合理?庆幸的是更新后的AsyncTask提供了executeOnExecutor方法可以灵活的选择使用的线程池,这样就可以解决android 3.0以上使用单一线程池的问题。

所以通常情况下,我们会这么做,

public static void executeAsyncTask(final AsyncTask<Void,?,?> task){
        if(task != null){
            if(Build.VERSION.SDK_INT >= AndroidVersionCodes.HONEYCOMB){
                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
            }else{
                task.execute();
            }
                  }
    }

这样确实能够解决兼容问题,但是细心的朋友会发现,AsyncTask 中所使用的SERIAL_EXECUTORTHREAD_POOL_EXECUTOR都是一个static 对象,意味着进程中所有的new AsyncTask 对象都会运行在同一个线程池当中。倘若应用中的多个模块都用AsyncTask来执行异步任务,而且执行的任务都相对耗时,就会出现长时间排队的现象,这也是使用AsyncTask或者说是使用线程池处理异步任务的局限性。

无巧不成书,最近刚好碰到一个case ,拿出来和大家分享下,应用过程中一个偶现崩溃,堆栈如下,

java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask 3@424f6c90 rejected from java.util.concurrent.ThreadPoolExecutor@4248d7c8[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 9]
            at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2011)
            at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:793)
            at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1339)
            at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:590)

通常情况使用线程池过程中会有以下场景,
(1)如果运行的线程少于corePoolSize,则Executor始终首选添加新的线程,而不进行排队。
(2)如果运行的线程等于或多于corePoolSize,则Executor始终首选将请求加入队列,而不添加新的线程。
(3)如果无法将请求加入队列,则创建新的线程,当创建此线程超出maximumPoolSize时就会抛出RejectedExecutionException 异常信息。

从崩溃的堆栈信息也可以看出,在使用AsyncTask过程中由于排队异步任务过多,导致线程数超过了maximumPoolSize,从而导致异常。从这里可以看到使用AsyncTask的局限性,要求我们根据使用场景灵活应用,对于一些长时间耗时的任务,可以直接开辟一个线程运行,而不去占用进程中的公共线程池,避免导致阻塞。

当然,针对这一类的问题,也需要有亡羊补牢的办法,毕竟对于任务的耗时也依赖于各种上下文环境因素,并不是很容易预测的。

try {
                Object objectExecutor = cls.getField("THREAD_POOL_EXECUTOR").get(null);
                if (objectExecutor instanceof ThreadPoolExecutor) {
                    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) objectExecutor;
                    threadPoolExecutor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
                        @Override
                        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                            Executors.newSingleThreadExecutor().execute(r); 
                        }
                    });
                }
                java.lang.reflect.Method setDefaultExecutor = cls.getMethod("setDefaultExecutor", Executor.class);
                setDefaultExecutor.invoke(cls, objectExecutor);
            } catch (Exception e) {

            }

调用ThreadPoolExecutor 的setRejectedExecutionHandler 方法,当并发线程池满时,可以起一个单独的线程执行任务。

Executors.newSingleThreadExecutor().execute(r)

好了,到这里基本就介绍完了,AsyncTask虽然用起来很舒服,却不是万能的,一定要根据场景善用。有任何问题,欢迎补充指正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值