Skip to content

Commit

Permalink
Utilize actor pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Banes committed Jun 18, 2020
1 parent 93cc1f2 commit 29ff6c4
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 20 deletions.
62 changes: 42 additions & 20 deletions coil/src/main/java/dev/chrisbanes/accompanist/coil/Coil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.Composable
import androidx.compose.getValue
import androidx.compose.launchInComposition
import androidx.compose.onCommit
import androidx.compose.onDispose
import androidx.compose.remember
import androidx.compose.setValue
import androidx.compose.state
Expand Down Expand Up @@ -122,28 +123,20 @@ fun CoilImage(
loading: @Composable (() -> Unit)? = null,
onRequestCompleted: (RequestResult) -> Unit = emptySuccessLambda
) {
var requestSize by state { IntPxSize.Zero }
var result by state<RequestResult?> { null }

launchInComposition(requestSize) {
val requestWidth = requestSize.width.value
val requestHeight = requestSize.height.value
val requestActor = remember(request) {
CoilRequestActor(request) { result = it }
}

result = when {
request.sizeResolver != null -> {
// If the request has a sizeResolver set, we just execute the request as-is
request.execute()
}
requestWidth > 0 && requestHeight > 0 -> {
// If we have a non-zero size, we can modify the request to include the size
request.newBuilder()
.size(requestWidth, requestHeight)
.build()
.execute()
}
// Otherwise we have a zero size, so no point executing a request
else -> null
}
launchInComposition(requestActor) {
// Launch the Actor
requestActor.run()
}

onDispose {
// When we leave the composition, close the Actor channel
requestActor.close()
}

onCommit(result) {
Expand All @@ -169,7 +162,10 @@ fun CoilImage(
else -> null
}

val mod = modifier.onSizeChanged { requestSize = it }
val mod = modifier.onSizeChanged { size ->
// When the size changes, send it to the request actor
requestActor.send(size)
}

if (painter == null) {
// If we don't have a result painter, we add a Box with our modifier
Expand All @@ -191,6 +187,32 @@ fun CoilImage(
}
}

private fun CoilRequestActor(
request: GetRequest,
onResult: (RequestResult?) -> Unit
) = RequestActor<IntPxSize, RequestResult?>(
execute = { size ->
val requestWidth = size.width.value
val requestHeight = size.height.value
when {
request.sizeResolver != null -> {
// If the request has a sizeResolver set, we just execute the request as-is
request.execute()
}
requestWidth > 0 && requestHeight > 0 -> {
// If we have a non-zero size, we can modify the request to include the size
request.newBuilder()
.size(requestWidth, requestHeight)
.build()
.execute()
}
// Otherwise we have a zero size, so no point executing a request
else -> null
}
},
onResult = onResult
)

/**
* Represents the result of an image request.
*/
Expand Down
43 changes: 43 additions & 0 deletions coil/src/main/java/dev/chrisbanes/accompanist/coil/RequestActor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dev.chrisbanes.accompanist.coil

import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.distinctUntilChanged

internal class RequestActor<Param, Result>(
private val execute: suspend (Param) -> Result,
private val onResult: (Result) -> Unit
) {
private val channel = Channel<Param>(Channel.CONFLATED)

suspend fun run() {
channel.consumeAsFlow()
.distinctUntilChanged()
.collect { param -> onResult(execute(param)) }
}

fun send(param: Param) {
channel.offer(param)
}

fun close() {
channel.close()
}
}

0 comments on commit 29ff6c4

Please sign in to comment.