ReactNative踩坑:ReactContext!=Activity

项目中有一个书架的View提供给ReactNative使用,监听ReactNative相应的生命周期进行界面刷新,代码如下

//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将ReactContext传给BookShelfView,并且在ReactContext上注册了LifecycleEvent的监听。希望的行为是界面展示的时候触发onHostResume方法执行refresh刷新界面。

然而测试发现一个情况,打开多个ReactNative页面后返回到书架页面时,不能正常刷新。

调试后发现,其他ReactNative页面关闭时也会回调BookShelfView的onHostDestroy使得注销了监听,所以再也收不到onHostResume,所以界面也无法刷新了。

ReactContext源码中onHostResume方法的参数已经表明一个ReactContext可以对应多个Activity,而onHostDestroy是直接通知所有的listener跟Activity无关。

  /**
   * 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;
  }

所以在使用LifecyclerEventListener的时候我们需要自己做一层保护,代码如下:

  @Override
  public void onHostResume() {
    //防止其他Activity resume触发刷新
    Activity activity  = mReactContext.getCurrentActivity();
    if (activity != null && activity == mContext){
      refresh();
    }
  }

  @Override
  public void onHostDestroy() {
    //友情提示:rn的ReactContext传给了不同activity,如果不作处理,
    //其他RN Activity onDestroy时候会使得监听注销,页面无法刷新.
    Activity activity  = mReactContext.getCurrentActivity();
    if (activity != null && activity == mContext){
      mReactContext.removeLifecycleEventListener(this);
    }
  }

不同的Activity是否共用ReactContext取决于ReactNative同学的代码,不过Android的代码还是应当做一层保护。

猜你喜欢

转载自blog.csdn.net/qq_15602635/article/details/79516309
今日推荐