Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Architecture réactive avec les Coroutines

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Architecture réactive avec les Coroutines

PAUG Juillet 2018

Avatar for Pierre Degand

Pierre Degand

July 23, 2018

Other Decks in Programming

Transcript

  1. De quoi parle-t-on ? dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutines}"

    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}" } kotlin { experimental { coroutines 'enable' } }
  2. De quoi parle-t-on ? dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutines}"

    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}" implementation "androidx.lifecycle:lifecycle-livedata:${versions.lifecycle}" implementation "androidx.lifecycle:lifecycle-extensions:${versions.lifecycle}" kapt "androidx.lifecycle:lifecycle-compiler:${versions.lifecycle}" } kotlin { experimental { coroutines 'enable' } }
  3. Les Coroutines - Kessékessa ? Unité de calcul suspendable Exprimer

    du code asynchrone et non bloquant de manière synchrone
  4. (avant) Les Coroutines fun requestTokenAsync(callback: (String) -> Unit) {} fun

    createUserAsync(token: String, callback: (User) -> Unit) {} fun displayUser(user: User) {}
  5. (avant) Les Coroutines fun requestTokenAsync(callback: (String) -> Unit) {} fun

    createUserAsync(token: String, callback: (User) -> Unit) {} fun displayUser(user: User) {} fun callbacks() { requestTokenAsync { token -> createUser(token) { user -> displayUser(user) } } }
  6. Les Coroutines fun coroutines() { val token = requestToken() val

    user = createUser(token) displayUser(user) }
  7. Les Coroutines fun requestToken(): String {} fun createUser(token: String): User

    {} fun displayUser(user: User) {} fun coroutines() { val token = requestToken() val user = createUser(token) displayUser(user) }
  8. Les Coroutines suspend fun requestToken(): String {} suspend fun createUser(token:

    String): User {} fun displayUser(user: User) {} fun coroutines() { val token = requestToken() val user = createUser(token) displayUser(user) }
  9. Les Coroutines suspend fun requestToken(): String {} suspend fun createUser(token:

    String): User {} fun displayUser(user: User) {} fun coroutines() { launch { val token = requestToken() val user = createUser(token) displayUser(user) } }
  10. Les Coroutines suspend fun requestToken(): String {} suspend fun createUser(token:

    String): User {} fun displayUser(user: User) {} fun coroutines() { launch(context = CommonPool) { val token = requestToken() val user = createUser(token) withContext(UI) { displayUser(user) } } }
  11. Les Coroutines suspend fun requestToken(): String {} suspend fun createUser(token:

    String): User {} fun displayUser(user: User) {} fun coroutines() { launch(context = CommonPool) { val token = requestToken() val user = createUser(token) withContext(UI) { displayUser(user) } } } KotlinConf 2017 - Introduction to Coroutines by Roman Elizarov https://youtu.be/_hfBv0a09Jc KotlinConf 2017 - Deep Dive into Coroutines on JVM by Roman Elizarov https://youtu.be/YrrUCSi72E8
  12. Les Channels interface Channel<E> : SendChannel<E>, ReceiveChannel<E> { suspend fun

    send(element: E) suspend fun receive(): E } val channel = Channel<Int>() launch { repeat(5) { print(channel.receive()) } } launch { for (x in 1..5) channel.send(x * x) }
  13. Les Channels - Iterator interface Channel<E> : SendChannel<E>, ReceiveChannel<E> {

    suspend fun send(element: E) suspend fun receive(): E operator fun iterator(): ChannelIterator<E> } interface ChannelIterator<out E> { suspend operator fun hasNext(): Boolean suspend operator fun next(): E }
  14. Les Channels - Iterator interface Channel<E> : SendChannel<E>, ReceiveChannel<E> {

    suspend fun send(element: E) suspend fun receive(): E operator fun iterator(): ChannelIterator<E> } interface ChannelIterator<out E> { suspend operator fun hasNext(): Boolean suspend operator fun next(): E } val channel = Channel<Int>() launch { for (value in channel) { print(value) } } launch { for (x in 1..5) channel.send(x * x) channel.close() }
  15. Les Channels - Capacity val channel = Channel<Int>(capacity = Int)

    public fun <E> Channel(capacity: Int): Channel<E> = when (capacity) { 0 -> RendezvousChannel() }
  16. Les Channels - Capacity val channel = Channel<Int>(capacity = Int)

    public fun <E> Channel(capacity: Int): Channel<E> = when (capacity) { 0 -> RendezvousChannel() else -> ArrayChannel(capacity) }
  17. Les Channels - Capacity val channel = Channel<Int>(capacity = Int)

    public fun <E> Channel(capacity: Int): Channel<E> = when (capacity) { 0 -> RendezvousChannel() Channel.UNLIMITED -> LinkedListChannel() else -> ArrayChannel(capacity) }
  18. Les Channels - Capacity val channel = Channel<Int>(capacity = Int)

    public fun <E> Channel(capacity: Int): Channel<E> = when (capacity) { 0 -> RendezvousChannel() Channel.UNLIMITED -> LinkedListChannel() Channel.CONFLATED -> ConflatedChannel() else -> ArrayChannel(capacity) }
  19. Les Channels val channel = Channel<Int>() launch { print("channel#1 ${channel.receive()}")

    } launch { print("channel#2 ${channel.receive()}") } launch { channel.send(1) }
  20. Les Channels val channel = Channel<Int>() launch { print("channel#1 ${channel.receive()}")

    } launch { print("channel#2 ${channel.receive()}") } launch { channel.send(1) } > channel#1 1
  21. Les Channels - Broadcast val channel = BroadcastChannel<Int>(1) launch {

    println("channel#1 ${channel.openSubscription().receive()}") } launch { println("channel#2 ${channel.openSubscription().receive()}") } launch { channel.send(1) } > channel#1 1 > channel#2 1
  22. Model Réactif interface UserStore { val user: User fun setName(name:

    String) fun setAge(age: Int) } Pas très réactif ...
  23. Model Réactif interface UserStore { val user: ReceiveChannel<User> suspend fun

    setName(name: String) suspend fun setAge(age: Int) }
  24. Model Réactif class UserStoreImpl : UserStore { private val _user

    = ConflatedBroadcastChannel<User>() override val user: ReceiveChannel<User> get() = _user.openSubscription() override suspend fun setName(name: String) { val newUser = // on update le user sur une API par exemple ... _user.send(newUser) } override suspend fun setAge(age: Int) { val newUser = // on update le user sur une API par exemple ... _user.send(newUser) } }
  25. Model Réactif class UserViewModel(val userStore: userStore) : ViewModel() { private

    val parentJob = Job() val userLiveData = MutableLiveData<User>() init { launch(context = UI, parent = parentJob) { for (user in userStore.user) { userLiveData.value = user } } } override fun onCleared() { parentJob.cancel() } }
  26. Model Réactif class UserViewModel(val userStore: UserStore, val userPresenter: UserPresenter) :

    ViewModel() { private val parentJob = Job() val userLiveData = MutableLiveData<UserView>() init { launch(context = UI, parent = parentJob) { userStore.user.map { user -> userPresenter.presentUser(user) }.consumeEach { userView -> userLiveData.value = userView } } } ... }
  27. Channels - Extensions fun <T, R> ReceiveChannel<T>.toLiveData( parent: Job, context:

    CoroutineContext, block: (T) -> R ): LiveData<R> { val liveData = MutableLiveData<R>() launch(parent = parent, context = context) { consumeEach { liveData.postValue(block(it)) } } return liveData }
  28. Model Réactif class UserViewModel(userStore: UserStore, userPresenter: UserPresenter) : ViewModel() {

    private val parentJob = Job() val userLiveData = userStore.user.toLiveData( context = UI, parent = parentJob, block = userPresenter::presentUser ) }
  29. Model Réactif class UserViewModel( private val userStore: UserStore, userPresenter: UserPresenter

    ) : ViewModel() { ... val userLiveData = userStore.user.toLiveData( context = UI, parent = parentJob, block = userPresenter::presentUser) fun updateUserName(newName: String) { launch(context = CommonPool, parent = parentJob) { userStore.setName(newName) } } }
  30. Merci ! Référence ultime : https://github.com/Kotlin/kotlinx.coroutines • Tous les liens

    utiles (confs, doc) sont dans le README Juliette Dompé Pierre Degand @p_degand