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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,665评论 25 708
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,010评论 19 139
  • 本篇文章是讲述 iOS 无埋点数据收集 SDK 系列的第二篇。在第一篇 中主要介绍了 SDK 整体实现思路以及...
    zerygao阅读 12,288评论 4 64
  • 陈林剑等人到了校场,这是一片平坦的练兵场所,一般是最不容易引人注意的。 “挖地三尺!” 陈林剑的一干手下们纷纷拿出...
    im喵小姐阅读 1,864评论 0 1
  • 今天朋友圈和微信理里的内容基本被王宝强一家承包了。男人女人,更何况还是娱乐圈收人关注的大明星的生活永远是最受欢迎的...
    酒酿蛋阅读 221评论 2 0