Skip to content

Commit 67a2708

Browse files
committed
feature: Use cache for images from network
1 parent 9813b2c commit 67a2708

File tree

3 files changed

+113
-13
lines changed

3 files changed

+113
-13
lines changed

android/src/main/java/com/reactnativecommunity/imageeditor/ImageEditorModuleImpl.kt

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,18 @@ import android.provider.MediaStore
2121
import android.text.TextUtils
2222
import android.util.Base64
2323
import androidx.exifinterface.media.ExifInterface
24+
import com.facebook.common.executors.CallerThreadExecutor
2425
import com.facebook.common.logging.FLog
26+
import com.facebook.common.memory.PooledByteBuffer
27+
import com.facebook.common.memory.PooledByteBufferInputStream
28+
import com.facebook.common.references.CloseableReference
29+
import com.facebook.datasource.BaseDataSubscriber
30+
import com.facebook.datasource.DataSource
31+
import com.facebook.datasource.DataSubscriber
32+
import com.facebook.drawee.backends.pipeline.Fresco
33+
import com.facebook.imagepipeline.core.ImagePipeline
34+
import com.facebook.imagepipeline.request.ImageRequest
35+
import com.facebook.imagepipeline.request.ImageRequestBuilder
2536
import com.facebook.infer.annotation.Assertions
2637
import com.facebook.react.bridge.Arguments
2738
import com.facebook.react.bridge.JSApplicationIllegalArgumentException
@@ -31,6 +42,8 @@ import com.facebook.react.bridge.ReadableMap
3142
import com.facebook.react.bridge.ReadableType
3243
import com.facebook.react.bridge.WritableMap
3344
import com.facebook.react.common.ReactConstants
45+
import com.facebook.react.modules.fresco.ReactNetworkImageRequest
46+
import com.facebook.react.views.image.ReactCallerContextFactory
3447
import java.io.ByteArrayInputStream
3548
import java.io.File
3649
import java.io.FileInputStream
@@ -51,7 +64,19 @@ object MimeType {
5164
const val WEBP = "image/webp"
5265
}
5366

54-
class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
67+
interface ImageLoadCallback {
68+
fun onImageLoaded(inputStream: InputStream?)
69+
70+
fun onFailure(error: Throwable)
71+
}
72+
73+
class ImageEditorModuleImpl(
74+
private val reactContext: ReactApplicationContext,
75+
private val callerContext: Any?,
76+
private val callerContextFactory: ReactCallerContextFactory?,
77+
private val imagePipeline: ImagePipeline?
78+
) {
79+
5580
private val moduleCoroutineScope = CoroutineScope(Dispatchers.Default)
5681

5782
init {
@@ -65,6 +90,58 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
6590
cleanTask()
6691
}
6792

93+
private fun getCallerContext(): Any? {
94+
return callerContextFactory?.getOrCreateCallerContext("", "") ?: callerContext
95+
}
96+
97+
private fun getImagePipeline(): ImagePipeline {
98+
return imagePipeline ?: Fresco.getImagePipeline()
99+
}
100+
101+
private fun fetchAndCacheImage(
102+
uri: String,
103+
headers: ReadableMap?,
104+
callback: ImageLoadCallback
105+
) {
106+
val uri = Uri.parse(uri)
107+
val imageRequest: ImageRequest =
108+
if (headers != null) {
109+
val imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(uri)
110+
ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, headers)
111+
} else ImageRequestBuilder.newBuilderWithSource(uri).build()
112+
113+
val dataSource: DataSource<CloseableReference<PooledByteBuffer>> =
114+
getImagePipeline().fetchEncodedImage(imageRequest, getCallerContext())
115+
val dataSubscriber: DataSubscriber<CloseableReference<PooledByteBuffer>> =
116+
object : BaseDataSubscriber<CloseableReference<PooledByteBuffer>>() {
117+
override fun onNewResultImpl(
118+
dataSource: DataSource<CloseableReference<PooledByteBuffer>>
119+
) {
120+
if (!dataSource.isFinished()) {
121+
return
122+
}
123+
// getImagePipeline().isInBitmapMemoryCache(uri) // true
124+
// getImagePipeline().isInDiskCacheSync(uri) // true
125+
val ref = dataSource.getResult()
126+
val result = ref?.get()
127+
if (result != null) {
128+
val stream: InputStream = PooledByteBufferInputStream(result)
129+
callback.onImageLoaded(stream)
130+
} else {
131+
callback.onImageLoaded(null)
132+
}
133+
}
134+
135+
override fun onFailureImpl(
136+
dataSource: DataSource<CloseableReference<PooledByteBuffer>>
137+
) {
138+
dataSource.close()
139+
callback.onFailure(dataSource.failureCause!!)
140+
}
141+
}
142+
dataSource.subscribe(dataSubscriber, CallerThreadExecutor.getInstance())
143+
}
144+
68145
/**
69146
* Asynchronous task that cleans up cache dirs (internal and, if available, external) of cropped
70147
* image files. This is run when the module is invalidated (i.e. app is shutting down) and when
@@ -148,7 +225,7 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
148225
// memory
149226
val hasTargetSize = targetWidth > 0 && targetHeight > 0
150227
val cropped: Bitmap? =
151-
if (hasTargetSize) {
228+
if (hasTargetSize)
152229
cropAndResizeTask(
153230
outOptions,
154231
uri,
@@ -160,9 +237,8 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
160237
targetHeight,
161238
headers
162239
)
163-
} else {
164-
cropTask(outOptions, uri, x, y, width, height, headers)
165-
}
240+
else cropTask(outOptions, uri, x, y, width, height, headers)
241+
166242
if (cropped == null) {
167243
throw IOException("Cannot decode bitmap: $uri")
168244
}

android/src/newarch/com/reactnativecommunity/imageeditor/ImageEditorModule.kt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,23 @@ import com.facebook.react.bridge.ReadableMap
66
import com.facebook.react.module.annotations.ReactModule
77

88
@ReactModule(name = ImageEditorModule.NAME)
9-
class ImageEditorModule(reactContext: ReactApplicationContext) :
10-
NativeRNCImageEditorSpec(reactContext) {
9+
class ImageEditorModule : NativeRNCImageEditorSpec {
1110
private val moduleImpl: ImageEditorModuleImpl
1211

13-
init {
14-
moduleImpl = ImageEditorModuleImpl(reactContext)
12+
constructor(reactContext: ReactApplicationContext) : super(reactContext) {
13+
moduleImpl = ImageEditorModuleImpl(reactContext, this, null, null)
14+
}
15+
16+
constructor(reactContext: ReactApplicationContext, callerContext: Any?) : super(reactContext) {
17+
moduleImpl = ImageEditorModuleImpl(reactContext, callerContext, null, null)
18+
}
19+
20+
constructor(
21+
reactContext: ReactApplicationContext,
22+
imagePipeline: ImagePipeline?,
23+
callerContextFactory: ReactCallerContextFactory?
24+
) : super(reactContext) {
25+
moduleImpl = ImageEditorModuleImpl(reactContext, null, callerContextFactory, imagePipeline)
1526
}
1627

1728
override fun getName(): String {

android/src/oldarch/com/reactnativecommunity/imageeditor/ImageEditorModule.kt

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
package com.reactnativecommunity.imageeditor
22

3+
import com.facebook.imagepipeline.core.ImagePipeline
34
import com.facebook.react.bridge.Promise
45
import com.facebook.react.bridge.ReactApplicationContext
56
import com.facebook.react.bridge.ReactContextBaseJavaModule
67
import com.facebook.react.bridge.ReactMethod
78
import com.facebook.react.bridge.ReadableMap
89
import com.facebook.react.module.annotations.ReactModule
10+
import com.facebook.react.views.image.ReactCallerContextFactory
911

1012
@ReactModule(name = ImageEditorModule.NAME)
11-
class ImageEditorModule(reactContext: ReactApplicationContext) :
12-
ReactContextBaseJavaModule(reactContext) {
13+
class ImageEditorModule : ReactContextBaseJavaModule {
1314
private val moduleImpl: ImageEditorModuleImpl
1415

15-
init {
16-
moduleImpl = ImageEditorModuleImpl(reactContext)
16+
constructor(reactContext: ReactApplicationContext) : super(reactContext) {
17+
moduleImpl = ImageEditorModuleImpl(reactContext, this, null, null)
18+
}
19+
20+
constructor(reactContext: ReactApplicationContext, callerContext: Any?) : super(reactContext) {
21+
moduleImpl = ImageEditorModuleImpl(reactContext, callerContext, null, null)
22+
}
23+
24+
constructor(
25+
reactContext: ReactApplicationContext,
26+
imagePipeline: ImagePipeline?,
27+
callerContextFactory: ReactCallerContextFactory?
28+
) : super(reactContext) {
29+
moduleImpl = ImageEditorModuleImpl(reactContext, null, callerContextFactory, imagePipeline)
1730
}
1831

1932
override fun getName(): String {

0 commit comments

Comments
 (0)