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;
}
}
如果有什么意見或者建議,可以留言評論。。。。