From 29ff6c4ef6a641ee798fa17627f386d9d60faf89 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Wed, 17 Jun 2020 21:23:49 +0000 Subject: [PATCH] Utilize actor pattern --- .../dev/chrisbanes/accompanist/coil/Coil.kt | 62 +++++++++++++------ .../accompanist/coil/RequestActor.kt | 43 +++++++++++++ 2 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 coil/src/main/java/dev/chrisbanes/accompanist/coil/RequestActor.kt diff --git a/coil/src/main/java/dev/chrisbanes/accompanist/coil/Coil.kt b/coil/src/main/java/dev/chrisbanes/accompanist/coil/Coil.kt index 8e67fca1e..68b4e82dd 100644 --- a/coil/src/main/java/dev/chrisbanes/accompanist/coil/Coil.kt +++ b/coil/src/main/java/dev/chrisbanes/accompanist/coil/Coil.kt @@ -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 @@ -122,28 +123,20 @@ fun CoilImage( loading: @Composable (() -> Unit)? = null, onRequestCompleted: (RequestResult) -> Unit = emptySuccessLambda ) { - var requestSize by state { IntPxSize.Zero } var result by state { 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) { @@ -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 @@ -191,6 +187,32 @@ fun CoilImage( } } +private fun CoilRequestActor( + request: GetRequest, + onResult: (RequestResult?) -> Unit +) = RequestActor( + 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. */ diff --git a/coil/src/main/java/dev/chrisbanes/accompanist/coil/RequestActor.kt b/coil/src/main/java/dev/chrisbanes/accompanist/coil/RequestActor.kt new file mode 100644 index 000000000..5f5085418 --- /dev/null +++ b/coil/src/main/java/dev/chrisbanes/accompanist/coil/RequestActor.kt @@ -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( + private val execute: suspend (Param) -> Result, + private val onResult: (Result) -> Unit +) { + private val channel = Channel(Channel.CONFLATED) + + suspend fun run() { + channel.consumeAsFlow() + .distinctUntilChanged() + .collect { param -> onResult(execute(param)) } + } + + fun send(param: Param) { + channel.offer(param) + } + + fun close() { + channel.close() + } +}