/* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package com.facebook.react.bridge; import com.facebook.infer.annotation.Assertions; import com.facebook.react.module.annotations.ReactModule; import com.facebook.systrace.Systrace; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; /** A set of Java APIs to expose to a particular JavaScript instance. */ public class NativeModuleRegistry { private final ReactApplicationContext mReactApplicationContext; private final Map mModules; public NativeModuleRegistry( ReactApplicationContext reactApplicationContext, Map modules) { mReactApplicationContext = reactApplicationContext; mModules = modules; } /** Private getters for combining NativeModuleRegistrys */ private Map getModuleMap() { return mModules; } private ReactApplicationContext getReactApplicationContext() { return mReactApplicationContext; } /* package */ Collection getJavaModules(JSInstance jsInstance) { ArrayList javaModules = new ArrayList<>(); for (Map.Entry entry : mModules.entrySet()) { if (!entry.getValue().isCxxModule()) { javaModules.add(new JavaModuleWrapper(jsInstance, entry.getValue())); } } return javaModules; } /* package */ Collection getCxxModules() { ArrayList cxxModules = new ArrayList<>(); for (Map.Entry entry : mModules.entrySet()) { if (entry.getValue().isCxxModule()) { cxxModules.add(entry.getValue()); } } return cxxModules; } /* * Adds any new modules to the current module registry */ /* package */ void registerModules(NativeModuleRegistry newRegister) { Assertions.assertCondition( mReactApplicationContext.equals(newRegister.getReactApplicationContext()), "Extending native modules with non-matching application contexts."); Map newModules = newRegister.getModuleMap(); for (Map.Entry entry : newModules.entrySet()) { String key = entry.getKey(); if (!mModules.containsKey(key)) { ModuleHolder value = entry.getValue(); mModules.put(key, value); } } } /* package */ void notifyJSInstanceDestroy() { mReactApplicationContext.assertOnNativeModulesQueueThread(); Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "NativeModuleRegistry_notifyJSInstanceDestroy"); try { for (ModuleHolder module : mModules.values()) { module.destroy(); } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } } /* package */ void notifyJSInstanceInitialized() { mReactApplicationContext.assertOnNativeModulesQueueThread( "From version React Native v0.44, " + "native modules are explicitly not initialized on the UI thread. See " + "https://github.com/facebook/react-native/wiki/Breaking-Changes#d4611211-reactnativeandroidbreaking-move-nativemodule-initialization-off-ui-thread---aaachiuuu " + " for more details."); ReactMarker.logMarker(ReactMarkerConstants.NATIVE_MODULE_INITIALIZE_START); Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "NativeModuleRegistry_notifyJSInstanceInitialized"); try { for (ModuleHolder module : mModules.values()) { module.markInitializable(); } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); ReactMarker.logMarker(ReactMarkerConstants.NATIVE_MODULE_INITIALIZE_END); } } public void onBatchComplete() { // The only native module that uses the onBatchComplete is the UI Manager. Hence, instead of // iterating over all the modules for find this one instance, and then calling it, we // short-circuit // the search, and simply call OnBatchComplete on the UI Manager. // With Fabric, UIManager would no longer be a NativeModule, so this call would simply go away ModuleHolder moduleHolder = mModules.get("UIManager"); if (moduleHolder != null && moduleHolder.hasInstance()) { ((OnBatchCompleteListener) moduleHolder.getModule()).onBatchComplete(); } } public boolean hasModule(Class moduleInterface) { String name = moduleInterface.getAnnotation(ReactModule.class).name(); return mModules.containsKey(name); } public T getModule(Class moduleInterface) { ReactModule annotation = moduleInterface.getAnnotation(ReactModule.class); if (annotation == null) { throw new IllegalArgumentException( "Could not find @ReactModule annotation in class " + moduleInterface.getName()); } return (T) Assertions.assertNotNull( mModules.get(annotation.name()), annotation.name() + " could not be found. Is it defined in " + moduleInterface.getName()) .getModule(); } public boolean hasModule(String name) { return mModules.containsKey(name); } public NativeModule getModule(String name) { return Assertions.assertNotNull(mModules.get(name), "Could not find module with name " + name) .getModule(); } public List getAllModules() { List modules = new ArrayList<>(); for (ModuleHolder module : mModules.values()) { modules.add(module.getModule()); } return modules; } }