ReactNative

本文主要介绍:
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设计思路:


图片1.png

Android中的实现:
ReactNative中把index.android.js编译成Android工程目录下assets里的index.android.bundle文件。
在Android中,导入的ReactNative的jar包中的代码会根据这个bundle文件做相应的转换,转换成Android中“对应的代码”去执行。

无标题.png

二:安装配置篇
我在公司从事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 的混写。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容