Search code examples
androidandroid-lifecycleuser-interactionandroid-ondestroy

Android - onDestroy() called after onCreate()


I have a screensaver activity running, a small animation. When the user touches the screen, they are moved to my MainActivity. To do so, I am overriding the method "onUserInteraction()". In case you're not familiar, any physical touch to the device's screen, or it's buttons, triggers this method.

Tapping the screen takes me over to the main activity successfully, every time. However, if I press the back button or volume buttons, I get the below series of events.

  1. onCreate()
  2. onResume()
  3. onPause()
  4. onCreate()
  5. onResume()
  6. onWindowFocusChanged()
  7. onStop()
  8. onDestroy()

In onCreate() and onResume() I set a boolean value to true. In onPause(), and onDestroy(), the boolean is set to false. So, I have a major issue when onDestroy() is called last. In the meantime, I can set my boolean to only be changed during onPause(), as it is called every time I change screens, but this issue is still bothering me.

In my Logcat, just before onDestroy() is called, I see the following line:

[MainActivity] Surface release

MainActivity in my Manifest:

<activity
    android:name=".MainActivity"
    android:screenOrientation="sensorLandscape" />

I provided the above as I read (somewhere) that changing the requested orientation inside of onCreate() could cause my issue. However, I am adding flags:

protected void onCreate(Bundle savedInstanceState){
    getWindow.addFlags(WindowManager.LayaoutParams.FLAG_TURN_SCREEN_ON);
    getWindow.addFlags(WindowManager.LayaoutParams.FLAG_KEEP_SCREEN_ON);
    setContentView(R.layout.activity_main);

    // other code...
}

What I really find funny, is that I have no issues at all when onDestroy() is called. All buttons work correctly, menu items, timers, background services. Other than this 'destroying' my boolean logic, and 'destroying' what I have left of my pride... nothing is actually affected. Is this a bug?

Running Android Studio 3.3.2 with Gradle 4.10.1

Thanks in advance for any help.

EDIT (March 21, 2019)

To answer the boolean logic question: I have popups that appear on the screen, as long as the user is on the home screen of the app, which is "MainActivity". I also track if I am on the screensaver activity - a popup request will move the user to the home screen, then open the popup.

In MainActivity.java

onCreate()  - isHomeScreenActive = true;
onPause()   - isHomeScreenActive = false;
onResume()  - isHomeScreenActive = true;
onDestroy() - isHomeScreenActive = false; //Temporarily removed

Moving to ScreenSaver from MainActivity isn't the issue, but I have added the code here just in case.

   public void startScreenSaverTimer(){

       // Restarts the timer when method is called.
       // Touch events, onResume(), onCreate() call this
       if(timer != null){
           timer.cancel();
       }

       // Start Timer
       timer = new Timer();
       timer.schedule(new TimerTask(){
           @Override
           public void run(){
               finish();
               Intent ScreenSaver = new Intent(this, ScreenSaver.class);
               ScreenSaver.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                                    Intent.FLAG_ACTIVITY_CLEAR_TOP);
               startActivity(ScreenSaver);
           }
       }, 60000); //Set to 1 minute
   }

By request, I have added "this" inside my logs. Results are below:

E/Tracker: onCreate() Boolean Value = true
E/Tracker: onCreate() called from com.x.x.MainActivity@be81228
E/Tracker: onResume() Boolean Value = true
E/Tracker: onResume() called from: com.x.x.MainActivity@be81228
E/Tracker: onPause() Boolean Value = false
E/Tracker: onPause() called from com.x.x.MainActivity@be81228
E/Tracker: onCreate() Boolean Value = true
E/Tracker: onCreate() called from com.x.x.MainActivity@d0faced
E/Tracker: onResume() Boolean Value = true
E/Tracker: onResume() called from: com.x.x.MainActivity@d0faced
E/Tracker: onWindowFocusChanged() called from com.x.x.MainActivity@d0faced
E/Tracker: onStop() called from com.x.x.MainActivity@be81228
E/Tracker: onDestroy() called from: com.x.x.MainActivity@be81228

Looking at the above, it looks like we are starting MainActivity twice. Nice call @David Wasser. Looks like we're a step closer to solving this.

MainActivity@be81228 (started first, gets Destroyed)
MainActivity@d0faced (started second, stays alive)

Since it looks like we are starting MainActivity twice, I have added the ScreenSaver code which starts MainActivity.

@Override
public void onUserInteraction(){
    super.onUserInteraction();

    // Boolean for tracking screensaver
    isScreenSaverActive = false;

    if(animation != null){
        if(animation.isRunning(){
            animation.stop();
        }
    }

    finish();

    Intent i = new Intent(this, MainActivity.class);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
               Intent.FLAG_ACTIVITY_CLEAR_TOP);
    startActivity(i)

    Log.e("Tracker", "ScreenSaver onUserInteraction() starting MainActivity);
}

The log in the above function, which I just added, presents us with a useful hint:

E/Tracker: ScreenSaver onUserInteraction starting MainActivity
E/Tracker: ScreenSaver onUserInteraction starting MainActivity
E/Tracker: onCreate() Boolean Value = true
E/Tracker: onCreate() called from com.x.x.MainActivity@bcb1a2c
E/Tracker: onResume() Boolean Value = true
E/Tracker: onResume() called from: com.x.x.MainActivity@bcb1a2c
E/Tracker: onPause() Boolean Value = false
E/Tracker: onPause() called from com.x.x.MainActivity@bcb1a2c
E/Tracker: onCreate() Boolean Value = true
E/Tracker: onCreate() called from com.x.x.MainActivity@6ad37bf
E/Tracker: onResume() Boolean Value = true
E/Tracker: onResume() called from: com.x.x.MainActivity@6ad37bf
E/Tracker: onWindowFocusChanged() called from com.x.x.MainActivity@6ad37bf
E/Tracker: onStop() called from com.x.x.MainActivity@bcb1a2c
E/Tracker: onDestroy() called from: com.x.x.MainActivity@bcb1a2c

It looks like onUserInteraction() is responding twice to a KeyEvent. Possibly onKeyDown() and onKeyUp(). So, of course I logged this too:

E/Tracker: ScreenSaver onUserInteraction starting MainActivity
E/Tracker: ScreenSaver onKeyDown() BACK BUTTON
E/Tracker: ScreenSaver onUserInteraction starting MainActivity
E/Tracker: ScreenSaver onKeyUp() BACK BUTTON
// Same as above for the remainder of logs

Looks like KeyDown/KeyUp must be the issue - for the back button, volume buttons.. and probably card view, bixby, and home button, which I haven't even thought to test.

For now, I made a simple workaround, and it works. However, I feel that there should be a more graceful approach. Any ideas are welcome.

private boolean runOnce = false;

@Override
public void onUserInteraction(){

    if(!runOnce){

       runOnce = true;
        // Start MainActivity

    }
}

So, I guess the moral of the story is that starting the same activity twice, simultaneously, can cause onDestroy() to be called after the "surviving" clone activity has been created. The only reason it was noticeable to me was because my boolean value updates were actually updating a static variable in a Singleton class, used for popup management in background services and a few other classes. Otherwise, it probably would have gone unnoticed.

EDIT (March 22, 2019)

Android Manifest for MainActivity

    <activity
        android:name=".MainActivity"
        android:screenOrientation="sensorLandscape" />

ScreenSaver Class, onUserInteraction()

@Override
public void onUserInteraction(){
    super.onUserInteraction();

    if(!runOnce){

        runOnce = true;

        SingletonScreenSaverTracker.isScreenSaverActive = false;

        if(animation != null){

            if(animation.isRunning()){
                animation.stop();
            }

        }

        finish();

        Intent i = new Intent(this, MainActivity.class);
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK   |
                Intent.FLAG_ACTIVITY_CLEAR_TOP);

        startActivity(i);
    }
}

Solution

  • Using the function onUserInteraction() can cause some issues if you aren't careful. Pressing a button will actually fire the method twice, during KeyDown and KeyUp events. If using this method to start an activity, tapping the screen will work fine. However, pressing a key will call the method twice, attempting to start the activity twice, simultaneously.

    When a two activities are started simultaneously, android recognizes this and kills one of the clones. As logs demonstrated, the first instance is wiped out, but not until after the second instance has been fully created and has window focus. Thus, I was able to see onDestroy() called on my started activity, after onCreate().