2

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

KayD
  • 372
  • 5
  • 17
  • Does this answer your question? [What's the difference between the various methods to get a Context?](https://stackoverflow.com/questions/1026973/whats-the-difference-between-the-various-methods-to-get-a-context) – Bö macht Blau Jan 27 '20 at 18:05
  • Partially, still the this and the this@Activity isn't 100% clear – KayD Jan 27 '20 at 18:21

2 Answers2

3

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)
        }
    }
Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
  • You explained perfectly the Flag problem. One question to that. So for the CLEAR_TOP I was accessing the applicationContext of the new activity already, that's why it caused the crash, right? – KayD Jan 27 '20 at 21:19
  • Well that aren't just 2 cents, that's what I was looking for! For now, I solved it with val mIntent= Intent(this,Scanner::class.java) ... and this.startActivity(mIntent) and it seems to work everywhere. – KayD Jan 27 '20 at 21:19
  • 1
    @Los Kayos - thanks for letting me know :) About "for the CLEAR_TOP I was accessing the applicationContext of the new activity" - AFAIK any app component can only ever access its own application context (directly as a property or indirectly depends on the type of component) – Bö macht Blau Jan 28 '20 at 05:48
  • as I've seen on the commis below: what's better to use "Intent.FLAG_... + Intent.FLAG_" or "Intent.FLAG or Intent.FLAG_.."? – KayD Jan 29 '20 at 21:40
  • and one last question, from what I've understood (and lease correct my now or never), it is ok to use "this" for Intent(this,Scanner::class.java) and also this.startActivity for a simple onClickListener without any other local "this" around. Right? – KayD Jan 29 '20 at 21:45
  • @Los Kayos - "yes" in Kotlin because inside of the lambda expression the View will be "it". When in doubt in any situation, you could use the debugger to get more information on what's hiding behind "this" – Bö macht Blau Jan 30 '20 at 05:07
  • 1
    ' what's better to use "Intent.FLAG_... + Intent.FLAG_" or "Intent.FLAG or Intent.FLAG_.."' - I like "+" better in this case, it's closer to "and" , so it's similar to what I would say in English – Bö macht Blau Jan 30 '20 at 05:11
  • 1
    Once more about the "this": my comment is valid if the OnClickListener is part of the code of an Activity, in a Fragment you have to write this.context when a Context is required – Bö macht Blau Jan 30 '20 at 05:17
0

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)    
}
Foroogh Varmazyar
  • 1,057
  • 1
  • 14
  • 18