Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Only one callback may be registered to a function in a native module react native error

I recently added an android native module to my which listens on timezone and time changed broadcasts from the system and allows the app to perform some operations. The native module looks like this

public class TimezoneHandlerModule extends ReactContextBaseJavaModule {
    private final Context context;
    private final TimezoneChangeBroadcastReceiver timezoneChangeBroadcastReceiver;
    private Callback onTimezoneChangeCallback;

    public TimezoneHandlerModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.context = reactContext;
        this.timezoneChangeBroadcastReceiver = new TimezoneChangeBroadcastReceiver();
    }

    private void registerForTimezoneChangeHandler() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_TIME_CHANGED);
        intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
        getReactApplicationContext().registerReceiver(timezoneChangeBroadcastReceiver, intentFilter);
    }

    private void unregisterTimezoneChangeHandler() {
        getReactApplicationContext().unregisterReceiver(timezoneChangeBroadcastReceiver);
    }

    public void setOnTimezoneChangeCallback(Callback onTimezoneChangeCallback) {
        this.onTimezoneChangeCallback = onTimezoneChangeCallback;
    }

    /**
     * @return the name of this module. This will be the name used to {@code require()} this module
     * from javascript.
     */
    @Override
    public String getName() {
        return "TimezoneHandler";
    }

    @ReactMethod
    public void start(Callback onChange) {
        Log.d(getName(), "Starting the timezone change handler");
        this.registerForTimezoneChangeHandler();
        this.setOnTimezoneChangeCallback(onChange);
    }

    @ReactMethod
    public void stop() {
        Log.d(getName(), "Stopping the timezone change handler");
        this.unregisterTimezoneChangeHandler();
    }

    private class TimezoneChangeBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(getName(), "Received broadcast for timezone/time change " + intent.getAction());
            final String action = intent.getAction();

            if (action.equals(Intent.ACTION_TIME_CHANGED) || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
                TimezoneHandlerModule.this.onTimezoneChangeCallback.invoke();
            }
        }
    }
}

Two react methods are exposed start and stop. start takes a function as a parameter which is invoked whenever a broadcast for timezone changed or time changed is received. After hooking up the native module and starting the app in emulator, I opened Settings and change the timezone and I can see that the relevant logs are printed.

11-24 17:07:21.837 1597-1597/com.xyz D/TimezoneHandler: Received broadcast for timezone/time change
11-24 17:07:21.837 1597-1907/com.xyz I/ReactNativeJS: Detected timezone change

When I change the timezone again, I see below error in the logcat output

1-24 17:22:42.356 1597-1597/com.galarmapp D/TimezoneHandler: Received broadcast for timezone/time change
11-24 17:22:42.365 1597-1907/com.galarmapp E/ReactNativeJS: The callback start() exists in module TimezoneHandler, but only one callback may be registered to a function in a native module.
11-24 17:22:42.367 1597-1908/com.galarmapp E/unknown:React: The callback start() exists in module TimezoneHandler, but only one callback may be registered to a function in a native module., stack:
                                                            __invokeCallback@12814:10
                                                            <unknown>@12685:24
                                                            guard@12604:3
                                                            invokeCallbackAndReturnFlushedQueue@12684:6

From the error message, it seems as if I am trying to attach a separate callback to the start function but I am not doing any such thing. I am calling the start method in the componentWillMount of the top level component and have confirmed that it is not called twice. I see that other people have also seen this error while trying different things but still don't understand the reason behind the problem.

Please share if you have any insights.

like image 696
Varun Gupta Avatar asked Nov 24 '16 11:11

Varun Gupta


1 Answers

According to the documentation http://facebook.github.io/react-native/docs/native-modules-android.html#callbacks - "A native module is supposed to invoke its callback only once. It can, however, store the callback and invoke it later." Once you have done invoke() on the callback, you cannot use it again.

This particular use case of time zone change is better solved by sending events to javascript. See this documentation http://facebook.github.io/react-native/docs/native-modules-android.html#sending-events-to-javascript

like image 55
agenthunt Avatar answered Sep 23 '22 02:09

agenthunt