您的位置:首页 > 移动开发 > Android开发

Android_AsyncTask详解及其应用(二 )_RejectedExecutionException问题

2013-10-16 17:50 627 查看
http://blog.csdn.net/wangjinyu501/article/details/9008219

在这篇文章里面说了AsyncTask的基本用法,想要进一步了解它是如何运行的朋友可以看一下这篇文章:点击,里面详细介绍了AsyncTask的原理以及运行流程。之前只是使用AsyncTask下载一张图片,显然在一个应用中不太现实,如果是很多图片,那使用AsyncTask会不会出现问题呢?下面来试一下,让ListView显示多张图片,使用AsyncTask下载图片。

代码如下:

[java] view
plaincopy

package com.example.asynctaskdemo;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.net.HttpURLConnection;

import java.net.MalformedURLException;

import java.net.URL;

import android.app.Activity;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.os.AsyncTask;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.BaseAdapter;

import android.widget.ImageView;

import android.widget.ListView;

public class Asy4Activity extends Activity {

private ListView listview;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_handlerimageloader);

listview = (ListView) findViewById(R.id.listview);

listview.setAdapter(new MyAdapter());

}

private class MyAdapter extends BaseAdapter {

public MyAdapter() {

}

@Override

public int getCount() {

// TODO Auto-generated method stub

return 100;

}

@Override

public Object getItem(int position) {

// TODO Auto-generated method stub

return null;

}

@Override

public long getItemId(int position) {

// TODO Auto-generated method stub

return 0;

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

if (convertView == null) {

convertView = LayoutInflater.from(getApplicationContext())

.inflate(R.layout.list_item, null);

}

final ImageView image = (ImageView) convertView

.findViewById(R.id.imageview);

String imageURL = "http://images.ladymax.cn/userup/1305/29111RO915.jpg";

class AsyncTaskLoadImage extends AsyncTask<String, Integer, Bitmap> {

@Override

protected void onPreExecute() {

super.onPreExecute();

}

@Override

protected Bitmap doInBackground(String... params) {

Bitmap bitmap = null;

try {

URL url = new URL(params[0]);

HttpURLConnection urlConnection = (HttpURLConnection) url

.openConnection();

urlConnection.connect();

int MAX = urlConnection.getContentLength();

InputStream inputStream = urlConnection

.getInputStream();

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

byte[] b = new byte[1024];

int len = 0;

while ((len = inputStream.read(b)) != -1) {

byteArrayOutputStream.write(b, 0, len);

}

bitmap = BitmapFactory.decodeByteArray(

byteArrayOutputStream.toByteArray(), 0, MAX);

inputStream.close();

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

return bitmap;

}

@Override

protected void onPostExecute(Bitmap result) {

super.onPostExecute(result);

image.setImageBitmap(result);

}

}

AsyncTaskLoadImage asyncTaskLoadImage = new AsyncTaskLoadImage();

asyncTaskLoadImage.execute(imageURL);

return convertView;

}

}

}

在这里让ListView显示100张图片,如图:



出现了两个异常,一个是:



这个异常很常见,就是经常说的OOM,内存溢出了,原因也是因为代码里并没有做OOM问题的优化处理,比如使用缓存、分页加载、重用等等。再看一下第二个异常:



那这个错误是什么呢?其实原因是当运行的AsyncTask 实例数量过多的时候会引发RejectedExecutionException异常。例如网络情况较差时,有大量的线程在等待。那如何解决这个问题呢?

思路:

既然抛出了RejectedExecutionException这个异常,那就从它开始。翻看java的在线文档RejectedExecutionException,我们看到了这个错误的解释:

[java] view
plaincopy

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方法:

[java] view
plaincopy

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这个属性,这样来到它赋值的地方

[java] view
plaincopy

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异常。

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

[java] view
plaincopy

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,不会再抛出那个异常了。

推荐博客: 1、Android AsyncTask behavior changes you should know

2、Android实战技巧:深入解析AsyncTask

3、AsyncTask 使用须知

4、Android多线程任务优化1:探讨AsyncTask的缺陷

5、解决java.util.concurrent.RejectedExecutionException问题
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐