美文网首页
React native 如何替换RN使用的Module

React native 如何替换RN使用的Module

作者: 左耳钻爱思念 | 来源:发表于2017-10-10 11:30 被阅读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;
        }
    }
    

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

    相关文章

      网友评论

          本文标题:React native 如何替换RN使用的Module

          本文链接:https://www.haomeiwen.com/subject/wvrvyxtx.html