好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

解决AsyncTask引发的RejectedExecutionException

解决AsyncTask引发的RejectedExecutionException

解决AsyncTask引发的RejectedExecutionException

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           @Override
  11           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           @Override
  21           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 either
  12        *         {  @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      }

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

  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 执行。

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

 

 

分类:  android性能优化

标签:  AsyncTask ,  android

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于解决AsyncTask引发的RejectedExecutionException的详细内容...

  阅读:39次