Reputation: 2648
In the React Native AppState library:
iOS has three states background->inactive->active
Android only has background->active
When an Android app is fully backgrounded the MainActivity goes from onPause -> onStop
When there is a System Notification e.g. an In app purchase it goes to onPause
I need to run some code when the app goes from background to the foreground
onStop -> onResume
I don't want it to run if the app was briefly paused because of a system notification
onPause -> onResume
Is this possible? The lifecycle events for React do not have an onHostStop
I tried emitting an event from MainActivity for each Activity lifecycle event but that caused the App to crash with a null pointer exception.
Is it even possible to emit an event to React Native from MainActivity?
Thanks
EDIT Added code to show attempt to emit event from MainActivity
MainActivity.java snippet
import com.facebook.react.ReactActivity;
import com.facebook.react.modules.core.DeviceEventManagerModule;
public class MainActivity extends ReactActivity {
DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter;
@Override
public void onStop() {
super.onStop();
eventEmitter.emit("onStop", "ActivityonStop");
}
}
React Native
const nativeEventListener = DeviceEventEmitter.addListener('onStop',
(e)=>{
console.log("NATIVE_EVENT");
dispatch({type: "NATIVE_EVENT"})
})
error in logcat
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.realer.android, PID: 15251
java.lang.RuntimeException: Unable to stop activity {com.realer.android/com.realer.android.MainActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'void com.facebook.react.modules.core.DeviceEventManagerModule$RCTDeviceEventEmitter.emit(java.lang.String, java.lang.Object)' on a null object reference
at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:3837)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3886)
at android.app.ActivityThread.-wrap25(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1494)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'void com.facebook.react.modules.core.DeviceEventManagerModule$RCTDeviceEventEmitter.emit(java.lang.String, java.lang.Object)' on a null object reference
at com.realer.android.MainActivity.onStop(MainActivity.java:40)
at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1289)
at android.app.Activity.performStop(Activity.java:6839)
at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:3834)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3886)
at android.app.ActivityThread.-wrap25(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1494)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
Upvotes: 19
Views: 18086
Reputation: 1877
According to docs
private void sendEvent(ReactContext reactContext,
String eventName,
@Nullable WritableMap params) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
If you are emitting from other class where context is not accessible then you can set Context to static class and then access it from any other classes.
// ContextHolder.java
import com.facebook.react.bridge.ReactApplicationContext;
public class ContextHolder {
private static ReactApplicationContext reactContext;
public static ReactApplicationContext getReactContext() {
return reactContext;
}
public static void setReactContext(ReactApplicationContext context) {
ContextHolder.reactContext = context;
}
}
So instead of reactContext
you can simply call ContextHolder.getReactContext()
Don't forget to set context in Module constructor
public class TheModule extends ReactContextBaseJavaModule {
public TheModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
ContextHolder.setReactContext(reactContext); // <- set
...
Upvotes: 1
Reputation: 478
Try updating your MainActivity.java like this:
public class MainActivity extends ReactActivity {
@Override
public void onStop() {
super.onStop();
WritableMap params = Arguments.createMap(); // add here the data you want to send
params.putString("event", "ActivityonStop"); // <- example
getReactInstanceManager().getCurrentReactContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("onStop", params);
}
}
Let me know if this works. In my apps I usually send the events from a class that extends ReactContextBaseJavaModule
where I can access the context just by calling getReactApplicationContext()
, but it seems that it might work if you can obtain the ReactContext
from the ReactInstanceManager
.
Upvotes: 27