在 Fragment 中使用 React Native
615122606
8年前
<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>