Skip to content

fix(android): sometimes view tag is not resolved immediately#3074

Merged
mfazekas merged 1 commit intomainfrom
mfazekas/fix-view-and-resolve
Sep 26, 2023
Merged

fix(android): sometimes view tag is not resolved immediately#3074
mfazekas merged 1 commit intomainfrom
mfazekas/fix-view-and-resolve

Conversation

@mfazekas
Copy link
Contributor

@mfazekas mfazekas commented Sep 25, 2023

Fixes the folllowing warning on android:

Can be reproduce with this example:

import React from 'react';
import { FlatList } from "react-native";
import { MapView, Camera, ShapeSource, LineLayer } from '@rnmapbox/maps';

const SHAPE = {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature",
        "geometry": {
          "type": "Polygon",
          "coordinates": [
            [
              [
                -105.02738295071447,
                -74.41429582091831
              ],
              [
                -108.67784207799926,
                -82.41571395310365
              ],
              [
                -117.5641615804278,
                -71.45151781686236
              ],
              [
                -105.02738295071447,
                -74.41429582091831
              ]
            ]
          ]
        },
      }
    ],
    "properties": {
        "bbox": [-117.5641615804278, -82.41571395310365, -105.02738295071447, -71.45151781686236],
    }
};

const SHAPES = Array.from({ length: 10 }).map(() => SHAPE);

const Example = () => <FlatList data={SHAPES} renderItem={renderItem} />;

const renderItem = ({ item }) => {
    const [minX, minY, maxX, maxY] = item.properties.bbox;

    return (
        <MapView zoomEnabled={false} scrollEnabled={false} scaleBarEnabled={false} style={{ height: 200 }}>
            <Camera
                bounds={{ ne: [maxX, maxY], sw: [minX, minY] }}
                animationDuration={0}
            />
            <ShapeSource id="shape" shape={item}>
                <LineLayer id="line" />
            </ShapeSource>
        </MapView>
    );
};

export default Example;
image

See also: https://github.com/software-mansion/react-native-reanimated/pull/2982/files

@mfazekas
Copy link
Contributor Author

@WoLewicki @j-piasecki Sometimes this code seems to raise an exception

val view = manager?.resolveView(viewRef.toInt()) as? RNMBXMapView
if (view != null) {
fn(view)
} else {
promise.reject(Exception("cannot find map view for tag ${viewRef.toInt()}"))
}

com.facebook.react.uimanager.IllegalViewOperationException: Trying to resolve view with tag 569 which doesn't exist.

I assume this is some kind of race condition. I assume it's possible that the tag => View association has yet not been made on the native side, when we're already called a method on the view module. My ugly workaround so doing another loop in the ui queue, if the first one we've not found the tag. It seems to work, but do you have a better suggestion?

} catch (err: Exception) {
reactApplicationContext.runOnUiQueueThread {
val view = manager?.resolveView(viewRef.toInt()) as? RNMBXMapView
if (view != null) {
fn(view)
} else {
promise.reject(Exception("cannot find map view for tag ${viewRef.toInt()}"))
}
}
}

 Exception in native call
                                                                                                    com.facebook.react.uimanager.IllegalViewOperationException: Trying to resolve view with tag 569 which doesn't exist
                                                                                                    	at com.facebook.react.uimanager.NativeViewHierarchyManager.resolveView(NativeViewHierarchyManager.java:102)
                                                                                                    	at com.facebook.react.uimanager.UIManagerModule.resolveView(UIManagerModule.java:870)
                                                                                                    	at com.rnmapbox.rnmbx.components.mapview.NativeMapViewModule.withMapViewOnUIThread$lambda$0(NativeMapViewModule.kt:30)
                                                                                                    	at com.rnmapbox.rnmbx.components.mapview.NativeMapViewModule.$r8$lambda$HbfWzkRxa1r9gGwoHqVmAHlh0JU(Unknown Source:0)
                                                                                                    	at com.rnmapbox.rnmbx.components.mapview.NativeMapViewModule$$ExternalSyntheticLambda0.run(Unknown Source:8)
                                                                                                    	at android.os.Handler.handleCallback(Handler.java:958)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                    	at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:205)
                                                                                                    	at android.os.Looper.loop(Looper.java:294)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:8176)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
                                                                                                    	

@WoLewicki
Copy link
Contributor

I thought that maybe we should do the same thing as on iOS:

return RCTGetUIManagerQueue();
so the methods are dispatched on the uiManager queue, but I don't see anything like this on Android unfortunately. I think your solution is closest to what we can do about it. We do something quite similar in react-native-svg where there are more attempts to make it work: https://github.com/software-mansion/react-native-svg/blob/f9c7d8a807c8dc5b21e8ac0358a9bf7a820a1f61/android/src/main/java/com/horcrux/svg/SvgViewModule.java#L66. There are more options there, you can check it in the code of the above file.

@mfazekas mfazekas force-pushed the mfazekas/fix-view-and-resolve branch from d0eb1c4 to 4fffe97 Compare September 26, 2023 09:58
@mfazekas
Copy link
Contributor Author

I thought that maybe we should do the same thing as on iOS:

return RCTGetUIManagerQueue();

so the methods are dispatched on the uiManager queue, but I don't see anything like this on Android unfortunately. I think your solution is closest to what we can do about it. We do something quite similar in react-native-svg where there are more attempts to make it work: https://github.com/software-mansion/react-native-svg/blob/f9c7d8a807c8dc5b21e8ac0358a9bf7a820a1f61/android/src/main/java/com/horcrux/svg/SvgViewModule.java#L66. There are more options there, you can check it in the code of the above file.

Thanks, much! I've ended up using the same technique, queue functions depending on the view until setId is called where we know we have the view created on native side as well.

https://github.com/software-mansion/react-native-svg/blob/f9c7d8a807c8dc5b21e8ac0358a9bf7a820a1f61/android/src/main/java/com/horcrux/svg/SvgView.java#L70-L74

@mfazekas mfazekas merged commit c60584d into main Sep 26, 2023
@mfazekas mfazekas deleted the mfazekas/fix-view-and-resolve branch September 26, 2023 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants