【React Native】在原生项目中集成之坑点总结

  大家座好,要开车了~~~

  俺是一个非常谨慎的人,对RN这门技术没有十足的信心之前,是不会直接拿来做新项目的(否则填坑,就能让你哭晕在厕所~)。

  但是还想要体验跨平台+实时发布带来的快感,最好不过的方法就是在现有的项目基础上进行改良,集成ReactNative框架,一个页面or一个View先使用RN搞起来(先挣它一个亿,慢慢来么,急什么),下面就是本人在Integration With Existing Apps (此处有英文显得高大上一些)的过程中踩得坑,又爬出来的过程,希望能对你有所帮助。

集成准备

  这方面网络上有好多的文案可以借鉴,(ReactNative向导),内容主要介绍npm和node的一些安装操作,没有这些基础建设,RN搞不转,如果你使用的是MAC,大致的关系如下:



还有就是Android和IOS的开发环境搭建,没什么难度。

  这里特别(记得是特别)需要提醒的地方就是compileSdkVersion及targetSdkVersion的版本问题(站好,要跳坑了)。

  先介绍三个基础的概念:

compileSdkVersion : 定义 Gradle 用哪个 Android SDK 版本编 译应用。
minSdkVersion : 定义应用可以运行的最低要求。
targetSdkVersion: 定义 Android 提供向前兼容的主要依据,如果不更新就不会应该最新版本的行为变化。

  好的,在RN官网介绍中有这么一段文字

Android Studio installs the most recent Android SDK by default. React Native,however,requires the Android 6.0 (Marshmallow) SDK.

  Android 6.0对应API等级是23,为什么需要Android 6.0,因为ReactNative引用了23的资源文件,另外有个别RN方法有23的等级限制。因此compileSdkVersion 23 就必不可免了,如果你性格倔强的话,就会看见下面的错误。



Error retrieving parent for item: No resource found that matches the given name 'xxx'.

  同理,如果遇到这类问题,也可以检查一下,使用的compileSdkVersion 版本是否正确。

  接下来,通过rebuild 编译程序,如果幸运的话,会看到下面的Gradle Build Message:

Error:错误: 程序包org.apache.http不存在
Error:错误: 程序包org.apache.http.message不存在

  对的,聪明的你肯定猜到了,Android API 23 移除了HttpClient 相关的类,解决办法如下:

android { useLibrary 'org.apache.http.legacy' }

  继续开车,此时我们还没有引入任何RN相关的依赖,好的run 一下程序,竟然Crash了,Word天啊,控制台打印出来的内容如下图:



Java.lang.SecurityException: xxx was not granted this permission :android.permission.xxx

  机智的我,祭出了Google神器,最终定位到了targetSdkVersion 23 ,在上面已经介绍过三个sdkVersion的区别,targetSdkVersion 如果升级,自动认为新版本的行为特性将生效,SDK 23中为了提高Android系统的安全性,Android开发小组进行了安全权限的重构,如果你没有做适配,原来能够正常运行的代码将可能Crash。
  如果你还没有做好,应用权限全面升级的准备的话,建议的填坑方案如下:

defaultConfig { minSdkVersion 16 targetSdkVersion 21 }

  车不能停,关注minSdkVersion 16 代码,这是能运行RN程序的最小SDK版本,作为一个大型应用,如果最小的安装版本定位在Level 16,显然说不过去,朋友介绍了一种解决方案:

<uses-sdk android:minsdkversion="11" android:targetsdkversion="21" tools:overridelibrary="com.facebook.react"></uses-sdk>

注: tools:overridelibrary 目的是重写react中的最小sdk版本为11

  但是切记小心使用,需要做好sdk版本兼容,小于16的版本,需要使用Native代码实现,或者跳转到空页面,否则会出现Crash。

  ok,集成之前的准备工作,大致的坑点就这么多,如果有其他的坑点,欢迎留言。

增加JS到你的应用

  这块非常简单,想遇到坑都很难。建议参考Add JS to your app 官网文档直接搞定。

引入react-native依赖

按官方文档搞两件事情,首先添加react-native依赖,其次添加maven的url。

dependencies {
    ...
    compile "com.facebook.react:react-native:+" // From node_modules.
}

allprojects {
    repositories {
        ...
        maven {
            // All of React Native (JS,Android binaries) is installed from npm
            url "$rootDir/../node_modules/react-native/android"
        }
    }
    ...
}

&幸运的事情发生了,编译项目错误提示如下:

Failed to resolve : com.facebook.react:react-native:xxx

  这个错误很好理解,就是Gradle在编译的时候,不知道引入的是个什么鬼,所以无法下载相应的dependency。

  尝试:开始以为是当前的react-native版本不在MavenCentral中,所以无法下载,于是打开Project Structure —> Dependencies —> Library Dependency —> 输入‘react-native’,确实给我返回了一个react-native的版本。选择引入后仍然出错(Cry,应该是姿势不对)。

  仔细的阅读官方文档(要说英文官方文档还是挺牛的,好多国内的翻译文章,很缺少细节),发现正确姿势,react-native都是通过npm来安装的。




  如果箭头指向的路径错误,将不能正确引入。因为我的应用结构采用的是eclipse的默认结构,所以我的修改方案为:
maven {
        url "$rootDir/node_modules/react-native/android"
    }

成功引入,OY!

重新编译程序,Word天




  这是部分间接依赖的错误日志,这些是ReactNative引用的开源库,通过 compile react-native会将间接依赖编译到程序中。知道少什么就好解决了,推荐的解决办法:
repositories{
    jcenter()
    mavenCentral()
}

如果你有更好的办法,麻烦给我留言奥~~

  到这里其实我们已经成功了一大步了,喘口气让我们继续填坑,马上就要看到胜利的曙光了(OY)

  通过上面的编译问题中也能看出,ReactNative自带很多的依赖库,这对我们自己的程序来说,如果我们也引用了其中的部分依赖,就很容易造成包重复的问题,比如:

Error:Execution failed for task ':processDebugAndroidTestResources'.
> Error: more than one library with package name 'android.support.v7.recyclerview' You can temporarily disable this error with android.enforceUniquePackageName=false However,this is temporary and will be enforced in 1.0
结构: more than one library with pachage name xxx

  解决的思路很简单,就是使用compile(){ exclude xxx} 排除掉重复的module,但是如何写很有讲究,否则会引发其他的问题。先给出亲测的正确的解决方案如下:

dependencies {
    compile('com.facebook.react:react-native:+') {
        exclude module: 'recyclerview-v7'
        exclude module: 'support-v4'
        exclude group: 'com.nineoldandroids',module: 'library'
    }
}

完美解决我的问题。下面是做过的错误重试及无效写法总结。

1). compile('com.facebook.react:react-native:+@aar') @aar的意思是只把当前的依赖包下载到本地,所有间接依赖不进行引入,这样确实能避免包重复问题。但是会引起下面的新问题,如下图:




因为ReactNative需要将所需的资源文件进行引入到当前项目中,如果所有的间接依赖都不进入,就会出现这个问题。

2). 如下的修改方案,将所有的support包进行屏蔽,同样会引发上面的错误。

compile('com.facebook.react:react-native:+') {
        exclude group: 'com.android.support'
 }

3). 可以说这是一个无效的写法,按着网上的很多文章的说法是ok的,但是我亲试却并没有起作用。

compile('com.facebook.react:react-native:+') {
       exclude group: 'com.android.support',module:'support-v4'
       exclude group: 'com.android.support',module:'support-v7-recyclerview'
    }

  如果大家重试第三种方案之后是可以的,也可以给我留言,网上说只写group可以,只写module也可以,都写仍然可以。我验证的结果这种结论是不正确的。

为解决包重复的问题,给大家推荐一个gradle的命令,

./gradlew -q sample:dependencies




能查看所有依赖使用的间接依赖及依赖版本。

项目中添加RN相关的Native代码

  add-native-code详细讲解向原有项目添加ReactNativeActivity的相关代码,此处无坑,放心使用。

  我在项目中尝试原生的LinearLayout使用ReactNativeView进行填充,把代码贴出来,仅供参考:

private LinearLayout mReactNativeLayout;
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;
    private void initReactView(){
        mReactNativeLayout = (LinearLayout) findViewById(R.id.react_native_layout);
        mReactRootView = new ReactRootView(mContext);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(((Activity)mContext).getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .setNativeModuleCallExceptionHandler(new NativeModuleCallExceptionHandler() {
                    @Override
                    public void handleException(Exception e) {

                    }
                })
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(false)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        mReactRootView.startReactApplication(mReactInstanceManager,"sampleScreenSaver",null);
        mReactNativeLayout.addView(mReactRootView);
    }

  有没有很腻害,ReactNative可以在原生的Activity、Fragment、View中任意添加,只要你喜欢。

运行程序

千呼万唤屎出来,终于可以运行了。O(∩_∩)O哈哈~

  车开起来,小坑填一下,就能抵达胜利的彼岸了。在这个过程中,我遇到一下几个小问题,我就按我遇到的问题的顺序梳理一下。

问题一:

Packager can't listen on port 8081

  解决办法:可能是其他的packager正在运行,关闭其他的Pachager即可。
问题二:

com.facebook.react.devsupport.JSException: Could not get BatchedBridge,make sure your bundle is packaged correctly

问题原因:没有找到执行的bundle文件。
解决方案:

  1. adb reverse tcp:8081 tcp:8081
  2. 如果1不起作用,使用react-native bundle 命令生成好bundle放在assets 中,程序就可以向引用资源一样,使用我的js文件。
  3. 命令同2,将下面的命令写到package.jsonscript中,当执行npm start 时,会自动打bundle。命令行如下:
$ react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output assets/index.android.bundle --sourcemap-output assets/index.android.map --assets-dest res/

要根据自己的项目目录结构进行修改。

问题三

Duplicate module name: react-native-vector-icons

  解决方案:一个Application应用存在多个node_module文件引发的问题,可能在Library中也有node_module可以删除掉其中一个,让另一个去引用已经存在的就OK。

运行程序,成功 !!! 请为自己鼓掌。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom