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

如果有什么意見或者建議,可以留言評論。。。。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,721評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,825評論 18 139
  • 本篇文章是講述 iOS 無埋點數據收集 SDK 系列的第二篇。在第一篇 中主要介紹了 SDK 整體實現思路以及...
    zerygao閱讀 12,244評論 4 64
  • 陳林劍等人到了校場,這是一片平坦的練兵場所,一般是最不容易引人注意的。 “挖地三尺!” 陳林劍的一干手下們紛紛拿出...
    im喵小姐閱讀 1,840評論 0 1
  • 今天朋友圈和微信理里的內容基本被王寶強一家承包了。男人女人,更何況還是娛樂圈收人關注的大明星的生活永遠是最受歡迎的...
    酒釀蛋閱讀 215評論 2 0