gpt4 book ai didi

android - 带有 ImageView 和 Executor Framework 的滞后 Listview

转载 作者:行者123 更新时间:2023-11-30 02:57:24 24 4
gpt4 key购买 nike

我有一个带有自定义项的 ListView,例如这个:

custom listview item

灰色方 block 是一个 ImageView。填充 ListView 的数据来自游标形式的数据库。但是图像并没有直接存储在数据库中,而是在SDCard中,数据库只保存了对它们的String引用。

一开始,我通过重写的 CursorAdapter 的 bindView() 回调方法将图像解码为位图:

 Bitmap bmp = BitmapFactory.decodeFile(imageLocation);
holder.imageHolder.setImageBitmap(bmp);

但是 ListView 滚动非常慢。所以我阅读了 Executor 框架并实现了它,用以下代码替换了以前的代码:

 ImageView imageView = holder.imageHolder;
asyncImageLoader.DisplayImage(imageLocation, imageView);

并创建 AsyncImageLoader 类。它在其构造函数中创建了一个线程池,最多有 5 个工作线程来处理发送到工作队列的 Runnable。然后,当我从自定义 CursorAdapter 调用 DisplayImage() 方法时,它会检查位置字符串是否包含 url。如果是,则将 ImageLoader Runnable 发送到线程池的工作队列。如果该位置包含“N/A”,则将默认图像设置为 ImageView。

当可用的工作线程处理 ImageLoader Runnable 时,SDCard 中的图像被解码为位图,并将 ImageDisplayer Runnable 发送到主线程的消息队列,以在 UI 中显示图像:

public class AsyncImageLoader {

ExecutorService executorService;
Handler handler = new Handler();

public AsyncImageLoader() {
this.executorService = Executors.newFixedThreadPool(5);
}

public void DisplayImage(String location, ImageView imageView) {
if(!location.matches("N/A")) {
queueImageDecoding(location, imageView);
} else {
imageView.setImageDrawable(imageView.getContext().getResources().getDrawable(R.drawable.not_available));
}
}

private void queueImageDecoding(String location, ImageView imageView) {
executorService.execute(new ImageLoader(location, imageView));
}

class ImageLoader implements Runnable {

private String location;
private ImageView imageView;

public ImageLoader(String location, ImageView imageView) {
this.location = location;
this.imageView = imageView;
}

@Override
public void run() {
Bitmap bmp = BitmapFactory.decodeFile(location);
handler.post(new ImageDisplayer(bmp, imageView));
}

}

class ImageDisplayer implements Runnable {

private Bitmap bitmap;
private ImageView imageView;

public ImageDisplayer(Bitmap bitmap, ImageView imageView) {
this.bitmap = bitmap;
this.imageView = imageView;
}

@Override
public void run() {
if(bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}

}

}

问题是我仍然在滚动 Laggy。如果我去掉 ImageLoader.run() 方法中的代码,滚动就完美了。该代码不应该在工作线程中处理吗?我在这里缺少什么?

更新

由于 ListView 中的 View 在滚动发生时被重用,从工作线程返回的位图在单个 ImageView 中设置了多次。所以可能的解决方案是:

  • 避免在 ListView 项已被重复使用时设置旧位图。
  • 或者更好的是,取消任务。

我正在使用 Future 对象取消任务。它存储在标记到自定义 CursorAdapter 内的项目 View 的容器中:

public class MyCustomAdapter extends CursorAdapter {

...
public AsyncImageLoader asyncImageLoader;

private static class ViewHolder {
ImageView imageHolder;
TextView text1Holder;
TextView text2Holder;
TextView text3Holder;
Button buttonHolder;
Future<?> futureHolder;
}

public MyCustomAdapter(Context context, Cursor c, int flags) {
...
this.asyncImageLoader = new AsyncImageLoader();
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
...
return view;
}

@Override
public void bindView(View view, Context context, Cursor cursor) {

ViewHolder holder = (ViewHolder)view.getTag();

String location = ...;
ImageView imageView = holder.imageHolder;
if(holder.futureHolder == null) {
holder.futureHolder = asyncImageLoader.DisplayImage(location, imageView);
} else {
if(!holder.futureHolder.isDone())
holder.futureHolder.cancel(true);
holder.futureHolder = asyncImageLoader.DisplayImage(location, imageView);
}
...
}

}

每次重用项目 View 时,我都会检查持有者的 future 对象是否为 Done()。如果不是,我使用 Future.cancel(true) 取消任务。但是现在,问题是任务完成得太快而无法取消。如果我让工作线程休眠,比如说 1 秒,那么任务会持续足够长的时间来取消,ListView 滚动效果会更好。但我必须等待 1 秒钟才能显示图像,我不想这样。

public class AsyncImageLoader {
....
public Future<?> DisplayImage(String location, ImageView imageView) {
if(!location.matches("N/A")) {
return executorService.submit(new ImageLoader(location, imageView));
} else {
imageView.setImageDrawable(imageView.getContext().getResources().getDrawable(R.drawable.not_available));
return null;
}
}

class ImageLoader implements Runnable {

private String location;
private ImageView imageView;

public ImageLoader(String location, ImageView imageView) {
this.location = location;
this.imageView = imageView;
}

@Override
public void run() {
boolean interrupted = false;
try {
if(!Thread.currentThread().isInterrupted()) {
Thread.sleep(1000);
Bitmap bmp = BitmapFactory.decodeFile(location);
handler.post(new ImageDisplayer(bmp, imageView));
}
} catch (InterruptedException consumed) {
interrupted = true;
} finally {
if(interrupted)
Thread.currentThread().interrupt();
}
}

}
...
}

第二种解决方案是让任务完成,但在 ListView 项目已被重用时阻止设置旧位图。但我不知道该怎么做。有什么建议吗?

最佳答案

好的,最初我从网络服务获取图像,并将它们存储在 SDCard 中。从我下载的示例中,我相信该服务会返回所有具有相同尺寸的图像。错误的!其中一些比预期的要大,并且在 ImageView 中设置时会导致延迟。我只需要缩小它们。 Load a Scaled Bitmap Version into Memory

关于android - 带有 ImageView 和 Executor Framework 的滞后 Listview,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23050212/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com