Android的runOnUiThread是否调用Adapters中的getView()?

发布于 2024-12-02 22:15:06 字数 7742 浏览 3 评论 0原文

我正在处理延迟加载图像的Gallery(一个类用于GalleryAdapter,另一个类用于延迟加载部分)。第二个类使用 runOnUiThread 来使用其上下文更新第一个类,但它似乎再次调用第一个类适配器的 getView() 方法。这意味着对于图库中的每个图像,getView() 都会被调用两次。查看下面的代码。

奇怪的是,第二个 getView() 调用仅在 Gallery 小部件中的选定图像上调用。如果同时显示四个图像,则将对这四个图像调用一次 getView() ,并对所选图像另外调用三次。

有什么想法为什么会发生这种情况吗?

这是适配器

public class ImageAdapter extends BaseAdapter {

        public HorizontalImageLoader horImageLoader;

        public ImageAdapter() {
            horImageLoader = new HorizontalImageLoader(Main.this);
        }

        public int getCount() {
            return coverFileNames.size();
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {

            ImageView imageView;

            if (convertView == null) {
                imageView = new ImageView(Main.this);
            } else {
                imageView = (ImageView) convertView;
            }

            // If I just use this, getView() is only called once per image (the correct way):
            // imageView.setImageResource(R.drawable.noposterxl);

            // If I just use this, getView() is only called once per
            // image, and additional times on the selected image (not correct):
            horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);

            return imageView;
        }

    }

这是 Horizo​​ntalImageLoader 类基于此示例< /a>)

public class HorizontalImageLoader {

    private Activity activity;
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());

    public HorizontalImageLoader(Activity activity) {
        this.activity = activity;
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
    }

    public void DisplayImage(String fileUrl, ImageView imageView, final int pos) {
        imageViews.put(imageView, fileUrl);
        queuePhoto(fileUrl, activity, imageView, pos);
        imageView.setImageResource(R.drawable.noposterxl);
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView, int position) {
        //This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them.
        photosQueue.Clean(imageView);
        PhotoToLoad p=new PhotoToLoad(url, imageView, position);
        synchronized(photosQueue.photosToLoad){
            photosQueue.photosToLoad.push(p);
            photosQueue.photosToLoad.notifyAll();
        }

        //start thread if it's not started yet
        if(photoLoaderThread.getState()==Thread.State.NEW)
            photoLoaderThread.start();
    }

    private Bitmap getBitmap(String fileUrl, int position) {

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPurgeable = true;

        Bitmap bm = BitmapFactory.decodeFile(fileUrl, options);

        return bm;
    }

    //Task for the queue
    private class PhotoToLoad
    {
        public String url;
        public ImageView imageView;
        public int pos;
        public PhotoToLoad(String u, ImageView i, int p){
            url=u;
            imageView=i;
            pos = p;
        }
    }

    PhotosQueue photosQueue=new PhotosQueue();

    public void stopThread()
    {
        photoLoaderThread.interrupt();
    }

    //stores list of photos to download
    class PhotosQueue
    {
        private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();

        //removes all instances of this ImageView
        public void Clean(ImageView image)
        {
            try {
                for(int j=0 ;j<photosToLoad.size();){
                    if(photosToLoad.get(j).imageView==image)
                        photosToLoad.remove(j);
                    else
                        ++j;
                }
            } catch (Exception e) {
                // Do nothing
            }
        }
    }

    class PhotosLoader extends Thread {
        public void run() {
            try {
                while(true)
                {
                    //thread waits until there are any images to load in the queue
                    if(photosQueue.photosToLoad.size()==0)
                        synchronized(photosQueue.photosToLoad){
                            photosQueue.photosToLoad.wait();
                        }
                    if(photosQueue.photosToLoad.size()!=0)
                    {
                        PhotoToLoad photoToLoad;
                        synchronized(photosQueue.photosToLoad){
                            photoToLoad=photosQueue.photosToLoad.pop();
                        }
                        Bitmap bmp = getBitmap(photoToLoad.url, photoToLoad.pos);
                        String tag=imageViews.get(photoToLoad.imageView);

                        if(tag!=null && tag.equals(photoToLoad.url)){
                            BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
                            Activity a=(Activity)photoToLoad.imageView.getContext();
                            a.runOnUiThread(bd); // This seems to be causing the additional calls to getView()
                        }
                    }
                    if(Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                //allow thread to exit
            }
        }
    }

    PhotosLoader photoLoaderThread=new PhotosLoader();

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        ImageView imageView;
        public BitmapDisplayer(Bitmap b, ImageView i){
            bitmap=b;
            imageView=i;
        }
        public void run()
        {
            if(bitmap!=null)
                imageView.setImageBitmap(bitmap);
            else
                imageView.setImageResource(R.drawable.noposterxl);
        }
    }

}

再次更新

如果我在 getView() 方法中执行以下操作,这就是 LogCat 所说的内容:

代码:

Log.d("POSITION", "Current position: " + position);
horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);

LogCat:(

09-03 13:59:11.920: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 2
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 3
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 0
09-03 13:59:12.110: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.240: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.300: DEBUG/POSITION(15278): Current position: 1

注意附加的三个日志条目“当前位置:底部的 1”)

如果我这样做,那么 LogCat 的内容如下:

代码:

Log.d("POSITION", "Current position: " + position);
imageView.setImageResource(R.drawable.noposterxl);

LogCat:(

09-03 14:02:47.340: DEBUG/POSITION(15412): Current position: 1
09-03 14:02:47.360: DEBUG/POSITION(15412): Current position: 2
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 3
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 0

请注意,这将返回正确的结果 - 每个图像仅调用一次)

PS。我的 Gallery 设置为首先选择索引 1,这就是首先调用位置 1 的原因。

I'm working with lazy loading a Gallery of images (one class for the Gallery and Adapter, and another for the lazy loading part). The second class uses runOnUiThread to update the first class using its context, but it seems that it's calling the getView() method of the first class' adapter again. This means that getView() is being called twice for every image in the Gallery. Check out the code below.

The weird thing is that second getView() call is only being called on the selected image in the Gallery widget. If four images are shown at the same time, getView() will be called on those four images once, and three additional times on the selected image.

Any ideas why this is happening?

Here's the adapter

public class ImageAdapter extends BaseAdapter {

        public HorizontalImageLoader horImageLoader;

        public ImageAdapter() {
            horImageLoader = new HorizontalImageLoader(Main.this);
        }

        public int getCount() {
            return coverFileNames.size();
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {

            ImageView imageView;

            if (convertView == null) {
                imageView = new ImageView(Main.this);
            } else {
                imageView = (ImageView) convertView;
            }

            // If I just use this, getView() is only called once per image (the correct way):
            // imageView.setImageResource(R.drawable.noposterxl);

            // If I just use this, getView() is only called once per
            // image, and additional times on the selected image (not correct):
            horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);

            return imageView;
        }

    }

Here's the HorizontalImageLoader class (based on this example)

public class HorizontalImageLoader {

    private Activity activity;
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());

    public HorizontalImageLoader(Activity activity) {
        this.activity = activity;
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
    }

    public void DisplayImage(String fileUrl, ImageView imageView, final int pos) {
        imageViews.put(imageView, fileUrl);
        queuePhoto(fileUrl, activity, imageView, pos);
        imageView.setImageResource(R.drawable.noposterxl);
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView, int position) {
        //This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them.
        photosQueue.Clean(imageView);
        PhotoToLoad p=new PhotoToLoad(url, imageView, position);
        synchronized(photosQueue.photosToLoad){
            photosQueue.photosToLoad.push(p);
            photosQueue.photosToLoad.notifyAll();
        }

        //start thread if it's not started yet
        if(photoLoaderThread.getState()==Thread.State.NEW)
            photoLoaderThread.start();
    }

    private Bitmap getBitmap(String fileUrl, int position) {

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPurgeable = true;

        Bitmap bm = BitmapFactory.decodeFile(fileUrl, options);

        return bm;
    }

    //Task for the queue
    private class PhotoToLoad
    {
        public String url;
        public ImageView imageView;
        public int pos;
        public PhotoToLoad(String u, ImageView i, int p){
            url=u;
            imageView=i;
            pos = p;
        }
    }

    PhotosQueue photosQueue=new PhotosQueue();

    public void stopThread()
    {
        photoLoaderThread.interrupt();
    }

    //stores list of photos to download
    class PhotosQueue
    {
        private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();

        //removes all instances of this ImageView
        public void Clean(ImageView image)
        {
            try {
                for(int j=0 ;j<photosToLoad.size();){
                    if(photosToLoad.get(j).imageView==image)
                        photosToLoad.remove(j);
                    else
                        ++j;
                }
            } catch (Exception e) {
                // Do nothing
            }
        }
    }

    class PhotosLoader extends Thread {
        public void run() {
            try {
                while(true)
                {
                    //thread waits until there are any images to load in the queue
                    if(photosQueue.photosToLoad.size()==0)
                        synchronized(photosQueue.photosToLoad){
                            photosQueue.photosToLoad.wait();
                        }
                    if(photosQueue.photosToLoad.size()!=0)
                    {
                        PhotoToLoad photoToLoad;
                        synchronized(photosQueue.photosToLoad){
                            photoToLoad=photosQueue.photosToLoad.pop();
                        }
                        Bitmap bmp = getBitmap(photoToLoad.url, photoToLoad.pos);
                        String tag=imageViews.get(photoToLoad.imageView);

                        if(tag!=null && tag.equals(photoToLoad.url)){
                            BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
                            Activity a=(Activity)photoToLoad.imageView.getContext();
                            a.runOnUiThread(bd); // This seems to be causing the additional calls to getView()
                        }
                    }
                    if(Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                //allow thread to exit
            }
        }
    }

    PhotosLoader photoLoaderThread=new PhotosLoader();

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        ImageView imageView;
        public BitmapDisplayer(Bitmap b, ImageView i){
            bitmap=b;
            imageView=i;
        }
        public void run()
        {
            if(bitmap!=null)
                imageView.setImageBitmap(bitmap);
            else
                imageView.setImageResource(R.drawable.noposterxl);
        }
    }

}

UPDATED AGAIN

If I do the following in my getView() method, this is what LogCat says:

Code:

Log.d("POSITION", "Current position: " + position);
horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);

LogCat:

09-03 13:59:11.920: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 2
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 3
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 0
09-03 13:59:12.110: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.240: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.300: DEBUG/POSITION(15278): Current position: 1

(note the additional three log entries with "Current position: 1" at the bottom)

If I do this, then here's what LogCat says:

Code:

Log.d("POSITION", "Current position: " + position);
imageView.setImageResource(R.drawable.noposterxl);

LogCat:

09-03 14:02:47.340: DEBUG/POSITION(15412): Current position: 1
09-03 14:02:47.360: DEBUG/POSITION(15412): Current position: 2
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 3
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 0

(note that this is returning the correct result - only one call per image)

PS. My Gallery is set to select index 1 first, that's why position 1 is called first.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

行雁书 2024-12-09 22:15:06

执行:

  1. public View getView(intposition, View ConvertView, ViewGroupparent)
  2. DisplayImage(coverFileNames.get(position), imageView,position)

请记住,如果 getView 包含在 DisplayImage 的执行中,您将陷入无限循环。

由于不存在无限循环,因此可能存在以下两种情况之一:

  1. DisplayImage 仅执行某事一次,而此某事 em> 正在触发 getView ONCE
  2. 其他东西正在触发 getView

2 不成立,因为 getView 中执行的唯一语句是 DisplayImage

1< /strong> 为 true,DisplayImage 执行一次 setImageDrawable 类型之一。

根据 setImageDrawable源码View.requestLayout()View.invalidate 被调用。

  • View.requestLayout() 将调用 ViewParent 接口的 requestLayout(),根据文档:

    <块引用>

    当某些内容发生更改导致该视图父级的子级布局无效时调用。这将安排视图树的布局过程。

  • View.invalidate 将调用 ViewParent 接口的 invalidateChild,这可能会导致 ViewParent 本身失效。


因此,可以肯定地说,设置 ImageView 的图像会导致其自身失效。 (事实确实如此,无论如何都不需要证明)

现在,由于此 ImageView 是 Adapter 接口的子级,因此 Adapter 的 getView 是最先进的使用包含此 ImageView 的确切视图的位置进行调用。


最后,不用担心,因为 getView< /code> 在这种情况下相当于invalidate() 并且您肯定希望调用 invalidate() 以便显示您的图像

这是正常的

还有一件事

您的应用程序性能不佳并不是由于调用了getView

您应该正确实现您的 DisplayImage。您的课程没有考虑任何优化。这不是 BaseAdapter 的责任。

建议

  1. 我相信一个大瓶颈是您的 queuePhoto 函数,它总是调用 Clean 函数!你一直在打扫卫生!
  2. 您的密钥是一个ImageView!并且您一直在循​​环中比较 ImageView

只是尝试增强您的代码并优化它。您应该考虑 getView 被多次调用。

Execution:

  1. public View getView(int position, View convertView, ViewGroup parent)
  2. DisplayImage(coverFileNames.get(position), imageView, position)

Bear in mind that if getView is contained in the execution of DisplayImage, You will have an infinite loop.

Since there is no infinite loop, then one of two scenarios can be true:

  1. DisplayImage executes something only ONCE and this something is trigerring getView ONCE
  2. something else is trigerring getView.

2 is not true, since the only statement executed in getView is DisplayImage

1 is true, DisplayImage executes one of the flavors of setImageDrawable once.

According to the source code of setImageDrawable View.requestLayout() and View.invalidate are called.

  • View.requestLayout() will call ViewParent Interface's requestLayout() which according to the docs:

    Called when something has changed which has invalidated the layout of a child of this view parent. This will schedule a layout pass of the view tree.

  • View.invalidate will call ViewParent Interface's invalidateChild which might cause an invalidation on the ViewParent itself.


Therefore, it is safe to say that setting the image of an ImageView will cause an invalidation to itself. (Which is really the case and does not need to be proven anyway)

Now Since this ImageView is a child of an Adapter interface, it is state of art that the Adapter's getView is called with the position of the exact view that contains this ImageView


Finally, Worry Not because getView in this case is equivelant to invalidate() and you surely want invalidate() to be called so that your images show up.

This is normal.

1 MORE THING

The bad performance of your application is not due to calling of the getView!

You should implement your DisplayImage correctly. Your class do not take into account any optimization. It is not the responsibility of the BaseAdapter.

Advice:

  1. I believe one big bottleneck is your queuePhoto function which is always calling the Clean function! You are cleaning all the time!
  2. Your key is an ImageView! and you are comparing ImageViews all the time in a loop

Just try to enhance your code and optimize it. You should account for getView being called multiple of times.

情深已缘浅 2024-12-09 22:15:06

BitmapDisplayer 是一个在 UI 线程上运行的 Runnable,它调用 ImageView 上的方法。这些方法恰好会生成布局请求,最终会导致 getView() 被调用。

BitmapDisplayer is a Runnable that you run on the UI thread, and it calls methods on ImageView. These methods happen to generate layout requests, which will eventually cause getView() to be invoked.

横笛休吹塞上声 2024-12-09 22:15:06

我看代码没有问题。我认为当选择一个项目时,adapterview调用getview是正常的。这是另一个类似问题的链接,将进一步解释。

自定义列表视图适配器 getView 方法被多次调用,且顺序不连贯

更新

注释掉 setImageResource DisplayImage 中的行,看看对于所选项目,对 getView 的调用次数是否减少到 2 次。

public void DisplayImage(String fileUrl, ImageView imageView, final int pos) {
    imageViews.put(imageView, fileUrl);
    queuePhoto(fileUrl, activity, imageView, pos);
 //   imageView.setImageResource(R.drawable.noposterxl); // coment this line
}

I can see no problem in the code. I think its normal for adapterview to call getview when a item is selcted. Here is a link to another similar question that will explain further.

custom listview adapter getView method being called multiple times, and in no coherent order

Update

Comment out the setImageResource line in DisplayImage and see if the number of calls to getView, for selected item, reduces to 2.

public void DisplayImage(String fileUrl, ImageView imageView, final int pos) {
    imageViews.put(imageView, fileUrl);
    queuePhoto(fileUrl, activity, imageView, pos);
 //   imageView.setImageResource(R.drawable.noposterxl); // coment this line
}
无人问我粥可暖 2024-12-09 22:15:06

这不是奇怪的问题,在显示活动之前多次调用 getView 方法是正常的,这都是关于验证视图的。

要测试它,请尝试创建仅包含文本的普通列表,您将看到每个视图都被调用两次。

This is not Weird issue, its normal to call getView method several times before showing the activity, this all about validating the views.

to test it try to create normal list with just text, you will see every view is called twice.

满天都是小星星 2024-12-09 22:15:06

我认为这是listview高度的问题。如果您为列表视图高度提供一些固定高度而不是 wrap_contentfill_parent 那么我认为它会起作用。一旦检查一下。

I think it is the problem with listview height. If you provide some fixed height instead of wrap_content or fill_parent for listview height then I think it will work. Once check that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文