2017-06-05 51 views
9

在我的应用程序有RecyclerView与吨it.Images图像加载为用户滚动RecyclerView与此代码:安卓:java.util.concurrent.ThreadPoolExecutor中

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) 
     loader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,url); 
    else 
     loader.execute(url); 

不幸的是,有时,当用户滚动这个快错误发生:

Task [email protected] rejected from 
[email protected][Running, pool size = 9, 
active threads = 9, queued tasks = 128, completed tasks = 279] 

有没有办法检测poolExecutor是否已满并跳过图像加载?

整个图像类:

public class Image extends ImageView { 
private AsyncTask<String,Integer,Bitmap> loader; 

public Image(Context context) { 
    super(context); 
    this.setScaleType(ScaleType.FIT_XY); 
} 

public Image(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    this.setScaleType(ScaleType.FIT_XY); 
} 

public void loadURL(String url) { 
    if(loader!=null) 
     loader.cancel(true); 
    loader=new AsyncTask<String, Integer, Bitmap>() { 
     @Override 
     protected Bitmap doInBackground(String... params) { 
      URL url = null; 
      byte[] bytes = null; 
      HttpURLConnection connection=null; 
      try { 
       url = new URL(params[0]); 
       connection=(HttpURLConnection) url.openConnection(); 
       connection.setRequestProperty("Connection", "close"); 
       connection.setRequestMethod("GET"); 
       connection.setUseCaches(true); 
       InputStream is = null; 
       is=connection.getInputStream(); 
       bytes = IOUtils.toByteArray(is); 
      } catch (MalformedURLException e) { 
       e.printStackTrace(); 
      } catch (ProtocolException e) { 
       e.printStackTrace(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      if (connection!=null) 
       connection.disconnect(); 
      Bitmap res=null; 
      if(!isCancelled() && bytes!=null) 
       res=BitmapFactory.decodeByteArray(bytes,0,bytes.length); 
      return res; 
     } 
     @Override 
     protected void onPostExecute(Bitmap res) { 
      if(res!=null) { 
       setImageBitmap(res); 
       _animate(); 
      } 
     } 
    }; 
    if (this.getDrawable()!=null) { 
     Bitmap bmp=((BitmapDrawable) this.getDrawable()).getBitmap(); 
     this.setAnimation(null); 
     if (bmp!=null) { 
      bmp.recycle(); 
      //Log.d("image","recycled"); 
     } 
     this.setImageBitmap(null); 
    } 
    /* 
    ThreadPoolExecutor e =(ThreadPoolExecutor) Executors.newFixedThreadPool(9); 
    Log.d("pool size",e.getActiveCount()+"/"+e.getMaximumPoolSize()); 
    if (e.getActiveCount() == e.getMaximumPoolSize()) { 
    } 
    */ 
    //start loading 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) 
     loader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url); 
    else 
     loader.execute(url); 
} 
private void _animate() { 
    ValueAnimator bgAnim= ValueAnimator.ofObject(new IntEvaluator(),0,255); 
    bgAnim.setDuration(500); 
    bgAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
     @Override 
     public void onAnimationUpdate(ValueAnimator animation) { 
      Image.this.getDrawable().setAlpha((int) (animation.getAnimatedValue())); 
     } 
    }); 
    bgAnim.start(); 
} 

}

+2

可能重复的[android应用程序崩溃时超过线程池9和排队的任务128](https://stackoverflow.com/questions/34395004/android-app-crashes-when-超线程池9和排队任务128) –

+1

而不是创建一个新的异步任务上传创建一个图像,只需创建一个并上传所有图像。 –

+0

它引发单个AsyncTask。 java.lang.IllegalStateException:无法执行任务:任务已在运行。 – undefined

回答

3

我只是relized我可以换加载代码的try/catch:

try { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) 
      loader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url); 
     else 
      loader.execute(url); 
    } catch (RejectedExecutionException e){ 
     e.printStackTrace(); 
} 

看起来这将是可选的解决方案。

4

您可以检查活动的线程数等于线程池的最大大小,然后你的线程池已满通过使用此

ThreadPoolExecutor e =(ThreadPoolExecutor)Executors.newFixedThreadPool(totalnofthreads); 
if (e.getActiveCount() == e.getMaximumPoolSize()) 
{ 

} 
+0

谢谢,'totalnofthreads'是什么? – undefined

+0

你的线程池的大小,因为它在你的问题logcat中显示9。 [email protected] [Running,pool size = 9,active threads = 9,queued tasks = 128,completed tasks = 279] –

+0

刚刚尝试e.getActiveCount()总是返回0,尽管有些活动线程。 – undefined

2

要检测用户是否快速滚动,可以使用onFlingListener()

recyclerView.setOnFlingListener(new RecyclerView.OnFlingListener() { 

    @Override 
    public boolean onFling(int velocityX, int velocityY) { 
     isFlinging = checkFlinging(velocityY); 

     if (isFlinging) { 
      //Stop image loading here 
     } 

     return false; 
    } 
}); 

private boolean checkFlinging(int velocityY) { 
    return (velocityY < 0 && velocityY < -RECYCLER_VIEW_FLING_VELOCITY) 
      || (velocityY > 0 && velocityY > RECYCLER_VIEW_FLING_VELOCITY); 
} 

让我解释一下,velocityY因为我用recyclerView与垂直滚动(移动水平滚动条只是这个参数更改为velocityX),RECYCLER_VIEW_FLING_VELOCITY - 你一扔速度,对我来说RECYCLER_VIEW_FLING_VELOCITY = 7000

2

而不是使用AsyncTask.THREAD_POOL_EXECUTOR的你可以用一个RejectedExecutionHandler使用自己的Executor实例,例如:

private final Executor mExecutor = new ThreadPoolExecutor(0, 8, 
      1, TimeUnit.SECONDS, 
      new LinkedBlockingQueue<Runnable>(16), 
      new ThreadPoolExecutor.DiscardPolicy()); 

这将创建一个运行最多8个线程的执行者,保持16级的任务队列中,并丢弃所有超出这些限制的任务(DiscardPolicy是预定义的RejectedExecutionHandler,完全是这样)。然后您可以将它传递给executeOnExecutor()

或者,您可能希望图像最终加载。在这种情况下,你可以使用带有无限(即无限制)队列的Executor。它永远不会抛出RejectedExecutionException。 Executors实用工具类中提供了创建这些一些不错的工厂方法:

private final Executor mExecutor = Executors.newFixedThreadPool(8); 
6

我之前回答(herehereherehere和可能其他人),我再为您一一解答:不要试图重新发明车轮!

图像加载/缓存是一个非常复杂的任务在Android和许多很好的非常开发人员已经这样做。线程只是其中一个问题,但我可以从你的代码中看到你有内存泄漏,没有缓存,所以如果再次滚动它,你会重新下载图像,HttpURLConnection是一个糟糕的网络层。

所以,解决这个问题的方法(恕我直言)它只是重用其他开发人员完成的工作。你应该考虑库的很好的例子对于那些:

毕加索是我最喜欢的,所以使用它,你需要只需拨打电话:

Picasso.with(context).load(url).into(imgView); 

这一切都是手带领你。

+0

你在哪里看到内存泄漏? 我用这条线连接缓存图像。setUseCaches(true);'我看不到其他问题,我已经用try/catch解决了这个问题 – undefined

+0

@undefined AsyncTask有一个引用ImageView的引用Activity引用的上下文。线程正在运行,Activity被破坏 - >内存泄漏。 – Budius

+0

我在AsyncTask中看不到引用'ImageView'(除了this引用不会导致泄漏) – undefined

2

时的ImageView从窗口分离,取消载入网址的相应任务:

public class Image extends ImageView { 

    @Override protected void onDetachedFromWindow() { 
    super.onDetachedFromWindow(); 
    if(loader!=null) 
     loader.cancel(true); 
    } 

} 
1

首先,你为什么还在使用的AsyncTask? ThreadPool异常即将到来,因为当您快速滚动适配器尝试将图像设置为不再可用的位置时,通常会停止此问题,您将禁用回收,但这只会在处理大量数据时使列表变慢。所以我建议你使用volley进行图像加载,很容易实现,并且很容易处理缓存。

 <com.android.volley.toolbox.NetworkImageView 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:id="@+id/mainImage" 
      android:scaleType="centerCrop" 
      android:adjustViewBounds="true" 
      android:maxHeight="270dp" 
      /> 

到位您的ImageView的使用上面,并创建volleySingleton类来处理所有的网络请求

public class VolleySingleton { 
private static VolleySingleton sInstance = null; 
private RequestQueue mRequestQueue; 
private ImageLoader imageLoader; 

private VolleySingleton(){ 
    mRequestQueue = Volley.newRequestQueue(Application.getAppContext()); 
    imageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() { 
     private final LruCache<String, Bitmap> cache = new LruCache<String, Bitmap>(200); 


     @Override 
     public Bitmap getBitmap(String url) { 
      return cache.get(url); 
     } 


     @Override 
     public void putBitmap(String url, Bitmap bitmap) { 
      cache.put(url, bitmap); 
     } 

    }); 

} 
public static VolleySingleton getsInstance(){ 
    if(sInstance == null){ 
     sInstance = new VolleySingleton(); 
    } 
    return sInstance; 
} 
public RequestQueue getmRequestQueue(){ 
    return mRequestQueue; 
} 
public ImageLoader getImageLoader() { 
    return imageLoader; 
} } 

让你的单例类的实例,然后将其添加到ImageView的和你的好去

 imageLoader = VolleySingleton.getsInstance().getImageLoader(); 
    networkImageVeiw.setImageUrl(imageUrl, imageLoader); 

创建扩展android.app.Application类,所以你可以在volleySingleton类获取上下文

public class Application extends android.app.Application { 
private static Application sInstance; 

public static Application getsInstance() { 
    return sInstance; 
} 

public static Context getAppContext() { 
    return sInstance.getApplicationContext(); 
} 

public static AppEventsLogger logger(){ 
    return logger; 
} 

@Override 
public void onCreate() { 
    super.onCreate(); 
    sInstance = this; 
} } 

不要忘了去你的manifest.xml和name属性添加到应用程序标签是你刚才扩展

<application 
    android:name="com.example.ApplicationClass" 

在这里你的应用程序类的名称是一个链接以获取关于安装排除和一些有用的技巧的指示volley library here

+0

为什么你认为'AsyncTask'太糟糕了?我认为这些失败的请求会出现在屏幕上不存在的图像上,所以我可以用try/catch来跳过它们。如果你知道'AsyncTask'的其他任何缺点,我会喜欢听它。 – undefined

+0

你可以使用try catch,但是如果你没有明确地处理异常,但只是打印栈跟踪,为什么不避免它发生,除非代码明确不能没有try catch写入。除此之外,我对AsyncTask没有任何反应,只是它在这种情况下并不是最好的使用,也许当android不像现在那样发展的时候,它已经很有用了,但现在有更好的工具来模拟你的网络,这些工具是异步的, 。如果他插入一个数据库,那么AsyncTask将是最好的解决方案。 – Smilecs

+0

只是为了纠正一个印象,AsyncTask是数据库操作的最佳选择。目前建议使用加载器,因为如果AsyncTasks使用不当可能会导致内存泄漏。 – Smilecs