Calling an Activity with FLAG_ACTIVITY_REORDER_TO_FRONT from a Service
Asked Answered
B

1

8

I am trying to call an activity (activity A) from a service and what I want to happen is to check if there's already an instance of A on the stack, and if there is, to bring that to the top of the stack (and trigger onNewIntent() method), instead of always creating a new instance of A.

Wondering if this is possible. My activity uses the "singleTop" launchmode in the androidmanifest. The usual Intent.FLAG_ACTIVITY_NEW_TASK flag that is required to call an activity from outside an activity doesn't bring the already open activity A to the top of the stack, but always creates a new instance of A. Also it seems that when I use both flags (intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) the same thing happens (still 2 instances of the same activity). Is there a way to go about doing this, always keeping in mind that I am making the call from a service, and I don't want to pass the activity context to the service?

(P.S. I am using androidannotations, the call to open activity A occurs inside an @EBean, which itself is used in a Service. Is there an easy way to somehow pass that activity context to that @EBean?)

Bricole answered 9/4, 2015 at 12:26 Comment(5)
You can use @RootContext.Connubial
@RootContext is not an instance of Activity when started from a Service. So it doesn't work, still requires that FLAG_ACTIVITY_NEW_TASKBricole
Sorry, if you inject the bean into the Service, of course the @RootContext will be the Sevice.Connubial
I'm having this same issue. Have you found a solution? Which context did you end up using?Villain
If you dont have a scenario where you might need 2 of them ever, Did you try using singleInstance launch mode?Emancipate
B
4

Since you want to use a non-activity context (e.g. applicationContext) to start the activity leading to use Intent.FLAG_ACTIVITY_NEW_TASK, you have only two choices to avoid creating a new instance of the target activity every time you're calling startActivity.

First, by specifying android:launchMode="singleInstance" in the manifest for the activity, you can force that only one instance of the activity should exist in a task that hosts only this instance. In this case, starting the activity brings it to the front if there exists in the host task, otherwise, a new task gets created containing the only instance of the activity. I think it's not the way we are looking for.

Second, by specifying android:launchMode="singleTask" in the manifest for the activity, we can achieve a better solution. In this case, the system creates a new task and adds the activity at the root of this task, if there exists no instance of the activity. Otherwise, the system brings the task containing the activity instance to the front then routes to the target activity, and calls onNewIntent.

Here is an example code of the second approach, examines 2 scenarios:

manifest.xml:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name=".ActivityA" android:launchMode="singleTask" />

<activity android:name=".ActivityB" />

MainActivity.kt:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // First Scenario: ActivityA doesn't exist in back-stack
        button1.setOnClickListener {
            Intent(applicationContext, ActivityA::class.java).apply {
                flags = Intent.FLAG_ACTIVITY_NEW_TASK
            }.let {
                applicationContext.startActivity(it)
            }
        }

        // Second Scenario: ActivityA exists in back-stack
        button2.setOnClickListener {
            startActivity(Intent(this, ActivityA::class.java))

            // Start ActivityB after a while
            Handler().postDelayed({
                startActivity(Intent(this, ActivityB::class.java))
            }, 1000)
        }
    }
}

ActivityA.kt

class ActivityA : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_a)

        Toast.makeText(this, "onCreate on ActivityA", Toast.LENGTH_SHORT).show()
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        Toast.makeText(this, "onNewIntent on ActivityA", Toast.LENGTH_SHORT).show()
    }
}

ActivityB.kt

class ActivityB : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_b)

        button.setOnClickListener {
            Intent(applicationContext, ActivityA::class.java).apply {
                flags = Intent.FLAG_ACTIVITY_NEW_TASK
            }.let {
                applicationContext.startActivity(it)
            }
        }
    }
}

Result:

.......

Blent answered 8/6, 2020 at 7:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.