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.
- onCreate()
- onResume()
- onPause()
- onCreate()
- onResume()
- onWindowFocusChanged()
- onStop()
- 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);
}
}