4

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

aminography
  • 21,986
  • 13
  • 70
  • 74
Yarh
  • 4,459
  • 5
  • 45
  • 95

5 Answers5

4

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

aminography
  • 21,986
  • 13
  • 70
  • 74
  • you solution will not work. In your code event buss is unregestered in onStop. So all activities in backstack will no receive this event – Yarh Oct 30 '18 at 10:58
  • the strange esenario you mentioned - is exactly my case) – Yarh Nov 05 '18 at 10:07
3

Before launching splash screen add this line

ActivityCompat.finishAffinity(this)
Sachin Kasaraddi
  • 597
  • 5
  • 12
2

I dont know what exactly you were trying to do but i got a feeling you could redesign your app differently to do it better.

Anyway to your question - I guess you can check onStart of the activities if the user has logged off, and if he did start a single instance launcher activity and close these activities with finish().

Ofek Ron
  • 8,354
  • 13
  • 55
  • 103
1

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

Here is one way:

  1. Have all the activities extend a custom BaseActivity.
  2. You can use LocalBroadcastManager to send local broadcast (within your app), when the logout button is clicked.
  3. Inside the base activity you can implement the listener. You can call finish() inside the listener.
  4. So when user clicks the logout button, you send the local broadcast to all the activities open. Since all your activities extend the common BaseActivity, the listener gets called and the activities finish.
  5. After sending the broadcast, you can open the intended LauncherActivity.

See How to use LocalBroadcastManager? for more.

P.S: You can un-register the listener in onDestroy. Since activity is still present, onDestroy won't have been called. And if it has already been destroyed then you have one less activity to worry about.

Shobhit Puri
  • 25,769
  • 11
  • 95
  • 124
  • where do you unregisteer broadcastreceiver? onPause, onStop, onDestroy will be called prior i launch Logout activity. – Yarh Oct 30 '18 at 10:59
  • Good question. You can do it inside the `onDestroy`. Since your activity's instance is still existing, I don't think onDestroy will be called. onStop will definitely be called since your new activity is completely hiding the old activities. – Shobhit Puri Oct 30 '18 at 18:30
1

In my experience extending the Application class is the simpler and most effective way to store limited amount of data which needs to be shared among all the activities.

In your case you can create a class holding your login data and store its instance in your custom Application object, where it can be reached by all activities. They can check for login availability at start, subscribe for changes and get notified when they need to finish. The Application object itself can subscribe for changes and start the login activity if needed.

gxcare
  • 511
  • 4
  • 12