天天看点

Android原生集成react-native

在原有的 Android 应用中集成 react-native。假设我们现在已经有了一个 Android 原生应用 MyApplication。 主要参考: https://reactnative.cn/docs/0.51/integration-with-existing-apps.html#content https://facebook.github.io/react-native/docs/integration-with-existing-apps.html https://stackoverflow.com/questions/40694285/react-native-expection-java-lang-unsatisfiedlinkerror- dlopen-failed-data-dat?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa https://blog.csdn.net/chichengjunma/article/details/53815299

#第一步:进入到项目更目录下,新建一个空文件夹,命名为 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 中添加网络权限: <uses-permission android:name="android.permission.INTERNET" /> 当然如果是在 Debug 模式下,方便调试还可以在<Application> </ Application >内加上: < activity android:name= "com.facebook.react.devsupport.DevSettingsActivity" /> 添加完之后大概为: < manifest ………… > < uses-permission android:name= "android.permission.INTERNET" /> < application ………… > …… …… < activity android:name= "com.facebook.react.devsupport.DevSettingsActivity" /> </ application > </ manifest >

#第五步:新建 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 文件下对应的活动下修改视图的主题就可以了 < activity android:name= ".Main2Activity" android:theme= "@style/Theme.AppCompat.Light.NoActionBar" > </ activity >

#第八步:将活动的生命周期传递给 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/