在 Fragment 中使用 React Native

615122606 6年前
   <p>React Native 官网提供了在 Activity 中使用 React Native 的方法,最近项目中需要在 Fragment 中使用 React Native,及各种尝试摸索后总结方法如下。</p>    <h3><strong>1. MyApplication</strong></h3>    <p>MyApplication 除了实现 ReactApplication 的抽象方法 getReactNativeHost 外,还需要获取到 ReactContext 并提供 get 接口,因为在 Fragment 里无法获取到 ReactContext ,只能获取 Context ,而原生调用 js 时使用 sendEvent 又需要用到 ReactContext 。</p>    <p>Fragment 中通过 ReactInstanceManager#getCurrentReactContext 获取到的 ReactContext 为空。</p>    <pre>  <code class="language-java">publicclassMyApplicationimplementsReactApplication{  // ...    privateReactContext mReactContext;    publicReactContextgetReactContext(){  returnmReactContext;   }    privatefinalReactNativeHost mReactNativeHost =newReactNativeHost(this) {  @Override  protectedbooleangetUseDeveloperSupport(){  returnBuildConfig.DEBUG;   }    @Override  protectedList<ReactPackage>getPackages(){  returnArrays.<ReactPackage>asList(  newMainReactPackage(),  newMyReactPackage(),  newOtherReactPackage()  // ...   );   }   };    @Override  publicReactNativeHostgetReactNativeHost(){  returnmReactNativeHost;   }     privatevoidregisterReactInstanceEventListener(){   mReactNativeHost.getReactInstanceManager().addReactInstanceEventListener(mReactInstanceEventListener);   }    privatevoidunRegisterReactInstanceEventListener(){   mReactNativeHost.getReactInstanceManager().removeReactInstanceEventListener(mReactInstanceEventListener);   }    privatefinalReactInstanceManager.ReactInstanceEventListener mReactInstanceEventListener =newReactInstanceManager.ReactInstanceEventListener() {  @Override  publicvoidonReactContextInitialized(ReactContext context){   mReactContext = context;   }   };    @Override  publicvoidonCreate(){  // ...     registerReactInstanceEventListener();   }  }  </code></pre>    <p>在 Application 的 onCreate 方法里注册一个 ReactInstanceEventListener ,用于初始化后获取到 ReactContext 。</p>    <h3><strong>2. ReactInstanceManager</strong></h3>    <p>通过 ReactNativeHost#getReactInstanceManager 可以获取 ReactInstanceManager 这个抽象类,它提供了 ReactInstanceEventListener 接口及相应的添加和删除方法。</p>    <pre>  <code class="language-java">/**   * Add a listener to be notified of react instance events.   */  publicabstractvoidaddReactInstanceEventListener(ReactInstanceEventListener listener);    /**   * Remove a listener previously added with {@link#addReactInstanceEventListener}.   */  publicabstractvoidremoveReactInstanceEventListener(ReactInstanceEventListener listener);    /**   * Listener interface for react instance events.   */  publicinterfaceReactInstanceEventListener{  /**   * Called when the react context is initialized (all modules registered). Always called on the   * UI thread.   */  voidonReactContextInitialized(ReactContext context);  }  </code></pre>    <h3><strong>3. BaseReactFragment</strong></h3>    <p>BaseReactFragment 继承自自己封装的 Fragment 基类 BaseFragment ,这里需要用到 ReactRootView 和 ReactInstanceManager 。</p>    <p>它们在 Fragment 的 onAttach 方法中获取,并在 onCreateView 方法中返回该 ReactRootView 。</p>    <p>在 onActivityCreated 方法中即可使用我们的 React Native 组件,这里需要子类实现 getMainPageName 抽象方法,获取到对应的 React Native 组件。</p>    <pre>  <code class="language-java">publicabstractclassBaseReactFragmentextendsBaseFragment{    privateReactRootView mReactRootView;  privateReactInstanceManager mReactInstanceManager;    @Override  publicvoidonAttach(Activity activity){  super.onAttach(activity);   mReactRootView = newReactRootView(activity);   mReactInstanceManager = ((MyApplication) getActivity().getApplication()).getReactNativeHost().getReactInstanceManager();   }    @Nullable  @Override  publicViewonCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState){  super.onCreateView(inflater, container, savedInstanceState);  returnmReactRootView;   }    @Override  publicvoidonViewCreated(View view, Bundle savedInstanceState){   }    @Override  publicvoidonActivityCreated(@Nullable Bundle savedInstanceState){  super.onActivityCreated(savedInstanceState);   mReactRootView.startReactApplication(mReactInstanceManager, getMainPageName(), null);   }    protectedabstractStringgetMainPageName();    protectedvoidsendEvent(String eventName,   @Nullable WritableMap params) {  if(((MyApplication) getActivity().getApplication()).getReactContext() !=null) {   ((MyApplication) getActivity().getApplication()).getReactContext()   .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)   .emit(eventName, params);   }   }  }  </code></pre>    <p>sendEvent 方法用于原生调用 js 的接口,需要获取到 ReactContext 对象,通过 ReactInstanceManager#getCurrentReactContext 获取到的 ReactContext 为空,这里从 Application 中获取。</p>    <p>创建一个 BaseReactFragment 的子类用于装载 React Native 组件</p>    <pre>  <code class="language-java">publicclassMyFragmentextendsBaseReactFragment{  @Override  publicStringgetMainPageName(){  return"MyComponent";// name of our React Native component we've registered   }  }  </code></pre>    <h3><strong>4. BaseReactActivity</strong></h3>    <p>BaseReactFragment 所在的 Activity 必须实现 DefaultHardwareBackBtnHandler ,用于绑定 React Native 组件的生命周期。</p>    <pre>  <code class="language-java">publicclassBaseReactActivityextendsBaseActivityimplementsDefaultHardwareBackBtnHandler{  /*   * Get the ReactInstanceManager, AKA the bridge between JS and Android   * We use a singleton here so we can reuse the instance throughout our app   * instead of constantly re-instantiating and re-downloading the bundle   */  privateReactInstanceManager mReactInstanceManager;    @Override  protectedvoidonCreate(Bundle savedInstanceState){  super.onCreate(savedInstanceState);    /**   * Get the reference to the ReactInstanceManager   */   mReactInstanceManager =   ((MyApplication) getActivity().getApplication()).getReactNativeHost().getReactInstanceManager();   }    @Override  publicvoidinvokeDefaultOnBackPressed(){  super.onBackPressed();   }    /*   * Any activity that uses the ReactFragment or ReactActivty   * Needs to call onHostPause() on the ReactInstanceManager   */  @Override  protectedvoidonPause(){  super.onPause();    if(mReactInstanceManager !=null) {   mReactInstanceManager.onHostPause();   }   }    /*   * Same as onPause - need to call onHostResume   * on our ReactInstanceManager   */  @Override  protectedvoidonResume(){  super.onResume();    if(mReactInstanceManager !=null) {   mReactInstanceManager.onHostResume(this,this);   }   }    }  </code></pre>    <p> </p>    <p>来自:https://danke77.github.io/2016/11/23/react-native-inside-fragment/</p>    <p> </p>