react-native-ui-lib icon indicating copy to clipboard operation
react-native-ui-lib copied to clipboard

Attempt to invoke virtual method 'void com.facebook.react.uimanager.UlManagerModule.onBatchComplete()' on a null object reference

Open wasimkham opened this issue 9 months ago • 14 comments

Description

Android crashes due to NullPointerException in RuntimeUtils.java when using the keyboard input features. The issue stems from the React Native UI Lib package's dependency structure for Android, where patches need to be applied to both react-native-ui-lib AND uilib-native packages.

Related to

  • [x] Components
  • [ ] Demo
  • [ ] Docs
  • [ ] Typings

Steps to reproduce

  1. Use react-native-ui-lib in a React Native application
  2. Implement components that use keyboard-related features
  3. Run on Android
  4. App crashes with NullPointerException in RuntimeUtils.java during keyboard interaction
  5. Apply patch to react-native-ui-lib but crashes continue

Expected behavior

The app should handle keyboard interactions properly without crashing.

Actual behavior

The app crashes with errors like:

Fatal Exception: java.lang.NullPointerException
Attempt to invoke virtual method 'void com.facebook.react.uimanager.UIManagerModule.onBatchComplete()' on a null object reference
       at com.wix.reactnativeuilib.keyboardinput.utils.RuntimeUtils$1.run(RuntimeUtils.java:13)

More Info

Root Cause Analysis

The root cause of this issue is a non-obvious relationship between react-native-ui-lib and uilib-native packages.

When building the Android app, the react-native-ui-lib package doesn't use its own native Android code. Instead, it points to the uilib-native package's Android code, as configured in react-native-ui-lib/react-native.config.js:

// node_modules/react-native-ui-lib/react-native.config.js
module.exports = {
  dependency: {
    platforms: {
      android: {
        sourceDir: "../uilib-native/android/",
        // Other config...
      },
    },
  },
};

This means that patching only react-native-ui-lib doesn't affect the Android build, since it's actually using code from uilib-native.

Code snippet

Original problematic code in uilib-native:

// node_modules/uilib-native/android/src/main/java/com/wix/reactnativeuilib/keyboardinput/utils/RuntimeUtils.java
package com.wix.reactnativeuilib.keyboardinput.utils;

import com.facebook.react.uimanager.UIManagerModule;
import com.wix.reactnativeuilib.keyboardinput.ReactContextHolder;

public class RuntimeUtils {
    private static final Runnable sUIUpdateClosure = new Runnable() {
        @Override
        public void run() {
            ReactContextHolder.getContext().getNativeModule(UIManagerModule.class).onBatchComplete();
        }
    };

    public static void runOnUIThread(Runnable runnable) {
        if (ReactContextHolder.getContext() != null) {
            ReactContextHolder.getContext().runOnUiQueueThread(runnable);
        }
    }

    public static void dispatchUIUpdates(final Runnable userRunnable) {
        runOnUIThread(new Runnable() {
            @Override
            public void run() {
                userRunnable.run();
                if (ReactContextHolder.getContext() != null) {
                    ReactContextHolder.getContext().runOnNativeModulesQueueThread(sUIUpdateClosure);
                }
            }
        });
    }
}

Fixed version with proper null checks:

package com.wix.reactnativeuilib.keyboardinput.utils;

import android.util.Log;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.wix.reactnativeuilib.keyboardinput.ReactContextHolder;

public class RuntimeUtils {
    private static final Runnable sUIUpdateClosure = new Runnable() {
        @Override
        public void run() {
            try {
                ReactContext context = ReactContextHolder.getContext();
                if (context != null) {
                    UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
                    if (uiManager != null) {
                        uiManager.onBatchComplete();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    public static void runOnUIThread(Runnable runnable) {
        try {
            if (ReactContextHolder.getContext() != null) {
                ReactContextHolder.getContext().runOnUiQueueThread(runnable);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void dispatchUIUpdates(final Runnable userRunnable) {
        if (ReactContextHolder.getContext() == null) {
            return; // Skip if context is null
        }

        try {
            runOnUIThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // Re-check context before running user code
                        if (ReactContextHolder.getContext() != null) {
                            userRunnable.run();

                            // Get a fresh context reference before queue operation
                            ReactContext context = ReactContextHolder.getContext();
                            if (context != null) {
                                context.runOnNativeModulesQueueThread(sUIUpdateClosure);
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Solution

To fix this issue, you need to patch both packages using patch-package:

  1. First, patch react-native-ui-lib
  2. Then, patch uilib-native
  3. Apply both patches using patch-package in your postinstall script

Example patches are provided in the code snippets above.

Verification

To verify that patches are correctly applied:

  1. Check that both package source files contain your patches
  2. Add Log statements in the patched code (like Log.d("TAG", "message"))
  3. Monitor the Android logs during app execution to confirm the patched code is running

Screenshots/Video

N/A

Environment

  • React Native version: 0.76.x
  • React Native UI Lib version: 7.38.1
  • uilib-native version: 4.5.1 (indirectly used by react-native-ui-lib)

Affected platforms

  • [x] Android
  • [ ] iOS
  • [ ] Web

Reference: https://github.com/facebook/react-native/issues/47662

wasimkham avatar Mar 10 '25 22:03 wasimkham

Thanks @wasimkham for the putting all together on the issue. I am experiencing the same for past couple of days. Tried to patch react-native-ui-lib , but didn't work. I'll try patching uilib-native as well. What I notice is the app crashes after the keyboard appears. Even tried upgrading react-native-screens, then using KeyboardAvoidingView and KeyboardAwareScrollView with no luck. Only fix works is if we change in the AndroidMainfest.xml file android:windowSoftInputMode="adjustResize" to android:windowSoftInputMode="adjustNothing". But consequences would be hard to handle. adjustPan doesn't work either. I hope the team will provide a help soon or if you can create a PR please.

KishoreBarik avatar Mar 12 '25 06:03 KishoreBarik

https://github.com/wix/react-native-ui-lib/issues/3397#issuecomment-2607309623

khanhdevvn avatar Mar 13 '25 09:03 khanhdevvn

I'm also having this issue on older Android API 27. It works on newer API 36

iuuukhueeee avatar May 15 '25 03:05 iuuukhueeee

I tried patching all the relevant files, but still encountered a Gradle 8+ error due to a circular dependency between the libraries.

modifyljf avatar May 21 '25 14:05 modifyljf

You saved my life thx a lot :)

hjick avatar May 23 '25 06:05 hjick

hope the team will provide a help soon

ee-dd-cc avatar Jun 22 '25 12:06 ee-dd-cc

Any updates on this issue as i am still getting the same error

aalindQuantel avatar Sep 04 '25 10:09 aalindQuantel

I was experiencing the same issue with the same root cause in one of my project and the patch provided by @wasimkham worked properly.

Project configuration

  • react-native: 0.77.3
  • react-native-ui-lib: 7.41.1
  • uilib-native: 4.5.1

Then switching to work to another one, I experienced the same issue too, but this time it was occurring on screen orientation change. Project configuration

  • react-native: 0.79.3
  • react-native-ui-lib: 7.46.0
  • uilib-native: 4.5.1

Applying the same patch fixed the issue too. So, Thanks to @wasimkham you just saved my day! 🎉🎉🎉

comwes avatar Sep 13 '25 15:09 comwes

Are there any updates?

FlorianB-DE avatar Sep 29 '25 10:09 FlorianB-DE

Still getting the same error

cagataykapuagasi avatar Sep 30 '25 11:09 cagataykapuagasi

Hello,

We have a version that supports new-arch (RN77), you can use the next tag for now. Please make sure to go over the v8 doc, it includes breaking changes and some known issues.

Please close this ticket if it solved your bug.

M-i-k-e-l avatar Oct 23 '25 11:10 M-i-k-e-l

I am still experiencing the same issue on the latest version with the following configuration:

  • expo: 54.0.21
  • react-native: 0.81.5
  • react-native-ui-lib: 7.46.3
  • uilib-native: 4.5.1

The patches listed above still work. Thanks a lot @wasimkham!

lorissikora avatar Oct 31 '25 10:10 lorissikora