React native 如何替换RN使用的Module
React native 如何替换RN使用的Module
前言
我们在使用RN的时候,发现很多RN自己的Module不合适自己使用,在我自己想替换okhttp使用SSL的时候,就发现了一个很奇葩的办法,然后我用这种方法解决了。但是自己觉得不合适,后面就忘了。但是经过同事的提点,我发现在RN0.47版本的时候支持替换RN的Module,仔细看了源码发现真的支持。
源码解析
在你的Application.java文件里面的getPackages方法中,我们可以看一个MainReactPackage方法。
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
打开MainReactPackage.class 查看这里面会发现添加了很多RN自己的方法。但是我们要看的不是这个方法,而且Application中的ReactNativeHost这个类。
...
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
.setApplication(mApplication)
.setJSMainModuleName(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
.setRedBoxHandler(getRedBoxHandler())
.setUIImplementationProvider(getUIImplementationProvider())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
return builder.build();
}
...
发现了一个ReactInstanceManager的类,ReactNativeHost主要的方法就是生成了这个类。继续打开。查看上面的addPackage的方法。
public ReactInstanceManagerBuilder addPackage(ReactPackage reactPackage) {
mPackages.add(reactPackage);
return this;
}
发现在reactPackage被添加到mPackages里面了,然后这个类里面搜索mPackages,发现。
/**
* @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
*/
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
。。。
// TODO(6818138): Solve use-case of native/js modules overriding
for (ReactPackage reactPackage : mPackages) {
Systrace.beginSection(
TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCustomReactPackage");
try {
processPackage(reactPackage, nativeModuleRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
。。。
}
发现执行了processPackage(reactPackage, nativeModuleRegistryBuilder, jsModulesBuilder),然后我们打开processPackage看,
private void processPackage(
ReactPackage reactPackage,
NativeModuleRegistryBuilder nativeModuleRegistryBuilder,
JavaScriptModuleRegistry.Builder jsModulesBuilder) {
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "processPackage")
.arg("className", reactPackage.getClass().getSimpleName())
.flush();
if (reactPackage instanceof ReactPackageLogger) {
((ReactPackageLogger) reactPackage).startProcessPackage();
}
nativeModuleRegistryBuilder.processPackage(reactPackage);
for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) {
jsModulesBuilder.add(jsModuleClass);
}
if (reactPackage instanceof ReactPackageLogger) {
((ReactPackageLogger) reactPackage).endProcessPackage();
}
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
实际是NatvieModuleRegistryBuilder.processPackage处理了reactPackage。于是我们接着打开看,
public void processPackage(ReactPackage reactPackage) {
if (mLazyNativeModulesEnabled) {
//RN的一种加载模式,具体代码我还没有仔细看
if (!(reactPackage instanceof LazyReactPackage)) {
throw new IllegalStateException("Lazy native modules requires all ReactPackage to " +
"inherit from LazyReactPackage");
}
//这里处理一下,reactPackage
LazyReactPackage lazyReactPackage = (LazyReactPackage) reactPackage;
List<ModuleSpec> moduleSpecs = lazyReactPackage.getNativeModules(mReactApplicationContext);
Map<Class, ReactModuleInfo> reactModuleInfoMap = lazyReactPackage.getReactModuleInfoProvider()
.getReactModuleInfos();
for (ModuleSpec moduleSpec : moduleSpecs) {
//遍历所有的包,这里会帮没有ReactModuleInfo创建
Class<? extends NativeModule> type = moduleSpec.getType();
ReactModuleInfo reactModuleInfo = reactModuleInfoMap.get(type);
ModuleHolder moduleHolder;
if (reactModuleInfo == null) {
if (BaseJavaModule.class.isAssignableFrom(type)) {
throw new IllegalStateException("Native Java module " + type.getSimpleName() +
" should be annotated with @ReactModule and added to a @ReactModuleList.");
}
ReactMarker.logMarker(
ReactMarkerConstants.CREATE_MODULE_START,
moduleSpec.getType().getSimpleName());
NativeModule module = moduleSpec.getProvider().get();
ReactMarker.logMarker(ReactMarkerConstants.CREATE_MODULE_END);
moduleHolder = new ModuleHolder(module);
} else {
moduleHolder = new ModuleHolder(
reactModuleInfo.name(),
reactModuleInfo.canOverrideExistingModule(),
reactModuleInfo.supportsWebWorkers(),
reactModuleInfo.needsEagerInit(),
moduleSpec.getProvider());
}
//核心代码 当本地namesToType包里面如果有相同的名字的包,并且getCanOverrideExistingModule是true的话,可以将原生里面的moduls替换掉。
String name = moduleHolder.getName();
if (namesToType.containsKey(name)) {
Class<? extends NativeModule> existingNativeModule = namesToType.get(name);
if (!moduleHolder.getCanOverrideExistingModule()) {
throw new IllegalStateException("Native module " + type.getSimpleName() +
" tried to override " + existingNativeModule.getSimpleName() + " for module name " +
name + ". If this was your intention, set canOverrideExistingModule=true");
}
mModules.remove(existingNativeModule);
}
namesToType.put(name, type);
mModules.put(type, moduleHolder);
}
} else {
FLog.d(
ReactConstants.TAG,
reactPackage.getClass().getSimpleName() +
" is not a LazyReactPackage, falling back to old version.");
for (NativeModule nativeModule : reactPackage.createNativeModules(mReactApplicationContext)) {
//如果不是懒加载模式的话,执行这里
addNativeModule(nativeModule);
}
}
}
public void addNativeModule(NativeModule nativeModule) {
//当本地namesToType包里面如果有相同的名字的包,并且getCanOverrideExistingModule是true的话,可以将原生里面的moduls替换掉。
String name = nativeModule.getName();
Class<? extends NativeModule> type = nativeModule.getClass();
if (namesToType.containsKey(name)) {
Class<? extends NativeModule> existingModule = namesToType.get(name);
if (!nativeModule.canOverrideExistingModule()) {
throw new IllegalStateException("Native module " + type.getSimpleName() +
" tried to override " + existingModule.getSimpleName() + " for module name " +
name + ". If this was your intention, set canOverrideExistingModule=true");
}
mModules.remove(existingModule);
}
namesToType.put(name, type);
ModuleHolder moduleHolder = new ModuleHolder(nativeModule);
mModules.put(type, moduleHolder);
}
从这里我们就可以发现,如果要替换原生的module,只需要name和你要替换的module一样,并且支持getCanOverrideExistingModule,就可以将原生替换掉。
写个测试的案例就是ToastModule,在你引用RN包的地方加入ToastModule(千万不要忘记),然后将代码添加canOverrideExistingModule,返回true,然后将getName返回的名字和原生的ToastModule一样即可替换。
public class ToastModule extends ReactContextBaseJavaModule {
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "ToastAndroid";
}
@Override
public boolean canOverrideExistingModule() {
return true;
}
}
如果有什么意见或者建议,可以留言评论。。。。