Skip to content

Commit c5adec2

Browse files
committed
fix: adderss race condition on doanlowd finished -> refresh
1 parent 3bde6bd commit c5adec2

File tree

4 files changed

+97
-14
lines changed

4 files changed

+97
-14
lines changed

android/src/main/java/com/margelo/nitro/rive/HybridRiveFile.kt

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,22 @@ import androidx.annotation.Keep
44
import app.rive.runtime.kotlin.core.File
55
import com.facebook.proguard.annotations.DoNotStrip
66
import com.margelo.nitro.NitroModules
7+
import java.lang.ref.WeakReference
8+
import kotlinx.coroutines.CoroutineScope
9+
import kotlinx.coroutines.Dispatchers
10+
import kotlinx.coroutines.SupervisorJob
11+
import kotlinx.coroutines.awaitAll
12+
import kotlinx.coroutines.cancel
13+
import kotlinx.coroutines.launch
714

815
@Keep
916
@DoNotStrip
1017
class HybridRiveFile : HybridRiveFileSpec() {
1118
var riveFile: File? = null
1219
var referencedAssetCache: ReferencedAssetCache? = null
1320
var assetLoader: ReferencedAssetLoader? = null
21+
private val weakViews = mutableListOf<WeakReference<HybridRiveView>>()
22+
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
1423

1524
override val viewModelCount: Double?
1625
get() = riveFile?.viewModelCount?.toDouble()
@@ -40,19 +49,45 @@ class HybridRiveFile : HybridRiveFileSpec() {
4049
}
4150
}
4251

52+
fun registerView(view: HybridRiveView) {
53+
weakViews.add(WeakReference(view))
54+
}
55+
56+
fun unregisterView(view: HybridRiveView) {
57+
weakViews.removeAll { it.get() == view }
58+
}
59+
60+
private fun refreshAfterAssetChange() {
61+
weakViews.removeAll { it.get() == null }
62+
63+
for (weakView in weakViews) {
64+
weakView.get()?.refreshAfterAssetChange()
65+
}
66+
}
67+
4368
override fun updateReferencedAssets(referencedAssets: ReferencedAssetsType) {
4469
val assetsData = referencedAssets.data ?: return
4570
val cache = referencedAssetCache ?: return
4671
val loader = assetLoader ?: return
4772
val context = NitroModules.applicationContext ?: return
4873

74+
val loadJobs = mutableListOf<kotlinx.coroutines.Deferred<Unit>>()
75+
4976
for ((key, assetData) in assetsData) {
5077
val asset = cache[key] ?: continue
51-
loader.updateAsset(assetData, asset, context)
78+
loadJobs.add(loader.updateAsset(assetData, asset, context))
79+
}
80+
81+
if (loadJobs.isNotEmpty()) {
82+
scope.launch {
83+
loadJobs.awaitAll()
84+
refreshAfterAssetChange()
85+
}
5286
}
5387
}
5488

5589
override fun release() {
90+
scope.cancel()
5691
assetLoader?.dispose()
5792
assetLoader = null
5893
riveFile?.release()

android/src/main/java/com/margelo/nitro/rive/HybridRiveView.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() {
2525
//region State
2626
override val view: RiveReactNativeView = RiveReactNativeView(context)
2727
private var needsReload = false
28+
private var registeredFile: HybridRiveFile? = null
2829
//endregion
2930

3031
//region View Props
@@ -46,6 +47,10 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() {
4647
}
4748
override var file: HybridRiveFileSpec = HybridRiveFile()
4849
set(value) {
50+
if (field != value) {
51+
registeredFile?.unregisterView(this)
52+
registeredFile = null
53+
}
4954
changed(field, value) { field = it }
5055
}
5156
override var alignment: Alignment? = null
@@ -99,8 +104,13 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() {
99104
//endregion
100105

101106
//region Update
107+
fun refreshAfterAssetChange() {
108+
afterUpdate()
109+
}
110+
102111
override fun afterUpdate() {
103-
val riveFile = (file as? HybridRiveFile)?.riveFile ?: return
112+
val hybridFile = file as? HybridRiveFile
113+
val riveFile = hybridFile?.riveFile ?: return
104114

105115
val config = ViewConfiguration(
106116
artboardName = artboardName,
@@ -113,6 +123,12 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() {
113123
layoutScaleFactor = layoutScaleFactor?.toFloat() ?: DefaultConfiguration.LAYOUTSCALEFACTOR,
114124
)
115125
view.configure(config, needsReload)
126+
127+
if (needsReload && hybridFile != null) {
128+
hybridFile.registerView(this)
129+
registeredFile = hybridFile
130+
}
131+
116132
needsReload = false
117133
super.afterUpdate()
118134
}

android/src/main/java/com/margelo/nitro/rive/ReferencedAssetLoader.kt

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import android.util.Log
88
import app.rive.runtime.kotlin.core.*
99
import com.margelo.nitro.NitroModules
1010
import kotlinx.coroutines.*
11+
import kotlinx.coroutines.CompletableDeferred
12+
import kotlinx.coroutines.Deferred
1113
import java.io.File as JavaFile
1214
import java.io.IOException
1315
import java.net.URI
@@ -61,9 +63,10 @@ class ReferencedAssetLoader {
6163
}
6264
}
6365

64-
private fun downloadUrlAsset(url: String, listener: (ByteArray) -> Unit) {
66+
private fun downloadUrlAsset(url: String, listener: (ByteArray?) -> Unit) {
6567
if (!isValidUrl(url)) {
6668
logError("Invalid URL: $url")
69+
listener(null)
6770
return
6871
}
6972

@@ -86,6 +89,9 @@ class ReferencedAssetLoader {
8689
}
8790
else -> {
8891
logError("Unsupported URL scheme: ${uri.scheme}")
92+
withContext(Dispatchers.Main) {
93+
listener(null)
94+
}
8995
return@launch
9096
}
9197
}
@@ -95,14 +101,17 @@ class ReferencedAssetLoader {
95101
}
96102
} catch (e: Exception) {
97103
logError("Unable to download asset from URL: $url - ${e.message}")
104+
withContext(Dispatchers.Main) {
105+
listener(null)
106+
}
98107
}
99108
}
100109
}
101110

102111
private fun loadResourceAsset(
103112
sourceAssetId: String,
104113
context: Context,
105-
listener: (ByteArray) -> Unit
114+
listener: (ByteArray?) -> Unit
106115
) {
107116
scope.launch {
108117
try {
@@ -124,13 +133,25 @@ class ReferencedAssetLoader {
124133
}
125134
} else {
126135
logError("Resource not found: $sourceAssetId")
136+
withContext(Dispatchers.Main) {
137+
listener(null)
138+
}
127139
}
128140
} catch (e: IOException) {
129141
logError("IO Exception while reading resource: $sourceAssetId - ${e.message}")
142+
withContext(Dispatchers.Main) {
143+
listener(null)
144+
}
130145
} catch (e: Resources.NotFoundException) {
131146
logError("Resource not found: $sourceAssetId - ${e.message}")
147+
withContext(Dispatchers.Main) {
148+
listener(null)
149+
}
132150
} catch (e: Exception) {
133151
logError("Unexpected error while processing resource: $sourceAssetId - ${e.message}")
152+
withContext(Dispatchers.Main) {
153+
listener(null)
154+
}
134155
}
135156
}
136157
}
@@ -139,20 +160,21 @@ class ReferencedAssetLoader {
139160
sourceAsset: String,
140161
path: String?,
141162
context: Context,
142-
listener: (ByteArray) -> Unit
163+
listener: (ByteArray?) -> Unit
143164
) {
144165
scope.launch {
145166
try {
146167
val fullPath = if (path == null) sourceAsset else constructFilePath(sourceAsset, path)
147168
val bytes = readAssetBytes(context, fullPath)
148169

149-
if (bytes != null) {
150-
withContext(Dispatchers.Main) {
151-
listener(bytes)
152-
}
170+
withContext(Dispatchers.Main) {
171+
listener(bytes)
153172
}
154173
} catch (e: Exception) {
155174
logError("Error loading bundled asset: $sourceAsset - ${e.message}")
175+
withContext(Dispatchers.Main) {
176+
listener(null)
177+
}
156178
}
157179
}
158180
}
@@ -165,9 +187,13 @@ class ReferencedAssetLoader {
165187
}
166188
}
167189

168-
private fun loadAsset(assetData: ResolvedReferencedAsset, asset: FileAsset, context: Context) {
169-
val listener: (ByteArray) -> Unit = { bytes ->
170-
processAssetBytes(bytes, asset)
190+
private fun loadAsset(assetData: ResolvedReferencedAsset, asset: FileAsset, context: Context): Deferred<Unit> {
191+
val deferred = CompletableDeferred<Unit>()
192+
val listener: (ByteArray?) -> Unit = { bytes ->
193+
if (bytes != null) {
194+
processAssetBytes(bytes, asset)
195+
}
196+
deferred.complete(Unit)
171197
}
172198

173199
when {
@@ -180,11 +206,16 @@ class ReferencedAssetLoader {
180206
assetData.sourceAsset != null -> {
181207
loadBundledAsset(assetData.sourceAsset, assetData.path, context, listener)
182208
}
209+
else -> {
210+
deferred.complete(Unit)
211+
}
183212
}
213+
214+
return deferred
184215
}
185216

186-
fun updateAsset(assetData: ResolvedReferencedAsset, asset: FileAsset, context: Context) {
187-
loadAsset(assetData, asset, context)
217+
fun updateAsset(assetData: ResolvedReferencedAsset, asset: FileAsset, context: Context): Deferred<Unit> {
218+
return loadAsset(assetData, asset, context)
188219
}
189220

190221
fun createCustomLoader(

rn82-example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 032eeda50ad5a29c0c723babb906d9e0339487a8

0 commit comments

Comments
 (0)