本文主要介绍:
1、ReactNative是什么?
2、怎么安装ReactNative?
3、ReactNative与原生交互。
最近公司项目上需要用到ReactNative,于是着手开始研究使用。
一:先看看相关的一些资料
ReactNative简介:
2015年3月,React Native开源。它的目的就是允许web开发者更多的基于现有经验实现媲美原生App的用户体验。开发者只需学习JavaScript就能轻易为任何平台高效地编写代码。做到Learn once, write anywhere.
总结一下就是:APP上的用户体验很好,但是APP又分平台ios和android。要开发两套代码,成本太大。
疑问,这个是怎么做到的???
设想一下有没有这样的一个框架,即在android和ios之上再抽象一层框架,调用这个框架的视图,运行到真实的手机上显示时,这个视图会调用android或者ios原生的视图控件去展示呢。
答案就是ReactNative!
看图-React Native设计思路:
Android中的实现:
ReactNative中把index.android.js编译成Android工程目录下assets里的index.android.bundle文件。
在Android中,导入的ReactNative的jar包中的代码会根据这个bundle文件做相应的转换,转换成Android中“对应的代码”去执行。
二:安装配置篇
我在公司从事Android应用的开发。使用的是这样的
环境:Windows7、 Android Studio2.2、Gradle3.3、 JDK1.8 、NDK、Python、Node、react-native0.48、SDK、Android SDK Tools相关、Android真机
资料:
参考ReactNative中文网
地址:http://reactnative.cn/docs/0.48/getting-started.html#content
1、首先确保你的工程在Android Studio的Terminal窗口命令中,使用“gradle installDebug”命令能把项目编译运行起来。
2、安装Python、Node、安装Android SDK Tools相关(参照ReactNative中文网)
3、配置JDK、NDK、SDK环境变量
4、在你想创建项目的文件夹下执行以下命令:
react-native init demo
当创建了对应的工程文件夹:demo时,电脑连接上手机,执行以下命令:
cd demo
react-native run-android
备注:
react-native run-android 命令会启动一个Js服务器,
并安装测试的apk在你的手机中。程序启动后,使用菜单键或者摇一摇,可以加载调试菜单,从菜单中可以做以下事情:
1、可以配置服务器地址:电脑IP:8081,如192.168.43.77:8081
(注意8081端口不能被其他程序占用,如果占用自行百度查询解决方式;如果不小心开启了两个Server,就是原来的没关闭,然后又执行npm start,你可以使用Ctrl + C 退出一个)
2、可以重新加载(Reload)Js服务器上的Js文件进行展示;
可以remote Debug(使用Chrome浏览器);
...
以上步骤成功运行,说明demo可以跑通;接下来看原生应用添加ReactNative是怎么配置的。
5、将代码管理服务器(git或svn)上的原生应用工程clone到对应你创建的demo/android文件夹下。
6、工程根目录下build.gradle文件中添加:
allprojects {
repositories {
maven {
//重点
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"//添加这一句,意思就是在本地的node_modules文件夹中去加载Library:react-native0.48.jar
}
}
}
7、
在Module目录下的build.gradle中配置ndk支持的so文件路径
(根据项目中是否用到其他so库,考虑添加)
defaultConfig {
applicationId "com.android.demo"
minSdkVersion 20
targetSdkVersion 25
//支持分包
multiDexEnabled true
//重点
ndk {
//显示指定支持的ABIs .so文件的路径
abiFilters 'armeabi',"armeabi-v7a", "x86"
}
}
8、
在Module目录下的build.gradle中引入React-native包
dependencies {
compile fileTree(include: ['*.jar'], dir: '../libs')
//重点
compile 'com.facebook.react:react-native:+'
}
9、修改demo\android\gradle\wrapper目录下的gradle-wrapper.properties文件
distributionUrl参数为本地gradle文件。如:distributionUrl=file:///D:/gradle-3.3-all.zip
10、然后用AndroidStudio打开项目,会提示配置NDK。在提示框中配置相应的路径就可以了。(如果没提示,那直接编译就可以了)
11、demo\android\test目录下创建assets文件夹,然后使用命令行到工程目录下执行命令:
如:(注意目录路径的修改)
react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output
D:/workspace/demo/android/test/assets/index.android.bundle
--assets-dest D:/workspace/demo/android/test/res/
这个命令会在demo/android/test/assets目录下生成index.android.bundle和index.android.bundle.meta文件。
注意这是“一行”命令!
12、开启命令行窗口,cd到工程目录,执行命令 npm start 启动js服务器。然后在AS中运行工程。
备注:
1、关闭防火墙
2、当有Js服务器在运行的时,在AS中编译Rebuild或者运行一贯应用有时候回提示不能删除build目录下的文件,
导致项目编译报错的情况,需要关闭Js服务器。重新编译运行即可。
3、如果有armeabi相关的错误:在demo\android\test\libs目录下复制armeabi文件夹,替换名称为armeabi-v7a
三:ReactNative与原生交互
原生与Js通信
1、创建ReactContextBaseJavaModule的子类
2、重写getName()函数,用于返回一个字符串,这个字符串在JavaScript端标记这个模块。
3、暴露一个函数给javascript端,并且使用注解@ReactMethod进行标记,
该函数的返回值必须为void,React Native的跨语言访问是异步进行的,
所以想要给JavaScript返回一个值的唯一办法是使用回调函数或者发送事件。
4、在JS文件中需要:import {
NativeModules
} from 'react-native';
然后通过React.NativeModules.getName返回的字符串.标记的方法名,调用原生应用的方法。
例子:
@Override
public String getName() {
return "CurrentDetailModule";
}
//Module中声明的方法
@ReactMethod
public void receiveDataFromJS(String money) {
Toast.makeText(getReactApplicationContext(), money, Toast.LENGTH_LONG).show();
}
//JS端调用Nactive中的方法有两种:
//第一种方式调用:
React.NativeModules.CurrentDetailModule.receiveDataFromJS
//第二种方式调用:创建一个my.js文件,复制粘贴 以下内容
'use strict'
var {NativeModules} = require('react-native');
module.exports = NativeModules.MyModule;
在另一个js文件中这样调用
var toastExa = require('./my');
toast() {
toastExa.show('showtext');
}
以上,是最简单的调用。当Native需要传递数据到JS当中时,有三种方式:
第一种:
//由JS端调用,Native端回调数据给JS
@ReactMethod
public void getLoginState(Callback callback) {
try {
WritableMap map = Arguments.createMap();
map.putString("isLogin", "0");
callback.invoke(map);
} catch (IllegalViewOperationException e) {
Logger.i("React-Native:getLoginState"));
}
}
//JS端代码:
var bridge = NativeModules.CurrentDetailModule;
bridge.getLoginState((properties) => {
if (properties["isLogin"] == '0') {
bridge.gotoLogin();
} else {
//
}
})
第二种:
//主动发送数据的方式
@ReactMethod
public void sendDataToJs() {
WritableMap params = Arguments.createMap();
params.putString("SendData", "1234");
mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("eventName", params);
}
// JS端代码
componentWillMount() {
this.callNative.bind(this);
DeviceEventEmitter.addListener('eventName', this.onScanningResult);
}
onScanningResult = (e) => {
this.setState({
money: e.SendData,
});
}
第三种:Promises,跟第一种类似
@ReactMethod
public void measureLayout(Promise promise) {
try {
//measureLayout(mMeasureBuffer);
WritableMap map = Arguments.createMap();
map.putDouble("relativeX", PixelUtil.toDIPFromPixel(mMeasureBuffer[0]));
map.putDouble("relativeY", PixelUtil.toDIPFromPixel(mMeasureBuffer[1]));
promise.resolve(map);
} catch (IllegalViewOperationException e) {
promise.reject(e.getMessage());
}
}
//在JS端调用:
async function measureLayout() {
try {
var {
relativeX,
relativeY,
width,
height,
} = await UIManager.measureLayout(100, 100);
console.log(relativeX + ':' + relativeY + ':' + width + ':' + height);
} catch (e) {
console.error(e);
}
}
measureLayout();
原生应用与ReactNative相互传递数据时,相对应的数据类型,如下:
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
注:Flexbox是React Native开发中的核心布局。flexbox是Flexible Box的缩写,既为弹性布局。可以简单快速地完成各种伸缩设计。
JSX 是 JavaScript 语法的扩展。HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX语法,它允许 HTML 与 JavaScript 的混写。