Вызывает ли Android runOnUiThread функцию getView () в адаптерах? - PullRequest
3 голосов
/ 03 сентября 2011

Я работаю с отложенной загрузкой Gallery изображений (один класс для Gallery и Adapter, а другой - для отложенной загрузки).Второй класс использует 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;
        }

    }

Вот класс HorizontalImageLoader ( на основе этого примера )

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 вызывается первой.

Ответы [ 5 ]

2 голосов
/ 09 сентября 2011

BitmapDisplayer - это Runnable, который запускается в потоке пользовательского интерфейса и вызывает методы в ImageView. Эти методы генерируют запросы компоновки, что в конечном итоге вызывает getView ().

2 голосов
/ 06 сентября 2011

Выполнение:

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

Имейте в виду, что если getView содержится в исполнении DisplayImage, у вас будет бесконечный цикл.

Так как бесконечного цикла нет, то может быть один из двух сценариев:

  1. DisplayImage выполняет что-то только ОДНАЖДЫ и это что-то является тригерингом getView ОДИН РАЗ
  2. что-то еще является тригерингом getView.

2 неверно, поскольку единственный оператор, выполненный в getView, это DisplayImage

1 верно, DisplayImage выполняет один из вариантов setImageDrawable один раз.

В соответствии с исходный код setImageDrawable View.requestLayout() и View.invalidate называется,

  • View.requestLayout() вызовет ViewParent Интерфейс requestLayout(), который в соответствии с документацией:

    Вызывается, когда что-то изменилось, что сделало недействительным макетребенок этого родителя представления.Это запланирует передачу макета дерева представления.

  • View.invalidate вызовет ViewParent Интерфейс invalidateChild, что может вызвать недействительность самого ViewParent.


Таким образом, можно с уверенностью сказать, что установка изображения ImageView приведет к недействительности для себя.(Это действительно так и не нуждается в доказательстве в любом случае)

Теперь, так как этот ImageView является дочерним элементом интерфейса адаптера, современное состояние адаптера getViewвызывается с позиции точного представления, которое содержит это ImageView


Наконец, Worry Not , потому что getView inэтот случай эквивалентен invalidate(), и вы, безусловно, хотите, чтобы вызывался invalidate(), чтобы ваши изображения отображались .

Это нормально .

1 MORE THING

Плохая производительность вашего приложения не связана с вызовом getView!

. Вы должны правильно реализовать DisplayImage.Ваш класс не учитывает какую-либо оптимизацию.Это не обязанность BaseAdapter.

Советы :

  1. Я считаю, что одно большое узкое место - это ваше queuePhotoфункция, которая всегда вызывает функцию Clean!Вы все время чистите!
  2. Ваш ключ - ImageView!и вы все время сравниваете ImageView s в цикле

Просто попробуйте улучшить свой код и оптимизировать его.Вы должны учитывать, что getView вызывается несколько раз.

1 голос
/ 06 сентября 2011

Это не странная проблема, это нормально вызывать метод getView несколько раз, прежде чем показывать действие, это все о проверке представлений.

, чтобы проверить это, попробуйте создать обычный список только с текстом, вы увидите, что каждое представление вызывается дважды.

1 голос
/ 03 сентября 2011

Я не вижу проблем в коде. Я думаю, что для адаптера viewview нормально вызывать 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
}
0 голосов
/ 08 сентября 2011

Я думаю, что это проблема с высотой списка.Если вы предоставите некоторую фиксированную высоту вместо wrap_content или fill_parent для высоты списка, то я думаю, что это сработает.Однажды проверь это.

...