KayD
KayD

Reputation: 382

How do I call the context correctly to start an Activity

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

Answers (2)

Foroogh Varmazyar
Foroogh Varmazyar

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

Bö macht Blau
Bö macht Blau

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

Related Questions