React native 如何替換RN使用的Module

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

如果有什么意見或者建議淆九,可以留言評論。毛俏。炭庙。。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末煌寇,一起剝皮案震驚了整個濱河市焕蹄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唧席,老刑警劉巖擦盾,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異淌哟,居然都是意外死亡迹卢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門徒仓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腐碱,“玉大人,你說我怎么就攤上這事掉弛≈⒓” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵殃饿,是天一觀的道長谋作。 經(jīng)常有香客問我,道長乎芳,這世上最難降的妖魔是什么遵蚜? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮奈惑,結果婚禮上吭净,老公的妹妹穿的比我還像新娘。我一直安慰自己肴甸,他們只是感情好寂殉,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著原在,像睡著了一般友扰。 火紅的嫁衣襯著肌膚如雪彤叉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天村怪,我揣著相機與錄音姆坚,去河邊找鬼。 笑死实愚,一個胖子當著我的面吹牛,可吹牛的內容都是我干的兔辅。 我是一名探鬼主播腊敲,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼维苔!你這毒婦竟也來了碰辅?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤介时,失蹤者是張志新(化名)和其女友劉穎没宾,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沸柔,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡循衰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了褐澎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片会钝。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖工三,靈堂內的尸體忽然破棺而出迁酸,到底是詐尸還是另有隱情,我是刑警寧澤俭正,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布奸鬓,位于F島的核電站,受9級特大地震影響掸读,放射性物質發(fā)生泄漏串远。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一寺枉、第九天 我趴在偏房一處隱蔽的房頂上張望抑淫。 院中可真熱鬧,春花似錦姥闪、人聲如沸始苇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽催式。三九已至函喉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荣月,已是汗流浹背管呵。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留哺窄,地道東北人捐下。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像萌业,于是被迫代替她去往敵國和親坷襟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內容

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