您的位置:首页 > Web前端 > React

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

2017-06-03 19:07 477 查看
  大家座好,要开车了~~~http://blog.csdn.net/u013531824/article/details/53496011

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

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


集成准备

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


 

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

  这里特别(记得是特别)需要提醒的地方就是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'.
1
1

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

  接下来,通过
rebuild
 编译程序,如果幸运的话,会看到下面的Gradle Build Message:
Error:错误: 程序包org.apache.http不存在
Error:错误: 程序包org.apache.http.message不存在
1
2
1
2

  对的,聪明的你肯定猜到了,Android API 23 移除了
HttpClient
 相关的类,解决办法如下:
android {
useLibrary 'org.apache.http.legacy'
}
1
2
3
1
2
3

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



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

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

  如果你还没有做好,应用权限全面升级的准备的话,建议的填坑方案如下:
defaultConfig {
minSdkVersion 16
targetSdkVersion 21
}
1
2
3
4
1
2
3
4

  车不能停,关注
minSdkVersion 16
 代码,这是能运行RN程序的最小SDK版本,作为一个大型应用,如果最小的安装版本定位在Level 16,显然说不过去,朋友介绍了一种解决方案:
<uses-sdk android:minsdkversion="11" android:targetsdkversion="21" tools:overridelibrary="com.facebook.react"></uses-sdk>
1
1

注: 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"
}
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

&幸运的事情发生了,编译项目错误提示如下:
Failed to resolve : com.facebook.react:react-native:xxx
1
1

  这个错误很好理解,就是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"
}
1
2
3
1
2
3

成功引入,OY!

重新编译程序,Word天 


 

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

repositories{
jcenter()
mavenCentral()
}
1
2
3
4
1
2
3
4

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

  到这里其实我们已经成功了一大步了,喘口气让我们继续填坑,马上就要看到胜利的曙光了(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
1
2
1
2
结构: more than one library with pachage name xxx
1
1

  解决的思路很简单,就是使用
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
2
3
4
5
6
7
1
2
3
4
5
6
7

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

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



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

2). 如下的修改方案,将所有的support包进行屏蔽,同样会引发上面的错误。
compile('com.facebook.react:react-native:+') {
exclude group: 'com.android.support'
}
1
2
3
1
2
3

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'
}
1
2
3
4
1
2
3
4

  如果大家重试第三种方案之后是可以的,也可以给我留言,网上说只写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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

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


运行程序

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

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

问题一:
Packager can't listen on port 8081
1
1

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

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

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

解决方案:
adb reverse tcp:8081 tcp:8081
如果1不起作用,使用
react-native bundle
 命令生成好bundle放在
assets
 中,程序就可以向引用资源一样,使用我的js文件。
命令同2,将下面的命令写到
package.json
 的
script
中,当执行
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/
1
1

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

问题三
Duplicate module name: react-native-vector-icons
1
1

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

运行程序,成功 !!! 请为自己鼓掌。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: