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

我正在使用延迟加载图库(一个类用于Gallery和Adapter,另一个用于延迟加载部分).第二个类使用runOnUiThread使用其上下文更新第一个类,但似乎它再次调用第一个类’适配器的getView()方法.这意味着为Gallery中的每个图像调用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类(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);
        }
    }

}

再次更新

如果我在我的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.我的画廊设置为首先选择索引1,这就是首先调用位置1的原因.

最佳答案
执行:

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

请记住,如果getView包含在DisplayImage的执行中,您将拥有无限循环.

由于没有无限循环,因此两种情况中的一种可能是真的:

> DisplayImage只执行一些ONCE,这就是getView ONCE的一部分
>其他东西正在扼杀getView.

2不是这样,因为在getView中执行的唯一语句是DisplayImage

1是真的,DisplayImage执行一次setImageDrawable的一种风格.

根据the source code of setImageDrawable调用View.requestLayout()和View.invalidate.

> View.requestLayout()将根据文档调用ViewParent Interface的requestLayout():

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将调用ViewParent Interface的invalidateChild,这可能会导致ViewParent本身失效.

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)

现在,由于此ImageView是Adapter接口的子级,因此使用包含此ImageView的精确视图的位置调用适配器的getView是最先进的.

最后,担心不是因为在这种情况下getView对invalidate()是公平的,你肯定希望调用invalidate()以便显示你的图像.

这个是正常的.

1更多的事情

应用程序的糟糕性能不是因为调用了getView!

您应该正确实现您的DisplayImage.您的班级没有考虑任何优化.它不是BaseAdapter的责任.

建议:

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

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

转载注明原文:Android的runOnUiThread是否在Adapters中调用getView()? - 代码日志