Search code examples
androidkotlinback-stack

How to finish several SingleInstance Activities?


I have several activities with launchMode SingleInstance. On log out i want to finish all activities and open launchScreen.

val intent = Intent(context, LauncherActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
(context as AppCompatActivity).finishAffinity()
context.startActivity(intent)

However if i press back at Launcher activity i am forwarded to previously launched activities with singleInstance mode


Solution

  • UPDATE 11/1/2018:

    I've tested multiple approach to deal with it such as event propagation, intent flags, counting activity instances, etc. There is some weird scenarios such as starting multiple singleInstance activities sequentially. In this case, middle activities doesn't start at all (onCreate method isn't called) and after pressing back button, they'll be started. Therefore no one of the prior approaches works! As the issue is a bit strange, I tried to solve it using a bit strange way.

    We maintain the logout state in a singleton object called LogoutHandler. It cooperates with a class LogoutAwareActivity which is inherited by all activities except LoginActivity because it should not be affected by logout mechanism. When the logout occurs, a flag is set in the LogoutHandler until the last child of LogoutAwareActivity has finished then clears the flag.

    Here is an implementation of that:

    LogoutHandler:

    import java.util.*
    
    object LogoutHandler {
    
        private var isLogout = false
        private var timerWatchDog: TimerWatchDog? = null
    
        fun isLogout() = isLogout
    
        fun onActivityDestroyed() {
            if (isLogout) {
                timerWatchDog?.refresh(Runnable {
                    isLogout = false
                    timerWatchDog = null
                })
            }
        }
    
        fun logout() {
            isLogout = true
            timerWatchDog = TimerWatchDog(500)
        }
    
        private class TimerWatchDog(private val delay: Long) : Runnable {
    
            private var timer: Timer? = null
            private var runnable: Runnable? = null
    
            fun refresh(runnable: Runnable) {
                this.runnable = runnable
                timer?.cancel()
    
                val timerTask = object : TimerTask() {
                    override fun run() {
                        Thread(this@TimerWatchDog).start()
                    }
                }
                timer = Timer()
                timer?.schedule(timerTask, delay)
            }
    
            override fun run() {
                runnable?.run()
            }
        }
    
    }
    

    LogoutAwareActivity:

    import android.support.v7.app.AppCompatActivity
    
    abstract class LogoutAwareActivity : AppCompatActivity() {
    
        override fun onResume() {
            super.onResume()
            if (LogoutHandler.isLogout()) {
                finish()
            }
        }
    
        override fun onDestroy() {
            super.onDestroy()
            LoginHandler.onActivityDestroyed()
        }
    
    }
    

    A concrete Activity:

    class ActivityA : LogoutAwareActivity() {
    
        // ...
    }
    

    Another concrete Activity:

    class ActivityB : LogoutAwareActivity() {
    
        // ...
    }
    

    Your logout function:

    fun logout() {
        val intent = Intent(context, LoginActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    
        LogoutHandler.logout()
        context.startActivity(intent)
    }
    

    Visual Result:

    All of MainActivity, ActivityA, ActivityB and ActivityC are single instance.

    Traversing between activities by pressing back button:

    enter image description here

    Going to LoginActivity then pressing back button:

    enter image description here