ReactNative: ReactContext!=Activity

Posted by rinjani on Fri, 03 Apr 2020 10:46:41 +0200

In the project, there is a bookshelf View provided to react native for use. It monitors the corresponding life cycle of react native to refresh the interface. The code is as follows

//ViewManager
public class BookShelfManager extends SimpleViewManager<BookShelfView> {
  private BookShelfView mBookShelfView;

  @Override
  protected BookShelfView createViewInstance(ThemedReactContext reactContext) {
    mBookShelfView = new BookShelfView(reactContext.getCurrentActivity());
    reactContext.addLifecycleEventListener(mBookShelfView);
    mBookShelfView.setReactContext(reactContext);
    return mBookShelfView;
  }

  //...
}
public class BookShelfView extends LinearLayout implements LifecycleEventListener {

  public BookShelfView(Context context) {
    super(context);
    this.mContext = context;
    initView();
  }

  public void setReactContext(ReactContext reactContext){
    mReactContext = reactContext;
  }

  //form com.facebook.react.bridge.LifecycleEventListener
  @Override
  public void onHostResume() {
    refresh();
  }

  //form com.facebook.react.bridge.LifecycleEventListener
  @Override
  public void onHostDestroy() {
   mReactContext.removeLifecycleEventListener(this);
}

ViewManager passes the ReactContext to BookShelfView, and registers the listening of LifecycleEvent on the ReactContext. The desired behavior is to trigger the onHostResume method to refresh the interface when the interface is displayed.

However, the test found that when multiple react native pages were opened and returned to the shelf page, they could not refresh normally.

After debugging, it is found that when other react native pages are closed, the onHostDestroy of BookShelfView will also be called back to log off the listening, so the onHostResume will no longer be received, so the interface cannot be refreshed.

The parameter of onHostResume method in the source code of ReactContext has indicated that a ReactContext can correspond to multiple activities, while onHostDestroy directly informs all listener s that it has nothing to do with activities.

  /**
   * Should be called by the hosting Fragment in {@link Fragment#onResume}
   */
  public void onHostResume(@Nullable Activity activity) {
    mLifecycleState = LifecycleState.RESUMED;
    mCurrentActivity = new WeakReference(activity);
    ReactMarker.logMarker(ReactMarkerConstants.ON_HOST_RESUME_START);
    for (LifecycleEventListener listener : mLifecycleEventListeners) {
      try {
        listener.onHostResume();
      } catch (RuntimeException e) {
        handleException(e);
      }
    }
    ReactMarker.logMarker(ReactMarkerConstants.ON_HOST_RESUME_END);
  }

  /**
   * Should be called by the hosting Fragment in {@link Fragment#onDestroy}
   */
  public void onHostDestroy() {
    UiThreadUtil.assertOnUiThread();
    mLifecycleState = LifecycleState.BEFORE_CREATE;
    for (LifecycleEventListener listener : mLifecycleEventListeners) {
      try {
        listener.onHostDestroy();
      } catch (RuntimeException e) {
        handleException(e);
      }
    }
    mCurrentActivity = null;
  }

So when using the lifecycleeventlistener, we need to do a layer of protection ourselves, the code is as follows:

  @Override
  public void onHostResume() {
    //Prevent other Activity resume from triggering refresh
    Activity activity  = mReactContext.getCurrentActivity();
    if (activity != null && activity == mContext){
      refresh();
    }
  }

  @Override
  public void onHostDestroy() {
    //Friendly tip: the ReactContext of rn is passed to different activities. If it is not handled,
    //Other RN Activity onDestroy will cause the monitor to log off and the page cannot be refreshed
    Activity activity  = mReactContext.getCurrentActivity();
    if (activity != null && activity == mContext){
      mReactContext.removeLifecycleEventListener(this);
    }
  }

Whether different activities share ReactContext depends on the code of ReactNative students, but Android code should be protected.

Topics: React Fragment Android