我在某处读过(并观察过)启动线程很慢。我总是假设AsyncTask创建并重用了一个线程,因为它需要在UI线程内启动。
从ListAdapter的getView方法调用以下(匿名)代码,以异步方式加载图像。它很有效,直到用户快速移动列表,然后变为“janky”。
final File imageFile = new File(getCacheDir().getPath() + "/img/" + p.image);
image.setVisibility(View.GONE);
view.findViewById(R.id.imageLoading).setVisibility(View.VISIBLE);
(new AsyncTask<Void, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(Void... params) {
try {
Bitmap image;
if (!imageFile.exists() || imageFile.length() == 0) {
image = BitmapFactory.decodeStream(new URL(
"http://example.com/images/"
+ p.image).openStream());
image.compress(Bitmap.CompressFormat.JPEG, 85,
new FileOutputStream(imageFile));
image.recycle();
}
image = BitmapFactory.decodeFile(imageFile.getPath(),
bitmapOptions);
return image;
} catch (MalformedURLException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
return null;
} catch (IOException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(Bitmap image) {
if (view.getTag() != p) // The view was recycled.
return;
view.findViewById(R.id.imageLoading).setVisibility(
View.GONE);
view.findViewById(R.id.image)
.setVisibility(View.VISIBLE);
((ImageView) view.findViewById(R.id.image))
.setImageBitmap(image);
}
}).execute();
我认为基于队列的方法会更好用,但我想知道是否有一个或者我是否应该尝试创建自己的实现。
答案 0 :(得分:1)
基本上你必须跟踪listView的滚动状态并通知 当适配器准备好与刚刚可见时做一些缓慢的事情时 项目
另请注意,将图像保存到磁盘是一个非常缓慢的过程。用一个 基于内存的缓存策略可以提高你的性能 应用。
答案 1 :(得分:1)
我可以看到您解码图像并将其压缩回磁盘,之后再次对其进行解码。我认为不是很有效。您可以将流从网络保存到磁盘,然后解压缩。这只是一次解压缩而不是3次压缩/解压缩。将为您节省大量的CPU处理时间。
我认为AsyncTask会为多个图像创建多个线程。因此,几个图像同时被压缩/解压缩,几个线程争夺CPU时间,不是很好。据我所知,AsyncTask使用线程池,因此它不会为每个图像启动新线程。但无论如何,同时几个线程并不是那么好。我同意队列会更有效。实现并不是很难自己创建它。我使用自己的队列实现,我很满意。
如果你有自己的主题,我认为可以给它一个较低的优先级。这将使UI更具响应性。
您当然需要某种内存缓存,否则UI不够快。解压缩很慢。您不能存储所有图像,只能存储最常用的图像。您可以使用SoftReference进行缓存实现。您可以使用inSampleSize选项使您的位图更小并占用更少的内存Strange out of memory issue while loading an image to a Bitmap object。
我做了一个完整的LazyList示例并发布了源代码,也可能有用Lazy load of images in ListView。