Пулы объектов для обработки изображений в Android?

В моем приложении для Android я создал представление, показывающее поле 9x9 (см. упрощенный снимок экрана):

введите здесь описание изображения

Каждое поле в представлении представлено ImageView (отображает изображения вместо прямоугольников). Некоторые ImageViews будут удалены, если установить для них значение null и установить для видимости значение View.GONE, а новые ImageView будут создаваться каждый раз, когда в поле добавляется новый элемент. Это означает, что я создаю много ImageView за короткий промежуток времени. Дело в том, что игра отлично работает на моем HTC One, но через какое-то время начинает лагать (думаю, когда работает сборка мусора), а потом перестает лагать и снова отлично работает.

Это подводит меня к идее использования Pool для управления моими объектами, например. переработав «удаленные изображения» и повторно используя их, установив положение и изменив источник изображения. Я разрабатываю для простого Android, но думаю об использовании фреймворка LibGDX.

Мой вопрос: есть ли у вас какие-либо предложения о том, как реализовать пул на простой Java/Android? Я нашел этот сообщение интересное, подумал, что мог бы использовать Apache Commons ObjectPool. Есть ли лучший способ на Android или LibGDX?

Примечание. ImageView не находятся в одной и той же позиции каждый раунд. Я перемещаю их с помощью Tween Engine. Тем не менее, количество просмотров изображения является постоянным. Значит, в какой-то момент игры у меня есть 81 ImageView (9 * 9) плюс несколько ImageView для анимации специальных событий (возможно, +10).

Буду признателен за любые советы/рекомендации.

С наилучшими пожеланиями,

Джимми


person jimmyp.smith    schedule 26.03.2014    source источник
comment
Если я правильно понимаю идею вашей игры, то сетка с изображениями статична, что означает, что количество изображений всегда одинаково, я прав? Почему бы вам просто не обновить фон или изображение imageView вместо их воссоздания? Идея с пулом, на мой взгляд, совсем не хороша - она ​​может быть полезна, если вам нужно реализовать что-то вроде списка или сетки, которая прокручивается в каком-то направлении.   -  person MP23    schedule 27.03.2014
comment
Извините, я еще не упомянул об этом, но я перемещаю ImageViews (анимированные с помощью Tween Engine), поэтому они не имеют одинакового положения в каждом раунде. В противном случае я бы полностью с вами согласился, что было бы разумно изменить только источник изображения. Кроме того, у меня есть еще несколько ImageViews для анимации слияния двух карт. Надеюсь, это имеет смысл для вас.   -  person jimmyp.smith    schedule 27.03.2014
comment
Хорошо, тогда я предлагаю использовать какую-то коллекцию, например List‹ImageView›, каждое представление изображения, которое больше не требуется, должно идти туда, и если какой-то новый ImageView необходим, то вы должны сначала проверить, не содержит ли ваш список один и обновить его фон и положение. Конечно, если вы его используете, вы должны удалить его из списка. Создавайте новый ImageView, только если список пуст. Я думаю, что это один из самых простых механизмов кэширования, но он работает правильно, например, в ListView (повторное использование представлений строк)   -  person MP23    schedule 27.03.2014
comment
Есть ли большая разница в сравнении вашего решения с пулами? В этом случае мне всегда нужно было бы искать невидимый ImageView. Может сработает, надо будет попробовать. Не могли бы вы опубликовать свое предложение в качестве ответа, пожалуйста? Я реализую это к завтрашнему дню, чтобы попробовать.   -  person jimmyp.smith    schedule 27.03.2014


Ответы (2)


По предложению Джимми я отправляю свой комментарий в качестве ответа

Я предлагаю использовать какую-то коллекцию, например List< ImageView >,

  • каждый просмотр изображения, который больше не нужен, должен идти туда

  • если необходим какой-то новый ImageView, вы должны сначала проверить, не содержит ли ваш список его, и обновить его фон и положение.

  • Конечно, если вы используете его, вы должны удалить его из списка.
  • Создавайте новый ImageView, только если список пуст.

Я думаю, что это один из самых простых механизмов кэширования, но он правильно работает, например, в ListView (повторное использование представлений строк).

person MP23    schedule 26.03.2014
comment
Или я бы использовал два списка: один для видимых ImageView и один для невидимых ImageView. Как я уже сказал, я дам вам знать завтра. Не терпится реализовать :) - person jimmyp.smith; 27.03.2014
comment
Он прекрасно работает с двумя представлениями списка: List<ImageView> cacheViews и List<ImageView> currentViews. Когда cacheViews пуст, я создаю новый ImageView и сохраняю ссылку в currentViews, если он не пуст, я повторно использую представление из cacheViews, перемещая его в другой список, устанавливая положение и видимость. Переработка проста, просто установите видимость на View.GONE и поместите его в cacheViews. Спасибо за идею/напоминание :-D - person jimmyp.smith; 28.03.2014
comment
приятно это слышать, ответ rupps тоже очень хорош, но это совершенно другой подход - person MP23; 28.03.2014
comment
Правильно, ваши решения было проще внедрить в мой существующий код, и это работает, когда я перемещаю ImageView с помощью Tween Engine. Думаю, это было бы довольно сложно с решением rupps. Тем не менее, я попробую нечто похожее на решение rupps, когда перейду на фреймворк LibGDX, потому что фреймворк использует (непрерывный) подход к рендерингу вместо ImageView. (По крайней мере, я так понял, пока) - person jimmyp.smith; 28.03.2014

У меня была аналогичная проблема, и я никогда не был действительно доволен производительностью. Концом всего этого было то, что я расширил свое игровое поле до 30 * 30, поэтому я получил 900 изображений. Я пробовал много оптимизаций, но производительность и память были высокими и непредсказуемыми на разных устройствах.

Итак, что я сделал, так это создал собственное представление. Только один. Затем этот вид рисует квадраты на холсте. Я был поражен, что теперь у меня могут быть буквально десятки тысяч квадратов (100x100) и производительность очень плавная.

Я публикую здесь скелет моего взгляда со всем удаленным дерьмом для вдохновения, я настоятельно рекомендую вам следовать этому подходу.

    /**
     * ChequeredView is a view that displays a 2D square matrix, where each square can be individually selected.
     * For high performance, It only uses one view regardless of the matrix size (everything is drawn in a canvas)
     * @author rodo 13 march 2014 <[email protected]>
     */

    public class ChequeredView extends View {

        private final String TAG="ChequeredView";

        private static final int 
            DEFAULT_MATRIX_SIZE_COLS=20, 
            DEFAULT_MATRIX_SIZE_COLS=20, 
            DEFAULT_SQUARE_SIZE=100;

        private int mCols=DEFAULT_MATRIX_SIZE_COLS, 
                    mRows=DEFAULT_MATRIX_SIZE_ROWS;

        /* Save touch press */
        private int mTouchX=0, mTouchY=0;

        ///////////////// VIEW CODE

        public ChequeredView(Context context, AttributeSet attrs) { super(context, attrs); }
        public ChequeredView(Context context) { super(context); }

        /**
         * Report a size of your view that is: SquareSize * NUM_COLS x SquareSize * NUM_ROWS. You will paint it later.
         */

        @Override
        protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

            // calculate optimum square size
            mStyleSquareSize=MeasureSpec.getSize(widthMeasureSpec) / mSquaresPerCanvas;

            // report total size
            setMeasuredDimension(DEFAULT_MATRIX_SIZE_COLS * mStyleSquareSize, DEFAULT_MATRIX_SIZE_ROWS * mStyleSquareSize);
        }

        @Override
        public void onDraw(Canvas canvas)  {
            render(canvas);
        }


        @Override 
        public boolean onTouchEvent(android.view.MotionEvent event) {

            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // I execute the action in ACTION_UP so I can put this inside a scrollview and touch doesn't interferre the scroll.
                mTouchX=(int) event.getX();
                mTouchY=(int) event.getY();
                return true;

            case MotionEvent.ACTION_UP:

                // only process touch if finger has not moved very much (if it has, it's a fling on parent)

                if ( isApprox(event.getX(), mTouchX, 5) && (isApprox(event.getY(), mTouchY, 5)) ) 
                        processTouch((int)event.getX(), (int)event.getY());

                break;

            }
            return false;
        };

        /**
         * Check if a value is close to another one
         * @param value Value to check
         * @param ref Reference value
         * @param threshold Threshold
         * @return true if |val-ref|<threshold
         */

        private boolean isApprox(float value, int ref, int threshold) {
            float result=Math.abs(value-ref);
            return (result<threshold);
        }

        ///////////////// VIEW METHODS


        public void setMatrixSize(int numx, int numy) {
            mRows=numx;
            mCols=numy;
            invalidate();
        }

        ///////////////// VIEW INTERNALS


        /**
         * Renders the whole squaremap
         * @param canvas
         */

        private void render(Canvas canvas) {
            if (canvas==null) return;
            for (int x=0; x<mCols; x++) {
                for (int y=0; y<mRows; y++) {
                    render_square(canvas, x, y);
                }
            }
        }

        /**
         * Renders one of the squares
         * @param canvas Canvas where to draw
         * @param nCol The column
         * @param nRow The row
         */

        private void render_square(Canvas canvas, int nCol, int nRow) {


            String text=null, transition=null;
            int delay=0;
            Paint paint=null;

            int cx=nCol*mStyleSquareSize, cy=nRow*mStyleSquareSize;

            canvas.save();
            canvas.translate(cx, cy);
            canvas.drawRect(mStyleSquareMargin, mStyleSquareMargin, mStyleSquareSize-2*mStyleSquareMargin, mStyleSquareSize-2*mStyleSquareMargin, paint);

            // this draws an square (I use vectorial squares with text rather than images, but just change drawRect to drawBitmap)
            // just change it for drawBitmap() to draw one bitmap

            canvas.restore();
        }

        /**
         * Process a touch on the map area
         * @param x raw x coordinate
         * @param y raw y coordinate
         */ 

        private void processTouch(int x, int y) {
            int nx=x/mStyleSquareSize, ny=y/mStyleSquareSize;
            mSelectedX=nx;
            mSelectedY=ny;
            if (mSquareListener!=null) {
                mSquareListener.onSquareSelected(nx, ny, data);
            } 
            invalidate();
        }
    }
person rupps    schedule 26.03.2014
comment
Не могли бы вы предложить, как я могу использовать ImageView с этим скелетом? Я не рисую прямоугольники - каждый ImageView представляет другой источник изображения (но некоторые из них будут идентичными). Тем не менее, я могу себе представить, что рисование снова и снова может быть намного быстрее. - person jimmyp.smith; 27.03.2014
comment
забудьте ImageView, просто используйте drawBitmap там, где я указал. drawBitmap может рисовать любое растровое изображение на холсте. ImageView внутри делает именно это! Посмотрите на функцию render_square! - person rupps; 27.03.2014
comment
да... дело именно в том, чтобы избавиться от ImageView и вместо создания 400 представлений создать всего одно. Повышение производительности относительно этой экономии, поверьте мне. - person rupps; 27.03.2014
comment
Теперь, думая об этом, я надеюсь, что это будет работать с Tween Engine. Tween Engine перемещает все элементы (любой макет или элемент в макете). Я уже реализовал все анимации с помощью этого движка. Прямо сейчас я понятия не имею, будет ли это работать с холстом. Надеюсь, что так.. - person jimmyp.smith; 27.03.2014
comment
Хм ... это может все усложнить ... не слышал о твиновом двигателе. Вы перемещаете квадраты? разрыв сетки? Если это просто анимация входа, возможно, вы можете применить анимацию ко всему представлению. - person rupps; 27.03.2014
comment
Да, я перемещаю квадраты/изображения. Я отредактировал свой вопрос, так как @MP23 указал на это. Кстати. Я поставил +1, так как мне нравится ваша идея! Никогда раньше не слышал о drawBitmap. - person jimmyp.smith; 27.03.2014
comment
изображение, которое вы разместили, смутило меня тогда, я подумал, что это своего рода сапер. Кстати, было бы круто, если бы вы выложили скриншот, выглядит как хорошая игра. О, и если вы перейдете на LibGDX, вам придется сделать это, возможно, очень похоже на мое решение :) - person rupps; 27.03.2014
comment
Я сделаю это, когда он попадет в магазин Google Play :D Пока это совершенно секретно (кроме деталей, которые я упомянул) :-P - person jimmyp.smith; 27.03.2014
comment
ха, я хотел понять ваш вопрос, мне достаточно моих проектов! - person rupps; 27.03.2014