在 Fragment 中使用 React Native
React Native 官網提供了在 Activity 中使用 React Native 的方法,最近項目中需要在 Fragment 中使用 React Native,及各種嘗試摸索后總結方法如下。
1. MyApplication
MyApplication 除了實現 ReactApplication 的抽象方法 getReactNativeHost 外,還需要獲取到 ReactContext 并提供 get 接口,因為在 Fragment 里無法獲取到 ReactContext ,只能獲取 Context ,而原生調用 js 時使用 sendEvent 又需要用到 ReactContext 。
Fragment 中通過 ReactInstanceManager#getCurrentReactContext 獲取到的 ReactContext 為空。
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();
}
}
在 Application 的 onCreate 方法里注冊一個 ReactInstanceEventListener ,用于初始化后獲取到 ReactContext 。
2. ReactInstanceManager
通過 ReactNativeHost#getReactInstanceManager 可以獲取 ReactInstanceManager 這個抽象類,它提供了 ReactInstanceEventListener 接口及相應的添加和刪除方法。
/**
* 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);
}
3. BaseReactFragment
BaseReactFragment 繼承自自己封裝的 Fragment 基類 BaseFragment ,這里需要用到 ReactRootView 和 ReactInstanceManager 。
它們在 Fragment 的 onAttach 方法中獲取,并在 onCreateView 方法中返回該 ReactRootView 。
在 onActivityCreated 方法中即可使用我們的 React Native 組件,這里需要子類實現 getMainPageName 抽象方法,獲取到對應的 React Native 組件。
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);
}
}
}
sendEvent 方法用于原生調用 js 的接口,需要獲取到 ReactContext 對象,通過 ReactInstanceManager#getCurrentReactContext 獲取到的 ReactContext 為空,這里從 Application 中獲取。
創建一個 BaseReactFragment 的子類用于裝載 React Native 組件
publicclassMyFragmentextendsBaseReactFragment{
@Override
publicStringgetMainPageName(){
return"MyComponent";// name of our React Native component we've registered
}
}
4. BaseReactActivity
BaseReactFragment 所在的 Activity 必須實現 DefaultHardwareBackBtnHandler ,用于綁定 React Native 組件的生命周期。
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);
}
}
}
來自:https://danke77.github.io/2016/11/23/react-native-inside-fragment/