Android活动生命周期 - 重新启动(销毁)应用程序不会删除对(自定义)监听器的引用?

Posted

技术标签:

【中文标题】Android活动生命周期 - 重新启动(销毁)应用程序不会删除对(自定义)监听器的引用?【英文标题】:Android activity life cycle - restarting (destroying) app does not remove reference to (custom) listener? 【发布时间】:2011-08-22 20:13:57 【问题描述】:

我有一个使用 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 中以接收运动事件。

我在这里遗漏了一些明显的事情吗?我原以为当调用 onDestroy 时,应用程序将被完全取消引用,因此不会留下任何痕迹。这不是真的吗?当我调用 new Renderer();它是在引用一个旧的吗?

我不知道到底发生了什么。特别是因为这个应用程序是我编写的另一个应用程序的基本副本,它工作得很好!

编辑:

经过少量实验后,我发现运动事件实际上是转到一个旧的 ScrollPanel(我制作的一个对象..),它被注册为一个监听器(我所说的监听器是我自己的实现..)运动事件。我为这些创建了自己的事件系统,如下所示:

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 并且有一个监听器列表。现在尽管被 Screen currentScreen = new Screen(); 重做了在 OnCreate() 中调用;这个监听器列表仍然填充了旧的 ScrollPanel?

这是怎么回事?我显然遗漏了一些明显的东西。不知何故,听众列表出于某种原因是静态的,并且尽管应用程序被完全重新制作,但没有被取消引用?

【问题讨论】:

【参考方案1】:

我怀疑您面临的问题可能与原始运动事件在 onMotionEvent() 返回后被框架回收(返回到它们的池)这一事实有关。

从您使用 InputObjects 的方式来看,我认为您可能会在其中保留对原始运动事件的引用,而不是复制事件数据。

快速尝试使用MotionEvent.obtain(event),无论您现在使用event(这会复制),看看这是否会使奇怪的行为消失。自然,如果这可行,您最终将不得不在完成这些副本后对其进行 recycle()。不过,请勿在原始运动事件上​​调用 recycle()

干杯,艾特。

【讨论】:

发现应用程序实际上存储了对我所做的 motionListener 的旧引用,我认为这与事件本身无关。但是,对于已被销毁的应用程序如何仍然保留对静态的引用,我感到非常困惑。编辑:不过我会尝试! 嗯,看了我的代码(老实说,我现在有点醉了)我似乎并没有真正存储事件本身“输入”对象是我自己的,我复制跨领域,如行动的类型和事件的位置。在我的输入池中,我只有其中的 10 个,并根据需要分配/返回。我想我的问题是,当应用程序被销毁时,所有引用都会被删除吗?那么当我再次启动一个应用程序时,它是一张完全空白的表格吗?目前,我似乎在一个仍然存在的界面中有一个静态的对象列表。我认为界面中的静态列表可能是一件坏事。 是的,接口中的每个字段都是隐式的 public final static。我想你回答了你自己的问题:一个接口不应该保留任何状态和实现,但是你以这种方式使用列表会导致问题......仍然令人困惑为什么在重新启动应用程序时状态没有重置。这表明当应用关闭时界面没有被卸载。

以上是关于Android活动生命周期 - 重新启动(销毁)应用程序不会删除对(自定义)监听器的引用?的主要内容,如果未能解决你的问题,请参考以下文章

Android入门Activity-生命周期与启动模式

Android第一行代码学习笔记七---活动的生命周期

活动的生命周期整理

Android 屏幕发生旋转对应的生命周期发生变化解析

Android四大组件之Activity的生命周期

Android笔记-活动生命周期&Bundle回收临时数据&活动启动模式&常用技巧