当前位置: 首页 > 知识库问答 >
问题:

带有ImageView和Executor框架的laggy Listview

柳修平
2023-03-14

我有一个带有自定义项的列表视图,比如:

灰色正方形是ImageView。填充ListView的数据来自光标形式的数据库。但图像并不直接存储在数据库中,而是在SDCard中,数据库仅保存对它们的String引用。

一开始,我将Image解码为来自覆盖CursorAdapter的bindView()回调方法的位图:

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

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

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

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

当一个可用的工作线程处理ImageLoader Runnable时,SD卡中的图像被解码成位图,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);
        }
    }

}

}

问题是,我仍然得到滞后的滚动。如果我去掉ImageLoader里面的代码。run()方法,滚动非常完美。这些代码不应该在工作线程中处理吗?我错过了什么?

使现代化

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

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

我正在使用Future对象取消任务。存储在自定义光标适配器内标记到项目视图的支架中:

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);
    }
    ...
}   

}   

每次重用项目视图时,我都会检查持有者的未来对象是否为one()。如果不是,我将取消未来的任务。取消(对)。但现在的问题是,任务完成得太快,无法取消。如果我让worker线程处于睡眠状态,比如说1秒钟,那么任务持续的时间足以被取消,ListView滚动效果更好。但我必须等一秒钟,图像才会出现,我不想这样。

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项已被重用时,阻止设置旧位图。但我不知道怎么做。有什么建议吗?

共有1个答案

林意蕴
2023-03-14

好的,最初我从Web服务获取图像,并将它们存储在SDCard中。从我下载的示例中,我相信该服务正在返回具有相同尺寸的所有图像。错误!其中一些比预期的要大,并且在ImageView中设置时会导致延迟。我只需要缩小它们。将缩放的位图版本加载到内存中

 类似资料:
  • 在上面,我认为fork-join执行器的代码并行是不可能的。对不同方法/函数的每次调用都需要上一步的内容。如果我要为线程选择fork-join执行器,这对我有什么好处呢?在fork-join和线程池执行器之间,上面的代码执行会有什么不同。 谢谢

  • 主要内容:1 Executor框架的概述,2 Executor线程池的概述,3 Executor线程池的基本结构基于JDK1.8详细介绍了Executor线程池框架的基本架构组成。 1 Executor框架的概述 JDK1.5之前,我们如果想要使用Java线程来完成相关任务,一般涉及两个类,一个是Thread类,一个Thread对象在启动(start)之后会创建一个关联的本地操作系统线程,随后会自动回调run方法。另一个是Runnable接口,可以看作 run方法的抽象,代表线程任务。通过Run

  • 我想创建一个从ArrayList中获取数据的listview。这是单行的一个示例 默认情况下,该复选框是禁用的,它只在一个SeloncemClickListener中激活(用于多次删除)。 最后一个图像是一个菜单按钮,它打开菜单列表以进行删除、共享等操作。 我不知道如何制作我的定制适配器。使用simplearrayadapter很简单,但我不能使用自定义行。有人帮忙吗?吻,塔季扬娜。对不起我的英语

  • 我想通过Remote tecommand操作打开一个对话框,并从页面后备bean中的中获取值。远程命令在页面加载时正确触发bb中的actionListener 使用此show Dialog方法也可以正确显示对话框: 在另一个Bean中: 问题是,我不知道如何聆听由以下事件引发的事件: 在PF示例中http://www.primefaces.org/showcase/ui/dialogFramewo

  • 我有一个类,它扩展了 创建表主题的第一条记录可以,但无法创建另一条记录: [PersistenceException:执行DML bindLog时出错[]错误[ERROR:重复的键值违反了唯一约束“pk\U主题”\n详细信息:键(id)=(1)已存在。]] 例外情况当然发生在这一行: 这很奇怪,因为: 它以前工作没有问题 我应该怎么做才能解决这个问题? 类(当然不是全部,只是相关的):

  • 我想实现一个Maven项目,它使用与Jersey集成的Spring social框架。但是它有一些依赖问题。