React native 如何替换RN使用的Module

2017-10-10  本文已影响0人  左耳钻爱思念

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;
    }
}

如果有什么意见或者建议,可以留言评论。。。。

上一篇下一篇

猜你喜欢

热点阅读