Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide MainScope factory #829

Closed
qwwdfsad opened this issue Nov 13, 2018 · 1 comment
Closed

Provide MainScope factory #829

qwwdfsad opened this issue Nov 13, 2018 · 1 comment
Assignees

Comments

@qwwdfsad
Copy link
Collaborator

qwwdfsad commented Nov 13, 2018

We should provide factory method fun MainScope() = CoroutineScope(Dispatchers.Main + SupervisorJob()).

It fixes multiple issues (especially with the #828):

  1. Integration with UI components are simple both for implementing an interface and having it in a field
  2. It protects newcomers from async behaviour when it is launched from UI component
  3. It simplifies guides and documentation
@elizarov
Copy link
Contributor

Let me add some more rationale here and additional suggestions. With this change an introduction to programming with coroutines for Android developers (see https://codelabs.developers.google.com/codelabs/kotlin-coroutines/index.html#4) could give the following example on "Controlling the UI with coroutines":

class MainViewModel(private val repository: TitleRepository) : ViewModel() {
    // ... 
    private val uiScope = MainScope()
    
    override fun onCleared() {
        super.onCleared()
        uiScope.cancelCoroutineScope()
    } 
}

This gives the following benefits:

  1. It avoids introducing the concepts of Job and CoroutineContext to novice developers.

  2. It internally uses SupervisorJob() instead of Job(), which generally has less surprising behavior here as opposed to Job(), since a crash of a single coroutine would only lead to a crash of this particular coroutine. It does not matter by default (where that would usually crash an application), but this plays nicely with custom coroutine exception handlers for advanced users, which could use them like this:

    private val uiScope = MainScope() + CoroutineExceptionHandler { ctx, ex ->
         /* do something on error -- for example, log but don't crash. */ 
    }

It would not have been this easy with Job(), because even if you handle exception, the Job() is cancelled after the crash and all future coroutines fail to start.

Note the name of cancelCoroutineScope() extension here. It is long and verbose for a reason, because a short name like cancel() could get quickly confusing in cases where CoroutineScope is serving as this implicit receiver scope:

launch { // note: this: CoroutineScope
    // ... other code
    cancel() // what are we cancelling here? 
}

This naming also plays nicely with the following pattern of using MainScope() by interface delegation:

class MainViewModel(
    private val repository: TitleRepository
) : ViewModel(), CoroutineScope by MainScope() {
     // ... 
    override fun onCleared() {
        super.onCleared()
        cancelCoroutineScope()
    } 
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants