React native 如何替換RN使用的Module
前言
我們在使用RN的時候,發(fā)現(xiàn)很多RN自己的Module不合適自己使用,在我自己想替換okhttp使用SSL的時候,就發(fā)現(xiàn)了一個很奇葩的辦法拯钻,然后我用這種方法解決了吉嚣。但是自己覺得不合適观话,后面就忘了胃夏。但是經(jīng)過同事的提點轴或,我發(fā)現(xiàn)在RN0.47版本的時候支持替換RN的Module,仔細看了源碼發(fā)現(xiàn)真的支持仰禀。
源碼解析
在你的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 查看這里面會發(fā)現(xiàn)添加了很多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();
}
...
發(fā)現(xiàn)了一個ReactInstanceManager的類悬嗓,ReactNativeHost主要的方法就是生成了這個類污呼。繼續(xù)打開。查看上面的addPackage的方法包竹。
public ReactInstanceManagerBuilder addPackage(ReactPackage reactPackage) {
mPackages.add(reactPackage);
return this;
}
發(fā)現(xiàn)在reactPackage被添加到mPackages里面了燕酷,然后這個類里面搜索mPackages,發(fā)現(xiàn)周瞎。
/**
* @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);
}
}
酱讶。。彼乌。
}
發(fā)現(xiàn)執(zhí)行了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創(chuàng)建
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)) {
//如果不是懶加載模式的話,執(zhí)行這里
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);
}
從這里我們就可以發(fā)現(xiàn)剃允,如果要替換原生的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;
}
}
如果有什么意見或者建議淆九,可以留言評論。毛俏。炭庙。。