Search code examples
javascriptandroidtimersetintervalscreen-rotation

Why setIntervals cannot be killed when the screen was rotated in Android?


Fellow Developers,

I'm designing a Timer, which should be able to keep the time even when the screen is rotated, but I have noticed a very strange behaviour with the following code:

function startTimer() {
    var setTime = setInterval(function () {
        console.log('increasing');
        if (isPaused)
            clearInterval(setTime);
        else {
            counter++;
            setLocalStorage("counter", counter + 1);
        }
    }, 1000);
}

function setLocalStorage(key, val) {
    if (getLocalStorageValue(key) !== null)
        removeLocalStorage(key);
    localStorage.setItem(key, val);
}

function getLocalStorageValue(key) {
    return localStorage.getItem(key);
}

function removeLocalStorage(key) {
    localStorage.removeItem(key);
}

I discovered that after rotating the screen, the counter is still increasing without stop and the console.log is not even printed in the console. I suppose the setInterval is somewhere continue running in parallel and I cannot find a way to stop it because I could change the variable of isPaused to true, but the counter is still on going without a valid reason and it stops until the app is fully closed. Have any of you ever experienced something similar?

Also, from the Android side I'm fully reloading each time the HTML page then it shouldn't keep anything or do you know any way to fully reload the browser each time?

Also, I added this code in Android and nothing happens:

@Override
protected void onDestroy() {
    super.onDestroy();
    WebView.pauseTimers();
}

Thanks for your advice or support.


Solution

  • After some research I found how to fix the issue and what is happening.

    First of all if you're running a setInterval and you don't clear it in a WebView when you rotate the screen, the original setInterval is going to continue running somewhere in the background and if you rotate it again and you were running a second time the timer, it's going to create another interval and so on and so on.

    If you want to be able to fix it you need to clean the page every time the screen is rotated. How did I do it?

    A small comment before to proceed, my solution is written in C# since I'm building the app in Xamarin.Android; however, you can translate it to Java or Kotlin in a blink of an eye and it looks like this:

    1. Declare a static variable for checking if the app is resuming for the first time or not:

      public static bool RUN_ONCE_RESUME { get; set; } = false;
      
    2. Override the OnResume and OnDestroy methods as follows:

      The OnResume must re-enable the timers if it's not the first time:

      protected override void OnResume()
      {
          base.OnResume();
      
          if (RUN_ONCE_RESUME)
          {
              WebView.ResumeTimers();
          }
          RunOnceResume();
      }
      

      The OnDestroy must kill the entire WebView as bellow:

      protected override void OnDestroy()
      {
          base.OnDestroy();
      
          WebView.RemoveAllViews();
          WebView.ClearHistory();
          WebView.ClearCache(true);
          WebView.LoadUrl("about:blank");
          WebView.OnPause();
          WebView.RemoveAllViews();
          WebView.PauseTimers();
          WebView.Destroy();
          WebView = null;
      }
      
    3. The methods for checking if it's the first time or not:

      private void RunOnceResume()
      {
          if (!RUN_ONCE_RESUME)
          {
              RUN_ONCE_RESUME = true;
          }
      }
      

    I tried without the previous method and it didn't work in my case.

    With all these changes the timer would work as expected.