/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.react.bridge; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; import com.facebook.react.common.MapBuilder; import com.facebook.react.common.SetBuilder; import com.facebook.infer.annotation.Assertions; import com.facebook.systrace.Systrace; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; /** * A set of Java APIs to expose to a particular JavaScript instance. */ public class NativeModuleRegistry { private final ArrayList<ModuleDefinition> mModuleTable; private final Map<Class<NativeModule>, NativeModule> mModuleInstances; private final String mModuleDescriptions; private final ArrayList<OnBatchCompleteListener> mBatchCompleteListenerModules; private NativeModuleRegistry( ArrayList<ModuleDefinition> moduleTable, Map<Class<NativeModule>, NativeModule> moduleInstances, String moduleDescriptions) { mModuleTable = moduleTable; mModuleInstances = moduleInstances; mModuleDescriptions = moduleDescriptions; mBatchCompleteListenerModules = new ArrayList<OnBatchCompleteListener>(mModuleTable.size()); for (int i = 0; i < mModuleTable.size(); i++) { ModuleDefinition definition = mModuleTable.get(i); if (definition.target instanceof OnBatchCompleteListener) { mBatchCompleteListenerModules.add((OnBatchCompleteListener) definition.target); } } } /* package */ void call( CatalystInstance catalystInstance, int moduleId, int methodId, ReadableNativeArray parameters) { ModuleDefinition definition = mModuleTable.get(moduleId); if (definition == null) { throw new RuntimeException("Call to unknown module: " + moduleId); } definition.call(catalystInstance, methodId, parameters); } /* package */ String moduleDescriptions() { return mModuleDescriptions; } /* package */ void notifyCatalystInstanceDestroy() { UiThreadUtil.assertOnUiThread(); Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "NativeModuleRegistry_notifyCatalystInstanceDestroy"); try { for (NativeModule nativeModule : mModuleInstances.values()) { nativeModule.onCatalystInstanceDestroy(); } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } } /* package */ void notifyCatalystInstanceInitialized() { UiThreadUtil.assertOnUiThread(); ReactMarker.logMarker("NativeModule_start"); Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "NativeModuleRegistry_notifyCatalystInstanceInitialized"); try { for (NativeModule nativeModule : mModuleInstances.values()) { nativeModule.initialize(); } } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); ReactMarker.logMarker("NativeModule_end"); } } public void onBatchComplete() { for (int i = 0; i < mBatchCompleteListenerModules.size(); i++) { mBatchCompleteListenerModules.get(i).onBatchComplete(); } } public <T extends NativeModule> T getModule(Class<T> moduleInterface) { return (T) Assertions.assertNotNull(mModuleInstances.get(moduleInterface)); } public Collection<NativeModule> getAllModules() { return mModuleInstances.values(); } private static class ModuleDefinition { public final int id; public final String name; public final NativeModule target; public final ArrayList<MethodRegistration> methods; public ModuleDefinition(int id, String name, NativeModule target) { this.id = id; this.name = name; this.target = target; this.methods = new ArrayList<MethodRegistration>(); for (Map.Entry<String, NativeModule.NativeMethod> entry : target.getMethods().entrySet()) { this.methods.add( new MethodRegistration( entry.getKey(), "NativeCall__" + target.getName() + "_" + entry.getKey(), entry.getValue())); } } public void call( CatalystInstance catalystInstance, int methodId, ReadableNativeArray parameters) { MethodRegistration method = this.methods.get(methodId); Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, method.tracingName); try { this.methods.get(methodId).method.invoke(catalystInstance, parameters); } finally { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } } } private static class MethodRegistration { public MethodRegistration(String name, String tracingName, NativeModule.NativeMethod method) { this.name = name; this.tracingName = tracingName; this.method = method; } public String name; public String tracingName; public NativeModule.NativeMethod method; } public static class Builder { private ArrayList<ModuleDefinition> mModuleDefinitions; private Map<Class<NativeModule>, NativeModule> mModuleInstances; private Set<String> mSeenModuleNames; public Builder() { mModuleDefinitions = new ArrayList<ModuleDefinition>(); mModuleInstances = MapBuilder.newHashMap(); mSeenModuleNames = SetBuilder.newHashSet(); } public Builder add(NativeModule module) { ModuleDefinition registration = new ModuleDefinition( mModuleDefinitions.size(), module.getName(), module); Assertions.assertCondition( !mSeenModuleNames.contains(module.getName()), "Module " + module.getName() + " was already registered!"); mSeenModuleNames.add(module.getName()); mModuleDefinitions.add(registration); mModuleInstances.put((Class<NativeModule>) module.getClass(), module); return this; } public NativeModuleRegistry build() { JsonFactory jsonFactory = new JsonFactory(); StringWriter writer = new StringWriter(); try { JsonGenerator jg = jsonFactory.createGenerator(writer); jg.writeStartObject(); for (ModuleDefinition module : mModuleDefinitions) { jg.writeObjectFieldStart(module.name); jg.writeNumberField("moduleID", module.id); jg.writeObjectFieldStart("methods"); for (int i = 0; i < module.methods.size(); i++) { MethodRegistration method = module.methods.get(i); jg.writeObjectFieldStart(method.name); jg.writeNumberField("methodID", i); jg.writeEndObject(); } jg.writeEndObject(); module.target.writeConstantsField(jg, "constants"); jg.writeEndObject(); } jg.writeEndObject(); jg.close(); } catch (IOException ioe) { throw new RuntimeException("Unable to serialize Java module configuration", ioe); } String moduleDefinitionJson = writer.getBuffer().toString(); return new NativeModuleRegistry(mModuleDefinitions, mModuleInstances, moduleDefinitionJson); } } }