react-native-fast-image: [Android] Crash, Error while updating property 'source' of a view managed by: FastImageView

Describe the bug There is an app crash reported by Crashlytics on a device running Android 4.4.4 (Samsung, Galaxy Core Prime)

FastImageViewManager.java line 111
com.dylanvann.fastimage.FastImageViewManager.setSrc
Caused by java.lang.ClassCastException
androidx.appcompat.widget.TintContextWrapper cannot be cast to e.g.x0.t0.d0

To Reproduce I cannot reproduce this, but hopefully the log provided by Crashlytics would shred some light on this.

Expected behavior Should not crash.

Screenshots

Dependency versions

  • React Native version: 0.62.3
  • React version: 16.11.0
  • React Native Fast Image version: 8.5.11

Note: if these are not the latest versions of each I recommend updating as extra effort will not be taken to be backwards compatible, and updating might resolving your issue.

About this issue

Most upvoted comments

@fdobre : Hey , I have raised the PR for this issue by forking the repo 29 days back. https://github.com/DylanVann/react-native-fast-image/pull/851/commits/1cfae0c695e1a3e974ea24986083447877b8f41b It’s still pending to be merged.

I am facing the same issue: I have fixed it by applying a null check in the FastImageSource.java file. This issue is happening because Glide lib doesn’t allow null or empty string to be passed to GlideUrl object, So it’s crashing.

Please anyone suggest if this approach is fine and a PR can be raised.

Solution: @Override public Uri getUri() { if(mUri != null && !mUri.equals(“”)) return mUri; return null; }

public Headers getHeaders() {
  if(mHeaders != null)
    return mHeaders;
  return null;
}

public GlideUrl getGlideUrl() {
  if(getUri().toString() != null && !getUri().toString().equals("")) {
    return new GlideUrl(getUri().toString(), getHeaders());
  }else {
    return new GlideUrl(Glide,getHeaders());
  }
}

@ilenin, thanks for pinging me, I have done some more research and found this thread. So I have updated my code in the PR!

Yes, please see my “patches/react-native-fast-image+8.5.11.patch” below, @dcboy.

diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java
index 361417b..85b8eeb 100644
--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java
+++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java
@@ -1,5 +1,6 @@
 package com.dylanvann.fastimage;
 
+import android.content.Context;
 import android.graphics.drawable.Drawable;
 
 import com.bumptech.glide.load.DataSource;
@@ -7,9 +8,9 @@ import com.bumptech.glide.load.engine.GlideException;
 import com.bumptech.glide.request.RequestListener;
 import com.bumptech.glide.request.target.ImageViewTarget;
 import com.bumptech.glide.request.target.Target;
+import com.facebook.react.bridge.ReactContext;
 import com.facebook.react.bridge.WritableMap;
 import com.facebook.react.bridge.WritableNativeMap;
-import com.facebook.react.uimanager.ThemedReactContext;
 import com.facebook.react.uimanager.events.RCTEventEmitter;
 
 public class FastImageRequestListener implements RequestListener<Drawable> {
@@ -37,11 +38,14 @@ public class FastImageRequestListener implements RequestListener<Drawable> {
             return false;
         }
         FastImageViewWithUrl view = (FastImageViewWithUrl) ((ImageViewTarget) target).getView();
-        ThemedReactContext context = (ThemedReactContext) view.getContext();
-        RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
         int viewId = view.getId();
-        eventEmitter.receiveEvent(viewId, REACT_ON_ERROR_EVENT, new WritableNativeMap());
-        eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
+        Context context = view.getContext();
+        ReactContext reactContext = context instanceof ReactContext ? (ReactContext) context : null;
+        if (reactContext != null) {
+            RCTEventEmitter eventEmitter = reactContext.getJSModule(RCTEventEmitter.class);
+            eventEmitter.receiveEvent(viewId, REACT_ON_ERROR_EVENT, new WritableNativeMap());
+            eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
+        }
         return false;
     }
 
@@ -51,11 +55,14 @@ public class FastImageRequestListener implements RequestListener<Drawable> {
             return false;
         }
         FastImageViewWithUrl view = (FastImageViewWithUrl) ((ImageViewTarget) target).getView();
-        ThemedReactContext context = (ThemedReactContext) view.getContext();
-        RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
         int viewId = view.getId();
-        eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, mapFromResource(resource));
-        eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
+        Context context = view.getContext();
+        ReactContext reactContext = context instanceof ReactContext ? (ReactContext) context : null;
+        if (reactContext != null) {
+            RCTEventEmitter eventEmitter = reactContext.getJSModule(RCTEventEmitter.class);
+            eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, mapFromResource(resource));
+            eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
+        }
         return false;
     }
 }
diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java
index 0944463..81a425d 100644
--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java
+++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java
@@ -10,6 +10,7 @@ import com.bumptech.glide.Glide;
 import com.bumptech.glide.RequestManager;
 import com.bumptech.glide.load.model.GlideUrl;
 import com.bumptech.glide.request.Request;
+import com.facebook.react.bridge.ReactContext;
 import com.facebook.react.bridge.ReadableMap;
 import com.facebook.react.bridge.WritableMap;
 import com.facebook.react.bridge.WritableNativeMap;
@@ -57,6 +58,10 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
 
     @ReactProp(name = "source")
     public void setSrc(FastImageViewWithUrl view, @Nullable ReadableMap source) {
+        int viewId = view.getId();
+        Context context = view.getContext();
+        ReactContext reactContext = context instanceof ReactContext ? (ReactContext) context : null;
+
         if (source == null || !source.hasKey("uri") || isNullOrEmpty(source.getString("uri"))) {
             // Cancel existing requests.
             clearView(view);
@@ -69,15 +74,15 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
             return;
         }
 
-        //final GlideUrl glideUrl = FastImageViewConverter.getGlideUrl(view.getContext(), source);
-        final FastImageSource imageSource = FastImageViewConverter.getImageSource(view.getContext(), source);
+        //final GlideUrl glideUrl = FastImageViewConverter.getGlideUrl(context, source);
+        final FastImageSource imageSource = FastImageViewConverter.getImageSource(context, source);
         if (imageSource.getUri().toString().length() == 0) {
-            ThemedReactContext context = (ThemedReactContext) view.getContext();
-            RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
-            int viewId = view.getId();
-            WritableMap event = new WritableNativeMap();
-            event.putString("message", "Invalid source prop:" + source);
-            eventEmitter.receiveEvent(viewId, REACT_ON_ERROR_EVENT, event);
+            if (reactContext != null) {
+                WritableMap event = new WritableNativeMap();
+                event.putString("message", "Invalid source prop:" + source);
+                RCTEventEmitter eventEmitter = reactContext.getJSModule(RCTEventEmitter.class);
+                eventEmitter.receiveEvent(viewId, REACT_ON_ERROR_EVENT, event);
+            }
 
             // Cancel existing requests.
             if (requestManager != null) {
@@ -108,10 +113,10 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
             VIEWS_FOR_URLS.put(key, newViewsForKeys);
         }
 
-        ThemedReactContext context = (ThemedReactContext) view.getContext();
-        RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
-        int viewId = view.getId();
-        eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_START_EVENT, new WritableNativeMap());
+        if (reactContext != null) {
+            RCTEventEmitter eventEmitter = reactContext.getJSModule(RCTEventEmitter.class);
+            eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_START_EVENT, new WritableNativeMap());
+        }
 
         if (requestManager != null) {
             requestManager
@@ -176,14 +181,15 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
     public void onProgress(String key, long bytesRead, long expectedLength) {
         List<FastImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
         if (viewsForKey != null) {
-            for (FastImageViewWithUrl view : viewsForKey) {
+            Context context = null;
+            for (FastImageViewWithUrl view: viewsForKey) {
+                context = view.getContext();
+                if (!(context instanceof ReactContext)) continue; // this view is not of react context
                 WritableMap event = new WritableNativeMap();
                 event.putInt("loaded", (int) bytesRead);
                 event.putInt("total", (int) expectedLength);
-                ThemedReactContext context = (ThemedReactContext) view.getContext();
-                RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
-                int viewId = view.getId();
-                eventEmitter.receiveEvent(viewId, REACT_ON_PROGRESS_EVENT, event);
+                RCTEventEmitter eventEmitter = ((ReactContext) context).getJSModule(RCTEventEmitter.class);
+                eventEmitter.receiveEvent(view.getId(), REACT_ON_PROGRESS_EVENT, event);
             }
         }
     }