Жизненный цикл активности Android - перезапуск (уничтожение) приложения не удаляет ссылку на (пользовательский) слушатель? - PullRequest
2 голосов
/ 11 мая 2011

У меня есть приложение, использующее GlSurfaceView и средство визуализации. У меня установлено так, что когда пользователь выходит из приложения через кнопку назад, я вызываю myActivity.finish ();

Это нормально, и я вижу, как активность вызывает вызовы onStop () и onDestroy ();

Приложение работает нормально при первом запуске, однако при последующем запуске у меня возникла проблема с motionEvents.

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

try
    {
        //Get the history first
        int hist = event.getHistorySize();
        if (hist > 0)
        {
            //Oldest is first in the list. (I think).
            for (int i=0;i <hist; i++)
            {
                InputObject input = inputObjectPool.take();
                input.useEventHistory(event, i);
                defRenderer.feedInput(input);
            }
        }

        //The current one still needs to be added
        InputObject input = inputObjectPool.take();
        input.useMotionEvent(event);
        defRenderer.feedInput(input);
    }

А в рендере:

            synchronized (inputQueueMutex) 
    {
        ArrayBlockingQueue<InputObject> inputQueue = this.inputQueue;
        while (!inputQueue.isEmpty()){try
            {
                InputObject input = inputQueue.take();

                if (input.eventType == InputObject.EVENT_TYPE_TOUCH)
                {
                    screenManager.processMotionEvent(input); 
                }
                else if (input.eventType == InputObject.EVENT_TYPE_KEY)
                {
                    screenManager.processKeyPress(input);
                }

                input.returnToPool();
            }
            catch (InterruptedException ie)
            {
                DLog.defError("Interrupted blocking on input queue.", ie);
            }
        }
    }

Как вы можете видеть из приведенного выше кода, я передаю эти события движения в ScreenManager, который, по сути, является моим способом создания нескольких "сцен", которые я отрисовываю. Это отлично работает при первом запуске приложения, и экран интерпретирует мои движения в настоящий момент как простой квадрат.

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

Я следил за событиями движения, и хотя они передаются «новому» средству визуализации, похоже, что они передают события движения на старый экран. Или скорее к старому объекту на экране. Это сбивает с толку, так как в моем коде в методе onCreate () я делаю это:

//Set up the renderer and give it to the SurfaceView
    defRenderer = new DefRenderer();
    defView = new DefView(this);
    defView.setRenderer(defRenderer);

    //Set out content to the surface view.
    setContentView(defView);

    //Set up the input queue
    createInputObjectPool();

OnCreate вызывается как в первый раз, так и во второй раз, когда мое приложение запускается (и приложение было уничтожено!), Экраны становятся новыми в defRenderer и передаются новому defView.

Я очень озадачен тем, как данные могут остаться в defRenderer для получения motionEvents, поскольку приложение полностью переделано.

Есть ли что-то очевидное, что я здесь упускаю? Я бы подумал, что при вызове onDestroy приложение будет полностью разыменовано, и от него не останется никаких следов. Это не правда? Делает ли что-то, когда я вызываю новый Renderer (); это ссылка на старую?

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

EDIT:

После небольшого количества экспериментов я обнаружил, что события движения на самом деле идут к старой ScrollPanel (объект, который я сделал ..), который зарегистрирован как слушатель (и под слушателем я имею в виду мою собственную реализацию ..) для MotionEvents. Я сделал для них свою собственную систему событий:

public interface TouchSource 
public static final int TYPE_TOUCHDOWN = 0;
public static final int TYPE_TOUCHDRAG = 1;
public static final int TYPE_TOUCHCLICK = 2;

public Vector<TouchListener> listeners = new Vector<TouchListener>();

public void addTouchListener(TouchListener listener);
public void removeTouchListener(TouchListener listener);

public void touchOccured(int type, int xPos, int yPos);

}

И интерфейс слушателя:

public interface TouchListener 
public boolean touchDownOccured(int xPos, int yPos);
public boolean touchDragOccured(int xPos, int yPos);
public boolean touchClickOccured(int xPos, int yPos);

Таким образом, Screen реализует touchSource и, таким образом, имеет список слушателей. Теперь несмотря на то, что REMADE by Screen currentScreen = new Screen (); вызывается в OnCreate (); менеджера этот список слушателей до сих пор заполнен старой ScrollPanel?

Как это? Я явно упускаю что-то очевидное. Например, список слушателей по какой-то причине статичен и не получает разыменования, несмотря на то, что приложение полностью переделано?

1 Ответ

0 голосов
/ 11 мая 2011

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

Исходя из того, как вы используете ваши объекты InputObjects, я думаю, что вы, возможно, сохраняете ссылку на оригинальные события движения, а не копируете данные события.

Быстро попробуйте использовать MotionEvent.obtain(event) везде, где вы используете event сейчас (это делает копию), и посмотрите, не исчезнет ли это странное поведение. Естественно, если это сработает, вам, в конце концов, придется перерабатывать () эти копии после того, как вы закончите с ними. Не звоните recycle() на оригинальные события, хотя.

Приветствия, Aert.

...