有人可以告诉我如何制定一个好的异步机制吗?下载图像以在 ListView/GridView 中使用?
有很多建议,但每个建议只考虑典型要求的一小部分。
下面我列出了一些我和我的同事无法立即满足的合理因素(要求或需要考虑的事项)。
我不要求代码(尽管它会受到欢迎),只是一种管理所描述的位图的方法。
- 没有重复的下载程序或位图
- 取消不再需要的图像的下载/分配,或可能会自动删除(软引用等)
- 注意:一个适配器可以具有同一 ID 的多个视图(对 getView(0) 的调用非常频繁)
- 注意:不能保证视图不会丢失而不是回收(考虑 List/GridView 调整大小或按文本过滤)
- 视图和视图的分离数据/逻辑(尽可能多)
- 不为每次下载启动单独的线程(UI 明显变慢)。使用队列/堆栈(BlockingQueue?)和线程池,或类似的东西......但如果活动停止则需要结束。
- 清除距离列表/网格中当前位置足够远的位图,最好仅当需要内存
- 对每个要丢弃的位图调用 recycle()。
- 注意:外部内存可能不可用(完全或始终),并且,如果使用,应清除(仅此处下载的图像)尽快(考虑 Android 的 Activity 销毁/重新创建)
- 注意:数据可以更改:条目被删除(多选和删除)和添加(在后台线程中)。已经下载的位图应该保留,只要它们链接到的条目仍然存在。
- setTextFilterEnabled(true) (如果基于 ArrayAdapter 的机制,将影响数组索引)
- 可在 ExpandableList 中使用(影响缩略图显示在)
- (可选)当下载位图时,仅刷新相关的 ImageView(列表项可能非常复杂)
请不要发布单个点的答案。我的问题是,我们越关注某些方面,其他方面就越模糊,海森堡式.
每一个都增加了一个维度的难度,尤其是 Bitmap.recycle,它需要在运行期间和 Activity 销毁时调用(注意 onDestroy,甚至 onStop 也可能不会被调用)。
这也排除了 依赖软引用。
这是必要的,否则即使经过任意次数的 gc、sleep 后我也会遇到 OutOfMemoryError (甚至 20 秒),在将位图清空后,在 try-catch 中进行yield 和巨大的数组分配(以强制受控的 OutOfMemory)。
我已经重新采样位图了。
Could someone tell me how to make a good mechanism for async. download of images for use in a ListView/GridView?
There are many suggestions, but each only considers a small subset of the typical requirements.
Below I've listed some reasonable factors (requirements or things to take into account) that I, and my collegues, are unable to satisfy at once.
I am not asking for code (though it would be welcome), just an approach that manages the Bitmaps as described.
- No duplication of downloaders or Bitmaps
- Canceling downloads/assigning of images that would no longer be needed, or are likely to be automatically removed (SoftReference, etc)
- Note: an adapter can have multiple Views for the same ID (calls to getView(0) are very frequent)
- Note: there is no guarantee that a view will not be lost instead of recycled (consider List/GridView resizing or filtering by text)
- A separation of views and data/logic (as much as possible)
- Not starting a separate Thread for each download (visible slowdown of UI). Use a queue/stack (BlockingQueue?) and thread pool, or somesuch.... but need to end that if the Activity is stopped.
- Purging Bitmaps sufficiently distant from the current position in the list/grid, preferably only when memory is needed
- Calling recycle() on every Bitmap that is to be discarded.
- Note: External memory may not be available (at all or all the time), and, if used, should be cleared (of only the images downloaded here) asap (consider Activity destruction/recreation by Android)
- Note: Data can be changed: entries removed (multi-selection & delete) and added (in a background Thread). Already downloaded Bitmaps should be kept, as long as the entries they're linked to still exist.
- setTextFilterEnabled(true) (if based on ArrayAdapter's mechanism, will affect array indexes)
- Usable in ExpandableList (affects the order the thumbnails are shown in)
- (optional) when a Bitmap is downloaded, refresh ONLY the relevant ImageView (the list items may be very complex)
Please do not post answers for individual points. My problem is that that the more we focus on some aspects, the fuzzier others become, Heisenberg-like.
Each adds a dimension of difficulty, especially Bitmap.recycle, which needs to be called during operation and on Activity destruction (note that onDestroy, even onStop might not be called).
This also precludes relying on SoftReferences.
It is necessary, or I get OutOfMemoryError even after any number of gc, sleep (20s, even), yield and huge array allocations in a try-catch (to force a controlled OutOfMemory) after nulling a Bitmap.
I am resampling the Bitmaps already.
发布评论
评论(2)
检查这个例子。由于 Google 使用它,我也使用相同的逻辑来避免 OutOfMemory 错误。
http://developer.android.com/resources/samples/XmlAdapters/index.html 基本上这个ImageDownlaoder
就是你的答案(因为它涵盖了你的大部分要求)你也可以在其中实现一些。
http://developer.android.com /resources/samples/XmlAdapters/src/com/example/android/xmladapters/ImageDownloader.html
Check this example. As Its is used by Google and I am also using the same logic to avoid OutOfMemory Error.
http://developer.android.com/resources/samples/XmlAdapters/index.html
Basically this ImageDownlaoder is your answer ( As It cover most of your requirements) some you can also implement in that.
http://developer.android.com/resources/samples/XmlAdapters/src/com/example/android/xmladapters/ImageDownloader.html
最后,我选择完全忽略回收错误。它只是在可管理的流程之上添加了一层不可能的难度。
没有这个负担(只是让适配器等停止显示图像),我使用
Map>
创建了一个管理器,将下载的位图存储在 URL 下。此外,2-4 个 AsyncTasks(同时使用 doInBackground 和 onProgressUpdate;通过添加抛出 InterruptedException 的特殊作业来停止)从
WeakHashMap< 支持的
。LinkedBlockingDeque>
获取作业;对象,设置deque(为早期 API 使用而复制的 LinkedBlockingDeque 代码)是一个队列,如果不再需要作业,它们可以离开该队列。该映射将作业创建者作为键,因此,如果适配器需要下载然后被删除,它将从映射中删除,因此,其所有作业都会从队列中消失。
如果图像已经存在,作业将同步返回。它还可以包含一组数据,可以识别它所涉及的 AdapterView 中的哪个位置。
缓存也可以在 SD 卡(如果可用)上的 URLEncoded 名称下完成。 (部分清理,从最旧的开始,在应用程序启动时,和/或使用deleteOnExit()
如果我们有缓存版本,请求包括“If-Modified-Since”,以检查更新。
同样的东西也可以用于 XML 解析和大多数其他数据获取。
如果我清理了该类,我将发布代码。
In the end, I chose to disregard the recycling bug entirely. it just adds a layer of impossible difficulty on top of a manageable process.
Without that burden (just making adapters, etc stop showing images), I made a manager using
Map<String, SoftReference<Bitmap>>
to store the downloaded Bitmaps under URLs.Also, 2-4 AsyncTasks (making use of both doInBackground and onProgressUpdate; stopped by adding special jobs that throw InterruptedException) taking jobs from a
LinkedBlockingDeque<WeakReference<DownloadingJob>>
supported by aWeakHashMap<Object, Set<DownloadingJob>>
.The deque (LinkedBlockingDeque code copied for use on earlier API) is a queue where jobs can leave if they're no longer needed. The map has job creators as keys, so, if an Adapter demands downloads and then is removed, it is removed from the map, and, as a consequence, all its jobs disappear from the queue.
A job will, if the image is already present, return synchronously. it can also contain a Bundle of data that can identify which position in an AdapterView it concerns.
Caching is also done on an SD card, if available, under URLEncoded names. (cleaned partially, starting with oldest, on app start, and/or using deleteOnExit()
requests include "If-Modified-Since" if we have a cached version, to check for updates.
The same thing can also be used for XML parsing, and most other data acquisition.
If I ever clean that class up, I'll post the code.