react nativeReact Native

React Native嵌入到Android原生应用

2017-06-20  本文已影响565人  沐风雨木

React native不仅可以在自己的js界面和现有工程的界面之间跳转,而且可以把js写的界面当成一个控件,嵌入到现有的activity,作为原生界面的一部分使用。但是看了React Native中文网的把React Native组件植入到Android,仿佛置身云雾里,硬着头皮尝试着去集成,遇到了很多坑,索性就打算写出来,为让后来的兄弟们少挖一些坑,废话少说,看:

注:最下面有我遇到的坑和解决办法,希望对你有所帮助。
今天(2018年1月16号),我用最RN新版(0.52.0),按照这篇文章,成功了。

一、把rn页面放在一个activity,在原生应用里启动该activity

1.在Android Studio(以后用AS代替)创建Android原生应用工程HelloRN。
2.在HelloRN工程的根目录中,安装react-native。可以直接在AS底部菜单栏Terminal命令行中运行

npm init

或者通过cmd命令行进入你工程的根目录,运行上面的命令。输入这个命令后,此时会提示你输入一系列参数,按照提示输入,我输入的参数如下:

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (myAppWithTest) helloas  // 输入项目名称
version: (1.0.0) // 回车,使用默认版本号
description: test native for react // 输入项目描述
entry point: (index.android.js) // 我们建立的入口文件是index.android.js所以填写index.android.js
test command:     // 回车,使用默认值
git repository: // 回车留空或填入Git地址
keywords: react test // 填写关键字react和test
author: andy // 填写作者
license: (ISC) // 回车,使用默认值

这里项目名helloas 要求小写。
3.创建js文件

npm install --save react
npm install --save react-native

之后会创建一个node_modules/的目录和package.json文件。

npm install --save react@16.0.0-alpha.6
npm install --save react-native@0.44.3

4.在根目录创建.flowconfig文件
mac环境:

curl -o .flowconfig https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig

windows环境:
没有curl命令,可以直接用浏览器打开https://raw.githubusercontent.com/facebook/react-native/master/.flowconfig ,将显示的内容直接保存到.flowconfig文件。

(后记)注:文件最后需要改成当前你用的RN版本
[version]
^0.44.3

5.打开package.json文件,在“scripts”内添加如下语句:"start": "node node_modules/react-native/local-cli/cli.js start"。


"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node node_modules/react-native/local-cli/cli.js start"
  }

其中的test节点是自动生成的,我们可以把它删除。
6.在根目录中创建index.android.js,加入简单的代码,你可以将如下内容粘贴到文件中

'use strict';
import React from 'react';
import { AppRegistry, StyleSheet, Text, View} from 'react-native';
class helloas extends React.Component { 
    render() { 
        return ( 
            <View style={styles.container}> 
                <Text style={styles.hello}>Hello, World</Text> 
            </View> 
        ) 
      }
    }

    var styles = StyleSheet.create({ 
        container: { 
            flex: 1, 
            justifyContent: 'center', 
        }, 
        hello: { 
            fontSize: 20, 
            textAlign: 'center', 
            margin: 10, 
        },
     });

AppRegistry.registerComponent('helloas', () => helloas);

注意,最后一句AppRegistry.registerComponent,第一个参数是前面package.json里设置的name,第二个参数是自定义的component。

7.在app的build.gradle中添加React Native的编译依赖

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

如果想要指定特定的React Native版本,可以用具体的版本号替换 +,当然前提是你从npm里下载的是这个版本(package.json里有) 。

8.在project的build.gradle中添加本地的React Native的marven目录

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

9.在AndroidManifest中添加如下的权限

<uses-permission android:name="android.permission.INTERNET"/>
/**设置调试 的权限**/
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />

10.在AndroidManifest的Application节点,添加如下Activity

<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

注意,该Activity仅在开发模式从开发服务器reload js时使用,所以在release模式下可精简。
index.android.js就是hello world界面。手机在debug模式下从自己的计算机下载index页面。下载的过程会有个提示的dialog,显示它需要上面的权限。6.0及以上的系统,除了在Manifest里授权,还需要在系统设置里的“应用-》在其他应用的上层显示”里,找到我们的应用,勾选上允许。
注:不同厂家的设置不太一样

11.添加原生代码

想要通过原生代码调用 React Native ,就像这样,我们需要在一个 Activity 中创建一个 ReactRootView 对象,将它关联一个 React application 并设为界面的主视图。

如果你想在安卓5.0以下的系统上运行,请用 com.android.support:appcompat 包中的 AppCompatActivity 代替 Activity 。
public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;
    public static int OVERLAY_PERMISSION_REQ_CODE = 1234;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                //.setJSMainModuleName("index.android")(这个已经没了)
                .setJSMainModulePath("index.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();

        // 注意这里的helloas必须对应“index.android.js”中的
        // “AppRegistry.registerComponent()”的第一个参数
        mReactRootView.startReactApplication(mReactInstanceManager, "helloas", null);  

        setContentView(mReactRootView);
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }
}

如果你的项目名字不是叫“HelloWorld”,则需要将“index.android.js”中的“AppRegistry.registerComponent()”方法中的第一个参数替换为对应的名字。

12.在AndroidManifest.xml文件中,为刚才创建的activity指定一个主题

 <activity
   android:name=".MyReactActivity"
   android:label="@string/app_name"
   android:theme="@style/Theme.AppCompat.Light.NoActionBar">
 </activity>

13.把一些activity的生命周期回调传递给ReactInstanceManager:

@Override
protected void onPause() {
    super.onPause();


    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostPause();
    }
}


@Override
protected void onResume() {
    super.onResume();


    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostResume(this, this);
    }
}


@Override
protected void onDestroy() {
    super.onDestroy();


    if (mReactInstanceManager != null) {
        mReactInstanceManager.onHostDestroy();
    }
}
@Override
 public void onBackPressed() {
    if (mReactInstanceManager != null) {
        mReactInstanceManager.onBackPressed();
    } else {
        super.onBackPressed();
    }
}

14.在MyReactActivity中添加按键响应函数:

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
        mReactInstanceManager.showDevOptionsDialog();
        return true;
    }
    return super.onKeyUp(keyCode, event);
}

15.配置权限以便开发中的红屏错误能正确显示

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);
    }
}
@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...
            }
        }
    }
}

16.在HelloRN程序的界面上�添加一个按钮,加载MyReactActivity。

Button bt = (Button)findViewById(R.id.start_react);
bt.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(MainActivity.this,MyReactActivity.class);
        startActivity(intent);
    }
});

17.运行你的应用
到这里基本可以在android studio中运行程序了,先在命令行启动js所需的服务器,执行下面的命令即可:

npm start
//或者
react-native start

运行截图如下:


Paste_Image.png

最后,在android studio,像启动其他程序一样运行程序,点击按钮就可以加载react native界面了。激动吧。如下图:

Paste_Image.png

二、将react native 写的界面当成组建嵌入到现有的activity

这个其实比较简单,新建一个布局xml文件,在上面的创建的MyReactActivity的onCreate函数中,像原生一样调用setContentView(R.layout.native_js); 然后通过addView 把reactnative 的界面,加入进入。下面是完整的onCreate函数代码:

public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.native_js);
        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        mReactRootView.startReactApplication(mReactInstanceManager, "HelloWorld", null);

        LinearLayout view = (LinearLayout) findViewById(R.id.react_root);
        view.addView(mReactRootView);

    }

编译运行,如下图,灰色是原声界面部分,蓝色为react native界面:

Paste_Image.png

三、遇到的坑

(后记)按照版本0.44来嵌入,我只遇到了 错误1 和 错误3 ,之后就成功了。

错误提示1:

Warning:Conflict with dependency 'com.google.code.findbugs:jsr305'. Resolved versions for app (3.0.1) and test app (2.0.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details.

解决办法:
在项目的app根目录中build.gradle中的

android {
...
    configurations.all {
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
    }
...
}

错误提示2:

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

解决办法:
方法1:修改项目中的package.json文件(亲测:不怎么好用)
看下你的文件中的是否有scripts模块,添加bundle-android,如图

Paste_Image.png
“bundle-android”: “react-native bundle –platform android –dev false –entry-file index.android.js –bundle-output android/app/src/main/assets/index.android.bundle –sourcemap-output android/app/src/main/assets/index.android.map –assets-dest android/app/src/main/res/”

注意:命令中的assets路径按照自己的项目自行调整

方法2:使用命令行直接生成,不用修改package.json,不管有没有bundle-android模块
首先cd到项目的根目录中,执行命令react-native bundle –platform android –dev false –entry-file index.android.js –bundle-output src/main/assets/index.android.bundle –assets-dest src/main/res/
运行效果如下图:

Paste_Image.png

注意:assets路径自己按照项目情况自行修改
这种方法可能出现会遇到一个问题,说是没有找到文件或文件集,如下图

Paste_Image.png

我们根据错误的提示路径,我们进入路径里面,路径如下

android/app/src/main/assets/index.android.bundle

发现main的文件夹里面没有assets文件夹,那么我们就新建一个文件夹,并命名为assets,然后在assets的文件夹里面新建一个空白文档命名为index.android.bundle然后保存即可。

至上面的两种方法最后生成的效果是一样的,都可以看到在项目的assets目录下生成了了来了两个文件,

Paste_Image.png

其中第二个文件 .meta亲测可以删除。

摇晃设备,reload js,是不是就大功告成了?结果屏幕继续飘红:


com.facebook.react.devsupport.DebugServerException: Could not connect to development server.

解决办法:点击Reload(R,R),打开调试菜单,点击Dev Settings,选Debug server host for device,输入你的正在运行packager的那台电脑的局域网IP加:8081(同时要保证手机和电脑在同一网段,且没有防火墙阻拦),再按back键返回,再按Menu键,在调试菜单中选择Reload JS,就应该可以看到运行的结果了。

这次应该出现“Hello, World”页面了吧,然而,如下图

Paste_Image.png
目前这个问题我也没有解决,预计原因是RN版本的问题。此时我的版本是0.45.1。
后来把版本改为了,如下图 Paste_Image.png
就成功了,啦啦啦。。。
错误提示3: Paste_Image.png
解决办法:关闭电脑的防火墙,如关闭360安全卫士、金山毒霸等。
错误提示4: Paste_Image.png
解决办法:需设置ip和端口。
最后,
附:查询电脑ip地址命令:ipconfig/all
连接网线时,采用 Paste_Image.png

连接wifi时,采用

Paste_Image.png
上一篇下一篇

猜你喜欢

热点阅读