集成 React Native 到现有Android项目

ReactNative混合开发,Facebook官网文档比较精简,坑比较多,根据此文档,作为初学者并不能顺利的将ReactNative引入Android项目,且不能了解ReactNative是以一种怎样的形式存在Native项目中。

此为开篇,解说官方引入方案,附带了我的踩坑经验,引导小诀窍,让你傻瓜式一键集成ReactNative。后续文章会介绍云集项目中React的实践,优化方案。这是一篇入门篇,没有门槛,何来高楼。

希望这一个系列的React文章能够帮助大家快速了解ReactNative的混合开发与掌握云集App中React的使用,快速定位相关问题,人人成为ReactNative小能手。

本文需要有初步的React Native基础,至少有能够运行一个React Native demo的环境与项目。

适用人群:Android工程师 Or 对ReactNative感兴趣的开发者

加载原理
(1)、Facebook有能够将页面的JS,通过自研的Metro工具转化一个bundle的压缩文件;
(2)、同时开发了一套能够解析Bundle文件中JS转化为Native行为的黑盒子工具API;
(3)、集成至于Native的根本原理,就是将Facebook的黑盒API引入到Android项目,同时能够加载Bundle文件;
(4)、集成ReactNative到原生项目分两大步:第一步引入依赖,第二步初始化ReactNative,加载页面;

1、正确引入ReactNative相关依赖

1.1、准备,组建一个React Native项目
  • 首先按照开发环境搭建教程来安装 React Native 在 Android 平台上所需的一切依赖软件,创建一个React项目,
//初始化创建项目-AwesomeProject 
npm init AwesomeProject
//下载React相关依赖,React Native (JS, Android binaries)等
npm install 
//安装项目
react-native run-android

项目中package.json文件,可修改指定的React Native版本号

  • 利用Android Studio模式打开AwesomeProject项目,编译Gradle

小技巧:成功编译后,对引入第三方lib,打包成aar,非常便捷

1.2、集成React Native
  • Android项目Lib or App下创建node_modules\react-native\android目录,并从AwesomeProject项目中node_modules\react-native\android目录下所有的文件拷贝至该目录

    对比案例图片

1.3、集成配置 maven
  • 你的 app 中 build.gradle 文件中添加 React Native 依赖:
dependencies {
    implementation 'com.android.support:appcompat-v7:27.1.1'
    ...
    //如要指定特定的 React Native 版本,可以用具体的版本号替换 +,当然前提是你从 npm 里下载的是这个版本。
    implementation "com.facebook.react:react-native:+" // From node_modules
}
  • 在项目的 build.gradle 文件中为 React Native 添加一个 maven 依赖的入口,必须写在 “allprojects” 代码块中:

    allprojects {
        repositories {
            maven {
                // All of React Native (JS, Android binaries) is installed from npm
                //上述项目中引入react-native的相对目录位置
                url "$rootDir/xxx/node_modules/react-native/android"
            }
            ...
        }
        ...
    }
    

$rootDir表示项目根目录

1.4、集成React Native的基础Lib
  • 你的 app 中 build.gradle 文件中添加-图片依赖
dependencies {
    ...
    //匹配图片框架与对应的版本号
    implementation 'com.facebook.fresco:animated-gif:2.0.0'
    implementation "com.facebook.fresco:webpsupport:2.0.0"
}

匹配版本号,从引入的ReactNative文件中,找出react-native-0.61.3.pom,查询对应的fresco的版本号

//打开此文件node_modules\...\react-native\react-native-0.61.3.pom查看各个依赖版本号
...
<dependency>
      <groupId>com.facebook.fresco</groupId>
      <artifactId>imagepipeline-okhttp3</artifactId>
      <version>2.0.0</version>
      <scope>compile</scope>
</dependency>
<dependency>
     <groupId>com.squareup.okhttp3</groupId>
     <artifactId>okhttp</artifactId>
     <version>3.12.1</version>
     <scope>compile</scope>
</dependency>
...
  • 引入第三方lib【推荐AAR方式 or 源码方式 】
//app 中 `build.gradle` 文件中添加 React Native 依赖
dependencies {
    ...
    implementation(name: 'react-native-video-5.0.2', ext: 'aar')
    implementation(name: 'react-native-vector-icons-6.6.0', ext: 'aar')
    ..........
}

小技巧:

1、通过Android Studio打开项目,在对应lib中,通过Gradle-assemble指令生成aar

2、第三方依赖某些适合并不完成与你的项目匹配,源码更容易修改调整

2、初始化ReactNative,加载开发页面Bundle

2.1、初始化React

applicatiion中,so初始化,ReactNativeHost初始化

public class MainApplication extends Application implements ReactApplication {

    private final ReactNativeHost mReactNativeHost =
            new ReactNativeHost(this) {

                /**
                 * 开发模式开发【红屏模式开关】
                 *
                 * @return
                 */
                @Override
                public boolean getUseDeveloperSupport() {
                    return BuildConfig.DEBUG;
                }

                /**
                 * 自定义React package链接处
                 *
                 * @return
                 */
                @Override
                protected List<ReactPackage> getPackages() {
                    @SuppressWarnings("UnnecessaryLocalVariable")
                    List<ReactPackage> packages = new PackageList(this).getPackages();
                    // Packages that cannot be autolinked yet can be added manually here, for example:
                    // packages.add(new MyReactNativePackage());
                    return packages;
                }

                /**
                 *  开发模式,地址路径
                 * @return
                 */
                @Override
                protected String getJSMainModuleName() {
                    return "index";
                }
            };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        /**
         * React相关so初始化
         */
        SoLoader.init(this, /* native exopackage */ false);
    }

   
}

需要在ReactNativeHost下重写getJSBundleFile(),指定bundle路径,否则将加载默认文件【assets://index.android.bundle】

2.2、加载React开发的页面,
public class MainActivity extends ReactActivity {

  @Override
  protected String getMainComponentName() {
    // Js入口AppRegistry.registerComponent中的appKey参数
    return "rnBuyer";
  }
}

到此为止普通的官方版的ReactNative已经集成完成,是不是很简单;【业务适用版集成方式,请关注后续React文章】

2.3、页面加载流程分解

①、React页面继承ReactActivity,逐步往上查看源码,可以看到ReactActivityDelegate.java中

........ 
//要么加载bundle入口
protected void loadApp(String appKey) {
    mReactDelegate.loadApp(appKey);
    getPlainActivity().setContentView(mReactDelegate.getReactRootView());
  }
.....

②、再次深入,facebook内部加载过程,ReactRootView.java呈现

ReactRootView.java
.....
  /**
   * Schedule rendering of the react component rendered by the JS application from the given JS
   * module (@{param moduleName}) using provided {@param reactInstanceManager} to attach to the JS
   * context of that manager. Extra parameter {@param launchOptions} can be used to pass initial
   * properties for the react component.
   */
  public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle initialProperties,
      @Nullable String initialUITemplate) {
    
      //...无关代码
    
     //子线程加载bundle
      mReactInstanceManager.createReactContextInBackground();
    
      //...无关代码
  }
.....

③、在深入,ReactInstanceManager类中选择对应的bundle来源,区分开发模式情况,与release情况

-----------------ReactInstanceManager.java
//主线程选择bundle
@ThreadConfined(UI)
private void recreateReactContextInBackgroundInner() {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.recreateReactContextInBackgroundInner()");
    PrinterHolder.getPrinter()
        .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: recreateReactContextInBackground");
    UiThreadUtil.assertOnUiThread();

     //开发者模式,取bundle方式, application中给mUseDeveloperSupport初始化赋值模式
    if (mUseDeveloperSupport && mJSMainModulePath != null) {
      final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();
      if (!Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
        if (mBundleLoader == null) {
          mDevSupportManager.handleReloadJS();
        } else {
          //mDevSupportManager.isPackagerRunning()后面React加载会优先检查metro服务器是否打通
          mDevSupportManager.isPackagerRunning(
              new PackagerStatusCallback() {
                @Override
                public void onPackagerStatusFetched(final boolean packagerIsRunning) {
                  UiThreadUtil.runOnUiThread(
                      new Runnable() {
                        @Override
                        public void run() {
                          //metro通,会优先加载服务器的bundle,否则加载本地
                          if (packagerIsRunning) {
                             //服务器bundle
                            mDevSupportManager.handleReloadJS();
                          } else if (mDevSupportManager.hasUpToDateJSBundleInCache()
                              && !devSettings.isRemoteJSDebugEnabled()) {
                            // If there is a up-to-date bundle downloaded from server,
                            // with remote JS debugging disabled, always use that.
                            onJSBundleLoadedFromServer(null);
                          } else {
                            // If dev server is down, disable the remote JS debugging.
                            devSettings.setRemoteJSDebugEnabled(false);
                            //本地bundle
                            recreateReactContextInBackgroundFromBundleLoader();
                          }
                        }
                      });
                }
              });
        }
        return;
      }
    }

    //release模式下,加载bundle路径
    recreateReactContextInBackgroundFromBundleLoader();
  }

-----------------release模式下继续加载bundle,ReactInstanceManager.java 
//......无关代码
@ThreadConfined(UI)
private void runCreateReactContextOnNewThread(final ReactInstanceManager.ReactContextInitParams initParams) {
    //.....无关代码
    
    //加载bundle
    final ReactApplicationContext reactApplicationContext =createReactContext(initParams.getJsExecutorFactory().create(),initParams.getJsBundleLoader());
   //.....无关代码
  }  
//.....无关代码
private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
       //.....无关代码
      //加载初始化赋值的本地bundle
      catalystInstance.runJSBundle();
     //.....无关代码
  }
 //.....无关代码

-----------------DevServerHelper.java meto服务间相关交互检查实践类
 .... 
//通过http://localhost:8081/status 检测metro是否通顺
public void isPackagerRunning(final PackagerStatusCallback callback)
 ....

④、回顾整个流程,整个流程图如图:

2.4、升级到ReactNative 0.61.3版本采坑
  • 仅支持so的armeabi_v7架构

  • 当Android 4.4系统中报xx.so找不到,但是在Android高版本中加载正常

    解决思路:xx.so与腾讯的xx.so冲突,报xx.so找不到,解析Apk,对比so的Md5后,发现xx.so非facebook提供的so,分析React的so加载流程,走串行链式加载方式

总结:

此次分享内容就是关于快速集成React到Native-Android的方式与踩坑,和解析了bundle在Android APP中是通过怎么流程进行加载的,方便大家快速上手集成,关于在云集项目中ReactNative是怎么封装使用的,见后续分享。

参考文献

2.4、升级到ReactNative 0.61.3版本采坑

  • 仅支持so的armeabi_v7架构

  • 当Android 4.4系统中报xx.so找不到,但是在Android高版本中加载正常

    解决思路:xx.so与腾讯的xx.so冲突,报xx.so找不到,解析Apk,对比so的Md5后,发现xx.so非facebook提供的so,分析React的so加载流程,走串行链式加载方式

总结:

此次分享内容就是关于快速集成React到Native-Android的方式与踩坑,和解析了bundle在Android APP中是通过怎么流程进行加载的,方便大家快速上手集成,关于在云集项目中ReactNative是怎么封装使用的,见后续分享。

参考文献

1、React Native中文官网

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
React Native提供了一个称为Native Modules的机制,允许您在React Native应用程序中使用原生代码。因此,您可以使用Java或Kotlin编写原生Android代码,并将其与React Native应用程序集成。以下是一些步骤: 1.创建一个新的Android项目。 2.在您的React Native项目中创建一个新的Native Module。 3.将您的原生代码添加到Android项目中。 4.编写Java或Kotlin代码来公开原生方法。 5.在React Native Native Module中使用这些方法。 6.构建并运行您的React Native应用程序。 这里是一个简单的例子,说明如何在React Native应用程序中使用原生Android模块: 1.创建一个新的Android项目Android Studio中,选择“File” > “New” > “New Module”。然后选择“Android Library”并按照向导中的说明创建一个新的Android项目。 2.在您的React Native项目中创建一个新的Native Module 在React Native项目的根目录下,运行以下命令: ``` react-native create-library MyNativeModule ``` 此命令将创建一个名为MyNativeModule的新目录。在此目录中,您可以添加一个名为MyNativeModule.java的文件。 3.将您的原生代码添加到Android项目中 将您的原生代码复制到Android项目中的src/main/java目录中。 4.编写Java或Kotlin代码来公开原生方法 在您的Java或Kotlin类中,使用@ReactMethod注释来标记要公开给React Native原生方法。例如: ``` @ReactMethod public void showToast(String message) { Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_SHORT).show(); } ``` 5.在React Native Native Module中使用这些方法 在您的React Native应用程序中,导入MyNativeModule并调用其方法。例如: ``` import { NativeModules } from 'react-native'; const { MyNativeModule } = NativeModules; MyNativeModule.showToast('Hello, world!'); ``` 6.构建并运行您的React Native应用程序 在React Native应用程序的根目录中,运行以下命令以构建并运行您的应用程序: ``` react-native run-android ``` 这样,您就可以在React Native应用程序中使用原生Android模块了!
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值