Does it make sense to have a job and does not assi...
# coroutines
p
Does it make sense to have a job and does not assign it to the
launch{}
? Example :
Copy code
private val job: Job? = null

internal class Foo : CoroutineScope {
  override val coroutineContext: CoroutineContext
   get() = Dispatchers.Main + Job!!

   ...

   private fun bar() {
    job = Job()

    launch { .... }
   }

   override onDetach() {
    job.cancel()
   }
}
Does it really cancel the launch?
🚫 1
@Oliver.O Even if that's the job that compose the coroutineContext?
o
If your job is meant to do something, you would want to use
Copy code
job = launch { ... }
Then
job.cancel()
would cancel the launched job.
m
not sure about the
get() = Dispatchers.Main + Job
. Arent you creating a new job everytime you request it ?
a
Setting aside that this is ugly code with double hash bangs. The simple answer is yes. It will cancel the job, and here is why. Calling
onDetach()
will cancel the whole scope, and its child coroutines, including the one launched by
launch{}
Does it make sense to have a job and not assign it launch?
In some cases yes, You might need a job associated with a whole scope, not just child coroutines. But I wouldn't use your current approach to achieve that
u
It should work assuming
Job!!
was meant to be
job!!
and not
Job()!!
. At least if you call `bar()`only once. Coroutine scopes organise their jobs in a hierarchical structure. So the Job created by
this@CoroutineScope.launch
becomes a child job of
job
. And
job.cancel()
will cancel
job
and all its children. Annotations: 1. Looks confusing that
job
is global. You should make it a member. 2. initialize
job
centrally and once (per instance) 3. The pattern of implementing
CoroutineScope
was suggested at the early times of coroutines. Nowadays it is mostly agreed to be more expressive to have a `CoroutineScpoe`member. You probably want something like this which makes your intentions quit obvious
Copy code
internal class Foo {
  val fooScope = CoroutineScope(Dispatchers.Main + Job())
   ...

   private fun bar() {
    fooScope.launch { .... }
   }

   override onDetach() {
    fooScope.cancel()
   }
}
p
@andylamax what would be your approach? something like @uli did?
@uli but if I implement
CoroutineScope
then it's attached to the lifecycle of the class right? If the class it's a custom view in Android when it detaches from the screen it should be cancelled (in case I cancel it)
m
‘it should work’ is hardly encouraging. Doesn’t the getter always creates a new job ?
p
I did not post the full code, but it assigns the job once the view is created (an override method that is just called once) and then it cancels it when it's dettached
But the thing is, is it necessary to implement CoroutineScope? Or can I do it like Uli said? Creating it mannually? which are the differences of doing it?
u
@myanmarking it creates a new context but reuses the job from
val job
p
Does this code makes sense just to control the hob that compose the coroutineContext and not the job created when launch the new coroutine?
But I'd like to improve it a little bit, because I don't find it attractive at all..
u
Copy code
@uli but if I implement CoroutineScope then it's attached to the lifecycle of the class right?
@Pablo take a look at the declaration of CoroutineScope. There is no lifecycle binding there. But there are androidx libraries for viewmodel scope et al
p
Note that it's a view that it has a method that is called when it's attached to the screen (just once) now is where I have the
Copy code
job = Job()
super.attachedToScreen()
And then there's a method called once when it's dettached that I want to cancel it.
So, @uli should be better to create a variable field
CoroutineScope
instead of implementing it?
Which advantages are doing it? I would like to understand it please
And in your case u are cancelling the scope not the job, is there any difference?
u
look at the implementation. It’s just a Click away in Android Studio (IntelliJ IDEA)
The advantage of a variable is just readability. You scope becomes explicit and I guess you would have had no doubt, that the following cancels your coroutine.
Copy code
fooScope.launch { } 
fooScope.cancel()
It is fully equivalent to what you tried to do. Just More explicit.
p
But since I need the Dispatchers.Main, couldn't be better (since I'm not passing the dispatcher via parameter) use the
MainScope()
instead? What I'd like to know is, why in your approach you cancel the scope and in my approach I cancel the Job, I know if I cancel the job (from my question) I'd cancel everything is launched in the
launch
, right?
u
Did you look at the implementation of
CoroutineScope.cancel()
?
On Mac it is Cmd+Click in Android Studio. On Windows probably something similar.
p
yes, it gets the job of it and it cancels
u
There simply is no need to remember the
job
in a separate val. CoroutineScope already remembers it for you
So it is equivalent
If you like, you could also store the job and then cancel the job. Only question is why .
p
Yup I see, but I remember that you want to use the implementation of
CoroutineScope
in case you want to be attached to your lifecycle of that class, right? But do I have to add it somewhere?
Something like, if your class gets destroyed it autommatically cancels all the scopes on it
u
you are linked to the lifecycle by calling
cancel
in
onDetach
Copy code
Something like, if your class gets destroyed it autommatically cancels all the scopes on it
No. And implementing an interface also does not do that
p
Then reading the documentation I should use the ### Custom usage CoroutineScope should be declared as a property on entities with a well-defined lifecycle that are responsabile for laucnhing children coroutines... etc
u
Again. Look at the source of CoroutineScope. Implementing that interface does not do anything else but what is written there explicitly. No magic
p
ok, thanks for your time Uli
🙂
Last one question @uli would be the same using your approach
Copy code
val fooScope = CoroutineScope(Dispatchers.Main + Job())
Than doing this
Copy code
val fooScope = MainScope()
? I see that
MainScope()
is creating a
SupervisorJob()
instead of a
Job()
. In terms of efficiently what do you think it's better?
u
CoroutineScope basically provides a CoroutineContext that can be used by CoroutineScope.extensionFunctions like CoroutineScope.launch. In your initial solution that context would be a member of your own class. In the explicit version you have a separate object to carry that context around.
p
So the second approach is "readable" for you, but does the same (ignoring the job stuff that I could cancel directly the scope since I'm not doing anything special with the job)
u
Right
regarding SupervisorScope. It is not a question of efficiency. It is a question of desired behaviour. What do you want to happen if one of your launched coroutines fail?
p
I will only have 1 job, because in that class I call just
launch{}
once
if it fails I'd show a message saying it failed, anything else.
u
Then it does not matter. Still if potential multiple jobs were related use
Job
. If the jobs are more unrelated use a
SupervisorJob
.
p
It was about to use directly
MainScope()
or just the way you typed it with
Dispatchers.Main
u
Usually in ViewModels you would use SuperrvisorJob because one failed job should normaly not affect other jobs
p
I'm doing it in a custom View
Where I just have 1 job
u
Go with MainScope
or, to be lifecycle aware, use lifecyle scopes: https://developer.android.com/topic/libraries/architecture/coroutines
p
I can assign the scope lifecycle of the customview? will I need to cancel it in onDettachToWindow method then?
u
no, that’s what it will do for you. If your lifecycle get’s destroyed then.
Copy code
Any coroutine launched in this scope is canceled when the Lifecycle is destroyed.
https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope
p
And if it's a custom view even if I have this methods
onAttachToWindows
and
onDetachToWindows
I can get the lifeCycle of it (with dagger for instance and then pass it through parameter of constructor)
u
Can’t help yo with finding the right lifecycle. Not so much my topic