2010-09-19 35 views
2

我正在尝试为设备和网络上的图像创建一个懒惰的图像加载器,用于ListView。 我在想如何使用和如何,一方面我可以使用一个线程来集中我的请求(总是运行,并且我可以附加一个视图和一个适配器,它将处理为我加载的图像),缓存我的已经加载的图像,并在我加载之前检查图像的可见性,所以我不会做不需要的工作。许多AsyncTasks产卵的AsyncTask性能问题。

我有另一个想法使用AsyncTask像在论坛上的许多建议。但有一个缺点。我看到很多人使用new MyTask().execute(urls);,如果我想开始加载并停止按需加载图像,则会出现问题。 如果我使用每个图像的异步任务,那么我需要为每个图像创建一个新的异步任务,这是很多“新”,我可以使用一个池,但是如果太多的异步任务卡住了,我仍然会创建大约150个-200 asyc任务,对我来说太多了......

你们认为什么?我认为,一个线程会在这里做一个更好的工作:

  1. 继续运行,直到被杀
  2. 尝试从队列中的作业,如果不工作,等待。
  3. 如果有工作可用,请获取并开始处理。
  4. 连续处理每个请求并阻塞该线程。
  5. 一旦继续'2'。
  6. 对于需要显示的视图,由适配器使用startLoadingImage()完成的每个排队都会创建一个新的作业并在等待锁上调用通知。

如果我想要多个并行GET \ POST请求,我可以使用线程池优化此代码。 此外,我正在缓存我已下载的图像\为下次访问时的快速加载而加载。这个想法是最小化GC并列出滞后。

回答

2

我实现了这样的事情:

/** Contains all the pending requests for thumbnails. */ 
private LinkedList<Uri> mPendingThumbnailRequests = new LinkedList<Uri>(); 

private ThumbnailGetter mThmGetter = null; 
/** 
* Asynchronous process for retrieving thumbnails from Uris. 
*/ 
private class ThumbnailGetter extends AsyncTask<Uri, Integer, Uri> { 
    private final String LOG_TAG = ThumbnailGetter.class 
      .getSimpleName(); 
    /** The Uri beeing processed */ 
    private Uri mUri = null; 
    /* 
    * (non-Javadoc) 
    * 
    * @see android.os.AsyncTask#doInBackground(Params[]) 
    */ 
    @Override 
    protected Uri doInBackground(Uri... uris) { 
     // We process Uris one after another... so the Array contains 
     // only one Uri. 
     mUri = uris[0]; 
     // Let the ThumbnailLoader do the job. 
     Uri result = ItemsLoader.getThumbnail(mContext, mUri); 
     return result; 
    } 
    /* 
    * (non-Javadoc) 
    * 
    * @see android.os.AsyncTask#onPostExecute(java.lang.Object) 
    */ 
    @Override 
    protected void onPostExecute(Uri result) { 
     super.onPostExecute(result); 
     // Give the retrieved thumbnail to the adapter... 
     mImageAdapter.updateThumbUri(mUri, result); 
     // then process any other pending thumbnail request. 
     if (!mPendingThumbnailRequests.isEmpty()) { 
      mThmGetter = new ThumbnailGetter(); 
      mThmGetter.execute(mPendingThumbnailRequests.poll()); 
     } 
    } 
} 

我再加入尤里斯使用加载:

if (!mPendingThumbnailRequests.contains(imageUri)) { 
    mPendingThumbnailRequests.offer(imageUri); 
    if (mThmGetter == null 
      || mThmGetter.getStatus() == AsyncTask.Status.FINISHED) { 
     // If the previous instance of the thumbnail getter has 
     // finished, start a new one. 
     mHandler.sendEmptyMessage(MSG_SHOW_INDETERMINATE_PROGRESS); 
     mThmGetter = new ThumbnailGetter(); 
     mThmGetter.execute(mPendingThumbnailRequests.poll()); 
    } 
} 

这甚至让你取消使用mPendingThumbnailRequests.remove()

请求全面实施是在这里: http://code.google.com/p/emailalbum/source/browse/EmailAlbumAndroid/trunk/src/com/kg/emailalbum/mobile/creator/SelectPictures.java

+0

这段代码绝对不错,但是如果我考虑一个带有图像的列表,它以未知的速度和批量加载它们(因为用户可以像他喜欢的那样快速或慢速地滚动),你会获得相对于你自己的线程创建的许多AyncTasks它会创建一次并入睡,直到你排入一个新对象为止,对于他来说这是一个更多的内存,但是如果你使用自己的池并避免在run()函数中创建新对象,那么它就是多余的,而不是GC。 – codeScriber

+0

当我们重复使用现有的AsyncTasks时,将不会创建许多AsyncTasks。那么,实际上,当用户开始滚动时,它会重新创建一个新的缩略图,并且有一些缩略图可供检索......但这只是滚动开始时的一个分配问题。我从来没有喜欢睡觉线程和担心更多关于那些永不停止或重复的线程。 –

+0

嘿,我刚刚发现你是对的,我为每个Uri创建了一个新的AsyncTask ...我将在此上工作。感谢您向我展示这一点。 ;-) –

1

我认为你在做过早的优化。尽可能以最快的方式实现你所需要的东西,之后你总能改进实施。另外为什么你需要同时启动200个AsyncTasks?我不认为你要在一个屏幕上显示所有图像(并且在ListView的情况下,为什么加载所有的图像,即使用户永远不能滚动到列表的末尾?)。

+0

不,那就是要点用户可以滚动到最后,所有图像都会预装一个默认图像,例如“等待加载”等,一旦图像被加载,视图被通知并且图像被放置在正确的视图中。一旦缓存,加载的响应时间很快,因此不需要异步执行。 关于200个异步任务,这只是一个例子,说明为什么我不能使用它... – codeScriber

+0

我这样做了,现在我得到了java.util.concurrent。RejectedExecutionException如果滚动速度过快,并且任务按正常顺序滚动,则会发生错误。首先尝试有效地做它可能是最容易的。 – yingted