Android原生集成react-native
在原有的 android 应用中集成 react-native。假设我们现在已经有了一个 android 原生应用 myapplication。
#第一步:进入到项目更目录下,新建一个空文件夹,命名为 android,然后将原本目录下所有的文件都移动到这个 android 文件夹下。
所以现在项目的根目录就是 myapplication,更目录下有一个 android 文件夹,里面是原来所有 android 原生应用的文件。
#第二步:在项目更目录下新建一个 package.json 文件用来安装所需要的 npm 依赖。package.json 文件内容大概为
{
"name": "myapplication",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"@babel/core": "^7.0.0-beta.42",
"babel-core": "^7.0.0-bridge.0",
"react": "16.3.0-alpha.1",
"react-native": "^0.54.2"
}
}
需要注意的是这里的 name 一定要与项目的名称保持一样。 react 和 react-native 以及其它库的版本根据当前最新版本适当修改即可。
然后在更目录下执行 npm install 即可。
#第三部:用 android studio 打开 android 下面的原生应用。在 app 的 build.gradle 文件中的 dependencies 闭包下加上一句
compile "com.facebook.react:react-native:+"
官网上是这么说的,同时官网上还要求了
compile 'com.android.support:appcompat-v7:23.0.1'
但是实际应用中发现一方面并不需要一定是 v7:23.0.1,而且更重要的是 compile 方式即将不支持了。换用 implementation。
而且新建 android 原生应用的时候会默认使用最新的编译库(当前是27)所以要是按照官网上的话,不仅需要修改便宜版本为23,
同时使用 compile。这样会导致很多问题。所以直接按照下面的配置就可以了:
//app: build.gradle
dependencies {
implementation 'com.android.support:appcompat-v7:27.1.1'
…… ……
implementation "com.facebook.react:react-native:+"
}
这样就不会有问题了。接着修改 project 的 build.gradle 文件,在 allprojects 闭包中的 repositories 闭包下添加 maven 依赖,
//project: build.gradle
allprojects {
repositories {
maven {
// all of react native (js, android binaries) is installed from npm
url "$rootdir/../node_modules/react-native/android"
}
...
}
...
}
这两部都修改完之后点击提示的 sync now. 构建完成之后应该不会出现错误。
#第四步:在 androidmanifest.xml 中添加网络权限:
当然如果是在 debug 模式下,方便调试还可以在
添加完之后大概为:
…………>
…… ……
#第五步:新建 app.js 和 index.js 文件,就像原本的 react-native 项目中那样,只是要记住在 index.js 文件中注册的名称比如
appregistry.registercomponent('myapplication', () => app); 这里的 myapplication
#第六步:因为在 debug 模式下的错误信息都是显示在悬浮窗中的,所以如果是在 android 6.0 或者以上的中,
我们需要动态的申请这个权限(当然,如果是在 release 模式下并不需要这个权限)。权限在希望启动 react-native 界面的活动下申请,
比如这里我们希望在 main2activity 这个活动中启动
private final int overlay_permission_req_code = 1;
@override
protected void oncreate(bundle savedinstancestate) {
…… ……
if (build.version.sdk_int >= build.version_codes.m) {
if (!settings.candrawoverlays(this)) {
intent intent = new intent(settings.action_manage_overlay_permission,
????????????????????????????????????????????uri.parse("package:" + getpackagename()));
startactivityforresult(intent, overlay_permission_req_code);
}
}
}
接着重写活动的 onactivityresult() 方法以处理用户对于上述权限的处理
@override
protected void onactivityresult(int requestcode, int resultcode, intent data) {
if (requestcode == overlay_permission_req_code) {
if (build.version.sdk_int >= build.version_codes.m) {
if (!settings.candrawoverlays(this)) {
// system_alert_window permission not granted
}
}
}
}
#第七部:将 react-native 组件加进来,在我们希望启动 react-native 的活动(这里的 main2activity)类实现
defaulthardwarebackbtnhandler 接口,然后通过 reactrootview 这个类将 react-native 组件加进来
public class main2activity extends appcompatactivity implements defaulthardwarebackbtnhandler {
private reactrootview mreactrootview;
private reactinstancemanager mreactinstancemanager;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
mreactrootview = new reactrootview(this);
mreactinstancemanager = reactinstancemanager.builder()
.setapplication(getapplication())
.setbundleassetname("index.android.bundle")
.setjsmainmodulepath("index")
.addpackage(new mainreactpackage())
.setusedevelopersupport(buildconfig.debug)
.setinitiallifecyclestate(lifecyclestate.resumed)
.build();
// 注意这里的名称(myapplication)必须与 index.js 文件中注册的名称一样
mreactrootview.startreactapplication(mreactinstancemanager, "myapplication", null);
setcontentview(mreactrootview);
}
@override
public void invokedefaultonbackpressed() {
super.onbackpressed();
}
}
这里可以看到,通过 setcontentview(mreactrootview) 方法将 react-native 的视图作为了这个活动的视图了。
做完这一步之后还需要注意,因为 react-native 界面顶部是没有 android 自带的 actionbar 的,所以我们需要把这个活动下视图的
actionbar 去掉,只要在
androidmanifest.xml 文件下对应的活动下修改视图的主题就可以了
android:theme="@style/theme.appcompat.light.noactionbar"> #第八步:将活动的生命周期传递给 reactinstancemanager: @override protected void onpause() { super.onpause(); if (mreactinstancemanager != null) { mreactinstancemanager.onhostpause(this); } } @override protected void onresume() { super.onresume(); if (mreactinstancemanager != null) { mreactinstancemanager.onhostresume(this, this); } } @override protected void ondestroy() { super.ondestroy(); if (mreactinstancemanager != null) { mreactinstancemanager.onhostdestroy(this); } if (mreactrootview != null) { mreactrootview.unmountreactapplication(); } } 同时在这个活动下,把后退按钮传递给 react-native: @override public void onbackpressed() { if (mreactinstancemanager != null) { mreactinstancemanager.onbackpressed(); } else { super.onbackpressed(); } } 另外,如果是在模拟器环境下,ctrl + m 按键调出调试面板是非常有用的,所以我们也可以重写这个按钮的事件 @override public boolean onkeyup(int keycode, keyevent event) { if (keycode == keyevent.keycode_menu && mreactinstancemanager != null) { mreactinstancemanager.showdevoptionsdialog(); return true; } return super.onkeyup(keycode, event); } #第九步:到此,集成已经完毕了,可以运行程序了。但是直接 react-native run-android 一般会有问题,不能正确的打开本地服务器。 一般用 android studio 将程序安装到手机或者模拟器上之后再通过 npm start 手动打开本地服务器. #最后还有一个问题,如果是运行在 64 位的手机或者模拟器上,程序会失败,提示错误信息为 dlopen failed: "/data/data/package/lib-main/libgnustl_shared.so" is 32-bit instead of 64-bit 这个好像是因为 react-native 要求的 .so 文件不支持 64 位的手机,解决办法就是 在 app:build.gradle 文件中在加上两个闭包 defaultconfig { …… …… ndk { abifilters "armeabi-v7a", "x86" } packagingoptions { exclude "lib/arm64-v8a/libgnustl_shared.so" } } 好了,到此就成功将 react-native 集成到 android 原生应用中啦!!! 下面是完整的 main2activity 活动的文件内容: ========================================================================================================= import android.content.intent; import android.net.uri; import android.os.build; import android.provider.settings; import android.support.v7.app.appcompatactivity; import android.os.bundle; import android.view.keyevent; import com.facebook.react.reactinstancemanager; import com.facebook.react.reactrootview; import com.facebook.react.common.lifecyclestate; import com.facebook.react.modules.core.defaulthardwarebackbtnhandler; import com.facebook.react.shell.mainreactpackage; public class main2activity extends appcompatactivity implements defaulthardwarebackbtnhandler { private final int overlay_permission_req_code = 1; private reactrootview mreactrootview; private reactinstancemanager mreactinstancemanager; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); if (build.version.sdk_int >= build.version_codes.m) { if (!settings.candrawoverlays(this)) { intent intent = new intent(settings.action_manage_overlay_permission, uri.parse("package:" + getpackagename())); startactivityforresult(intent, overlay_permission_req_code); } } mreactrootview = new reactrootview(this); mreactinstancemanager = reactinstancemanager.builder() .setapplication(getapplication()) .setbundleassetname("index.android.bundle") .setjsmainmodulepath("index") .addpackage(new mainreactpackage()) .setusedevelopersupport(buildconfig.debug) .setinitiallifecyclestate(lifecyclestate.resumed) .build(); mreactrootview.startreactapplication(mreactinstancemanager, "myapplication", null); setcontentview(mreactrootview); } @override public void invokedefaultonbackpressed() { super.onbackpressed(); } @override protected void onactivityresult(int requestcode, int resultcode, intent data) { if (requestcode == overlay_permission_req_code) { if (build.version.sdk_int >= build.version_codes.m) { if (!settings.candrawoverlays(this)) { // 如果悬浮窗权限申请失败的话…… } } } } @override protected void onpause() { super.onpause(); if (mreactinstancemanager != null) { mreactinstancemanager.onhostpause(this); } } @override protected void onresume() { super.onresume(); if (mreactinstancemanager != null) { mreactinstancemanager.onhostresume(this, this); } } @override protected void ondestroy() { super.ondestroy(); if (mreactinstancemanager != null) { mreactinstancemanager.onhostdestroy(this); } if (mreactrootview != null) { mreactrootview.unmountreactapplication(); } } @override public void onbackpressed() { if (mreactinstancemanager != null) { mreactinstancemanager.onbackpressed(); } else { super.onbackpressed(); } } @override public boolean onkeyup(int keycode, keyevent event) { if (keycode == keyevent.keycode_menu && mreactinstancemanager != null) { mreactinstancemanager.showdevoptionsdialog(); return true; } return super.onkeyup(keycode, event); } } =================================================================================================== react-native android 打包命令: 事先在 ./android/app/src/main/ 下要有一个 assets 文件夹,没有的话就手动建立一个即可 (附带,引用这个文件夹下的资源可通过 file:///android_asset/xxx) react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle --assets-dest android/com/your-company-name/app-package-name/src/main/res/ 例如这里就用 react-native bundle --platform android --dev false --entry-file index.js --bundle-output ./android/app/src/main/assets/index.android.bundle --assets-dest ./android/app/src/main/res/