文档章节

解决AsyncTask引发的RejectedExecutionException

今日竹石
 今日竹石
发布于 2014/03/17 12:54
字数 1874
阅读 2457
收藏 6

AsyncTask是google为易用和有效的异步操作UI线程的所开发的一个封装类。使用者可以很轻易的使用其进行后台操作,然后将结果传给UI线程,而不需要使用Thread和Handler。

这样好用的一个类,显然可以在ListView异步加载图片时大显身手,本着这样的想法,笔者瞬间就完成了一段这样的模拟代码:

Adapter的getView方法:

复制代码

 1         @Override 2         public View getView(int pos, View view, ViewGroup viewGroup) { 3             if (view == null) { 4                 view = getLayoutInflater().inflate(R.layout.test2_list_item, 5                         null); 6             } 7             ImageView imageView = (ImageView) view.findViewById(R.id.imageView); 8             //这里每次都new出一个新的AsyncTask,进行执行。 9             new TestAsyncTask(imageView, pos).execute(new Object());10             TextView itemView = (TextView) view.findViewById(R.id.itemView);11             itemView.setText("测试数据" + pos);12             return view;13         }

复制代码

TestAsyncTask:

复制代码

 1 private class TestAsyncTask extends AsyncTask { 2         private ImageView imageView; 3         private int index; 4  5         public TestAsyncTask(ImageView imageView, int index) { 6             this.imageView = imageView; 7             this.index = index; 8         } 9 10         @Override11         protected Object doInBackground(Object... arg0) {12             // 模拟长时间的运算;大多数情况下是等待进行网络请求。13             long sum = 0;14             for (int i = 0; i <= 10000 * 100; i++) {15                 sum += i * 1l;16             }17             return null;18         }19 20         @Override21         protected void onPostExecute(Object result) {22             //模拟已经获得了网络请求的图片23             imageView.setImageBitmap(BitmapFactory.decodeResource(24                     getResources(), R.drawable.image01, null));25         }26     }

复制代码

运行调试,图片一个一个全都加载出来,没有问题,正当我疯狂的向下翻动着,妄图享受一下列表迅速滑动的快感的时候,一个无情的错误弹了出来。

04-17 11:22:52.009: E/AndroidRuntime(22792): FATAL EXCEPTION: main
04-17 11:22:52.009: E/AndroidRuntime(22792): java.util.concurrent.RejectedExecutionException: pool=128/128, queue=10/10
04-17 11:22:52.009: E/AndroidRuntime(22792): at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1961)
04-17 11:22:52.009: E/AndroidRuntime(22792): at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:794)
04-17 11:22:52.009: E/AndroidRuntime(22792): at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1315)

好吧,现在我理解为啥网络上流传的异步加载图片是用更加繁琐的Thread加Handler形式,而不用AsyncTask了。

难道是AsyncTask有什么用法上的问题,从出错结果来看貌似是达到了某个上限被拒绝了。那我们就从这个java.util.concurrent.RejectedExecutionException错误入手。

翻看java的在线文档RejectedExecutionException,我们看到了这个错误的解释

复制代码

public class RejectedExecutionException
extends RuntimeException
Exception thrown by an Executor when a task cannot be accepted for execution. Since: 1.5 See Also: Serialized Form

复制代码

这个表明是Executor这个类不接受执行task时抛出的异常。而Executor是java1.5之后增加的java.util.concurrent包中操作多线程的主要类。可以执行多个RunnableTask,看来google的AsyncTask就是用的这套API。

了解到这点,我们就可以打开AsyncTask的源码查看它到底是怎么实现的:

打开源码之后,找到execute方法: 

复制代码

 1     /** 2      * Executes the task with the specified parameters. The task returns 3      * itself (this) so that the caller can keep a reference to it. 4      * 5      * This method must be invoked on the UI thread. 6      * 7      * @param params The parameters of the task. 8      * 9      * @return This instance of AsyncTask.10      *11      * @throws IllegalStateException If {@link #getStatus()} returns either12      *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.13      */14     public final AsyncTask<Params, Progress, Result> execute(Params... params) {15         if (mStatus != Status.PENDING) {16             switch (mStatus) {17                 case RUNNING:18                     throw new IllegalStateException("Cannot execute task:"19                             + " the task is already running.");20                 case FINISHED:21                     throw new IllegalStateException("Cannot execute task:"22                             + " the task has already been executed "23                             + "(a task can be executed only once)");24             }25         }26 27         mStatus = Status.RUNNING;28 29         onPreExecute();30 31         mWorker.mParams = params;32         sExecutor.execute(mFuture);33 34         return this;35     }

复制代码

 找到这里使用的ExecutorsExecutor这个属性,这样来到它赋值的地方

复制代码

 1     private static final String LOG_TAG = "AsyncTask"; 2  3     private static final int CORE_POOL_SIZE = 5; 4     private static final int MAXIMUM_POOL_SIZE = 128; 5     private static final int KEEP_ALIVE = 10; 6  7     private static final BlockingQueue<Runnable> sWorkQueue = 8             new LinkedBlockingQueue<Runnable>(10); 9 10     private static final ThreadFactory sThreadFactory = new ThreadFactory() {11         private final AtomicInteger mCount = new AtomicInteger(1);12 13         public Thread newThread(Runnable r) {14             return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());15         }16     };17 18     private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,19             MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

复制代码

可以看到他是构造了一个ThreadPoolExecutor常量,保证new出多个AsyncTask都是使用这一个Executor。异常应该是它抛出的,我们看下这个类的文档,其中有一段是这样描述的:

复制代码

Rejected tasks
 New tasks submitted in method execute(Runnable) will be rejected when the Executor has been shut down, and also when the Executor uses finite bounds for both maximum threads and work queue capacity, and is saturated. In either case, the execute method invokes the rejectedExecution(Runnable, ThreadPoolExecutor) method of its         RejectedExecutionHandler. Four predefined handler policies are provided:
  1.In the default ThreadPoolExecutor.AbortPolicy, the handler throws a runtime RejectedExecutionException upon rejection.   2.In ThreadPoolExecutor.CallerRunsPolicy, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow    down the rate that new tasks are submitted.   3.In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped.   4.In ThreadPoolExecutor.DiscardOldestPolicy, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried     (which can fail again, causing this to be repeated.)
It is possible to define and use other kinds of RejectedExecutionHandler classes. Doing so requires some care especially when policies are designed to work only under particular capacity or queuing policies.

复制代码

 原来在Executor的队列和容量都达到最大时,再次增加新的task的时候将会进行拒绝行为,而默认的拒绝行为就是抛出这个RejectedExecutionException异常。

 看到这里顿时恍然了,原来初始化ThreadPoolExecutor没有指定拒绝行为,导致了异常的发生。google,你可以的!

 那解决方案也就诞生了,就是复制一份AsyncTask的源码,自己重写这个初始化方法,增加相应的拒绝策略,后面就有几个可供选择的策略。修改AsyncTask源码如下

1     private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,2             MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory, new ThreadPoolExecutor.DiscardOldestPolicy());

再次运行,OK,不会再抛出那个异常了。

其实当你看AsyncTask的google官方文档时,你会发现后面有这么一段:

Order of execution

When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.

也就是说刚开始,AsyncTask是被限制在一个线程里。从1.6开始,使用了线程池,允许运行多任务同时进行。而到了3.0之后,又被限制在一个线程里为了避免多线程执行的错误。

变3次了,你可以的,google。

为了验证这段话,瞅瞅3.0之后的AsyncTask的源码:

复制代码

    private static final String LOG_TAG = "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);    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();    private static final int MESSAGE_POST_RESULT = 0x1;    private static final int MESSAGE_POST_PROGRESS = 0x2;    private static final InternalHandler sHandler = new InternalHandler();    //这里我们发现到了这个默认的执行器,是下面实现的线性队列。    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;    private final WorkerRunnable<Params, Result> mWorker;    private final FutureTask<Result> mFuture;    private volatile Status mStatus = Status.PENDING;    
    private final AtomicBoolean mCancelled = new AtomicBoolean();    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();    
    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);
            }
        }
    }

复制代码

 这样我们也就可以根据3.0的源码修改我们的AsyncTask里的默认执行器,同样使用SerialExecutor保证只会启用一个线程。另外3.0的AsyncTask多了一个executeOnExecutor(java.util.concurrent.Executor, Object[])方法,如果你希望有多个任务同时启动,也可以使用THREAD_POOL_EXECUTOR执行。

该篇文章只是浅浅的探讨了一下使用AsyncTaskRejectedExecutionException问题解决,对于多线程理解多有不足之处,欢迎各位大牛拍砖。



    © 著作权归作者所有

    共有 人打赏支持
    今日竹石
    粉丝 41
    博文 227
    码字总数 181312
    作品 0
    朝阳
    程序员
    私信 提问
    加载中

    评论(1)

    z
    zhangfei_jiayou
    博主,你不能那么做的,无限制启动新的asynctask是个不合理的设计,要涉及成当list滑动停止后才让启动asynctask,并且只下载list可见区域的图片!手机性能有限,不能肆无忌惮的启动线程下载图片
    解决AsyncTask引发的RejectedExecutionException

    AsyncTask是google为易用和有效的异步操作UI线程的所开发的一个封装类。使用者可以很轻易的使用其进行后台操作,然后将结果传给UI线程,而不需要使用Thread和Handler。 这样好用的一个类,显...

    今日竹石
    2014/04/08
    0
    0
    Android AsyncTask

    简介: AsyncTask是android提供的轻量级的异步类。比Handler更轻量级一些,适用于简单的异步处理。 AsyncTask内部封装了Thread和Handler,简化Thread+Handler,可以让我们在后台进行计算并且把...

    SRain215
    2016/03/18
    54
    0
    Android多线程任务优化1:探讨AsyncTask的缺陷

    导语:在开发Android应用的过程中,我们需要时刻注意保障应用的稳定性和界面响应性,因为不稳定或者响应速度慢的应用将会给用户带来非常差的交互体验。在越来越讲究用户体验的大环境下,用户...

    Dean83
    2012/06/14
    0
    0
    AsyncTask的缺陷

    导语:在开发Android应用的过程中,我们需要时刻注意保障应用的稳定性和界面响应性,因为不稳定或者响应速度慢的应用将会给用户带来非常差的交互体验。在越来越讲究用户体验的大环境下,用户...

    鉴客
    2011/09/17
    52K
    22
    源码解析--AsyncTask

    1.概念 相信大家对AsyncTask都不陌生,对于执行耗时任务,然后更新UI是一把利器,当然也是替代Thread + Handler 的一种方式,如果你对Handler机制还不了解请看这篇文章: https://my.oschina...

    android-key
    2016/11/22
    4
    0

    没有更多内容

    加载失败,请刷新页面

    加载更多

    商品详情页上拉查看详情

    商品详情页上拉查看详情 目录介绍 01.该库介绍 02.效果展示 03.如何使用 04.注意要点 05.优化问题 06.部分代码逻辑 07.参考案例 01.该库介绍 模仿淘宝、京东、考拉等商品详情页分页加载的UI效...

    潇湘剑雨
    16分钟前
    0
    0
    Netty内存池之PoolArena详解

    PoolArena是Netty内存池中的一个核心容器,它的主要作用是对创建的一系列的PoolChunk和PoolSubpage进行管理,根据申请的不同内存大小将最终的申请动作委托给这两个子容器进行管理。整体上,P...

    爱宝贝丶
    20分钟前
    0
    0
    Django使用Channels实现WebSocket--下篇

    希望通过对这两篇文章的学习,能够对Channels有更加深入的了解,使用起来得心应手游刃有余 通过上一篇《Django使用Channels实现WebSocket--上篇》的学习应该对Channels的各种概念有了清晰的认...

    运维咖啡吧
    27分钟前
    1
    0
    linux下设置定时执行shell脚本的示例

    很多时候我们有希望服务器定时去运行一个脚本来触发一个操作,比如说定时去备份服务器数据、数据库数据等 不适合人工经常做的一些操作这里简单说下 shell Shell俗称壳,类似于DOS下的command...

    阿锋zxf
    31分钟前
    2
    0
    介绍Kubernetes监控Heapster

    什么是Heapster? Heapster是容器集群监控和性能分析工具,天然的支持Kubernetes和CoreOS,Kubernetes有个出名的监控agent—cAdvisor。在每个kubernetes Node上都会运行cAdvisor,它会收集本机...

    xiangyunyan
    32分钟前
    0
    0

    没有更多内容

    加载失败,请刷新页面

    加载更多

    返回顶部
    顶部