androidandroid-timer

Timer does not stop at old activity


I set a timer on AccountActivity.class to ensure that user does not press home button if not will start the countdown to logout the user or if the user locks his screen.

But now I am facing an issue because of the onPause method. When my user clicks on a button which invokes the getaccounttask() method and it will redirect my user to AccountInformationActivity.class, the onPause method is activated as well and the timer starts to countdown.

Is there any solution to prevent the onPause method from counting down or the timer to be cancelled on my AccountInformationActivity.class?

I tried to do the cancelling of timer before my intent starts but still does not work.

I have tried using handler as well but encountered the same problem, I am still trying to grasp how Android fully works, so your help or solution is deeply appreciated.

public class AccountActivity extends AppCompatActivity {

    private Timer timer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_account);
    }

    private class getaccounttask extends AsyncTask<String, Void, String>
    {
      @Override
      protected String doInBackground(String... urlaccount)
      {
        StringBuilder result = new StringBuilder();
        try
        {
            //My Codes
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return result.toString();
      }

      @Override
      protected void onPostExecute(String result)
      {
        Intent intent = new Intent();
        intent.setClass(getApplicationContext(), AccountInformationActivity.class);
        startActivity(intent);
      }
     }

    @Override
    protected void onPause() {
        super.onPause();

        timer = new Timer();
        Log.i("Main", "Invoking logout timer");
        LogOutTimerTask logoutTimeTask = new LogOutTimerTask();
        timer.schedule(logoutTimeTask, 300000); //auto logout in 5 minutes
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (timer != null) {
            timer.cancel();
            Log.i("Main", "cancel timer");
            timer = null;
        }
    }

    private class LogOutTimerTask extends TimerTask {

        @Override
        public void run() {

            //redirect user to login screen
            Intent i = new Intent(AccountActivity.this, MainActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(i);
            finish();
        }
    }
}

Solution

  • Well, the implementation architecture you choose could be improved. I shall come to that later. First apply this quick fix to fix your architecture.

    Clearly you want to start a Timer when you go out of Activity using home screen. But user can also go out of Activity by using Intent to switch to AccountActivity. This you can track. So keep a boolean flag like shouldNavigate, initially it should be false. when onResume, it should be set to false, but when getcounttask goes to onPostExecute it should be set to true. So in onPause, if you are going out via getcounttask,shouldNavigate is going to be true and if is true, cancel your Timer else, start your Timer.

    Code:

    public class AccountActivity extends AppCompatActivity {
    
        private Timer timer;
        private volatile boolean shouldNavigate = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_account);
        }
    
        private class getaccounttask extends AsyncTask<String, Void, String>
        {
          @Override
          protected String doInBackground(String... urlaccount)
          {
            StringBuilder result = new StringBuilder();
            try
            {
                //My Codes
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            return result.toString();
          }
    
          @Override
          protected void onPostExecute(String result)
          {
            shouldNavigate = true;
            Intent intent = new Intent();
            intent.setClass(getApplicationContext(), AccountInformationActivity.class);
            startActivity(intent);
          }
         }
    
        @Override
        protected void onPause() {
            super.onPause();
            if (!shouldNavigate){
                timer = new Timer();
                Log.i("Main", "Invoking logout timer");
                LogOutTimerTask logoutTimeTask = new LogOutTimerTask();
                timer.schedule(logoutTimeTask, 300000); 
            }else{
                if (timer != null){
                      timer.cancel();
                      timer = null;
                }
            }
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            shouldNavigate = false;
            if (timer != null) {
                timer.cancel();
                Log.i("Main", "cancel timer");
                timer = null;
            }
        }
    
        private class LogOutTimerTask extends TimerTask {
    
            @Override
            public void run() {
    
                //redirect user to login screen
                shouldNavigate = false;
                Intent i = new Intent(AccountActivity.this, MainActivity.class);
                i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(i);
                finish();
            }
        }
    }
    

    Now a better approach could be keep a Service and a Singleton class. The Singleton class would be

    public Singleton{
        private static Singleton instance = null;
        private Singleton(){
            activityMap = new HashMap<String, Activity>();
        }
    
        public static Singleton getInstance(){
             if (instance == null) instance = new Singeton();
             return instance;
        }
    
        public HashMap<String, Activity> activityMap;
    
    }
    

    Now each activity will have a Tag (like its name), so each activity when resumes will do

    Singleton.getInstance().activityMap.put(tag, this);
    

    and when goes to onPause will do

    Singleton.getInstance().activityMap.remove(tag, this);
    

    So when the service finds that size of Singleton.getInstance().activityMap is zero then clearly no activity is on foreground, so it starts a timer. when the timer expires check again if the count is still zero, if zero then perform your logout.