Reputation: 382
Since I experienced crashes of this genre:
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
I started wondering of the right usage of the context for a startActivity-Intent.
This was my Kotlin-Code (Activity -> Activity):
btn_scan.setOnClickListener {
val mIntent = Intent(applicationContext, Scanner::class.java)
mIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
mIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
applicationContext.startActivity(mIntent)
}
which I was able to fix with "this":
btn_scan.setOnClickListener {
val mIntent = Intent(this, Scanner::class.java)
mIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
mIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
this.startActivity(mIntent)
}
But now I'm a little bit insecure of the right usage of this, since accessing it from a inner-function for example needs this@ActivityName. So I'd like to ask you politely to explain me how to know which is the right context, when starting an Activity from an Activity, from a Fragment or from a function or using CoroutineScope
Thank you
Upvotes: 2
Views: 809
Reputation: 1565
if use kotlin
btn_scan.setOnClickListener {
val mIntent = Intent(this, Scanner::class.java)
mIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
this.startActivity(mIntent)
}
Upvotes: 0
Reputation: 13009
Not sure if I can answer all your questions but here's my two cents:
Your code looked like this when your app crashed:
btn_scan.setOnClickListener {
val mIntent = Intent(applicationContext, Scanner::class.java)
mIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
mIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
applicationContext.startActivity(mIntent)
}
The error message said you should use FLAG_ACTIVITY_NEW_TASK
when starting an Activity
from outside of an Activity
context. You used the Application
context which is like a "cousin" of the Activity
context inheritance-wise (see ContextWrapper for subclasses and their relations), so the Intent
flags were checked and the required flag was missing.
But why can the flag be missing if you set it explicitly?
mIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
That's because you assign another value to mIntent.flags immediately after this:
mIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
If you want to have both flags you have to add them:
mIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + Intent.FLAG_ACTIVITY_CLEAR_TOP
Now the app no longer crashes.
But is the Application
context even necessary here? Your code is part of an Activity
after all, and Activity
is an indirect subclass of Context
.
You possibly tried the following and it worked:
btn_scan.setOnClickListener {// Note: your IDE tells you "it: View!"
val mIntent = Intent(applicationContext, Scanner::class.java)
mIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
mIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
this.startActivity(mIntent)
}
Because inside the OnClickListener
lambda, the View
is referred to by "it", there is no ambiguity: "this" refers to the Activity
, no crash.
(Of course now you could skip the Intent.FLAG_ACTIVITY_NEW_TASK
)
Things look differently when you have something like this:
with(btn_scan){ // this: Button!
setOnClickListener{ // it: View!
val mIntent = Intent(applicationContext, Scanner::class.java)
mIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + Intent.FLAG_ACTIVITY_CLEAR_TOP
this.startActivity(mIntent)
}
}
Now your IDE won't accept this.startActivity(mIntent)
. This is because here "this" refers to the Button
. If you want another, "outer this", you have to say explicitly which one. In Kotlin, you do so by adding @ActivityName.
I suppose the same goes for coroutine code inside an Activity
(although I must confess I'm not yet a coroutine expert): whenever the Activity
"this" is hidden behind some local "this", you need the annotation.
Back to familiar ground: the context
property of a Fragment
is the Activity
's context if the Fragment
is attached to an Activity
, else it is null
. So if it's not null
you can use it for starting an Activity
.
But you can even use the Button
's context
property, since it is identical to the Activity
context as well:
with(btn_scan){ // this: Button!
setOnClickListener{
val mIntent = Intent(applicationContext, Scanner::class.java)
mIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
this.context.startActivity(mIntent)
}
}
Upvotes: 3