本文主题:如何实现原生代码的复用,即如何将原生模块封装。

(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/52862892

有时候我们的应用需要进行访问原生平台系统的API接口,但是React Native可能还没有封装相应功能组件。还有可能我们需要

去复用一些原生java代码而不是让JavaScript重新去实现一遍。或者我们可能需要些一些更加高级的功能代码,所线程相关的。例如:

图片处理,数据库以及一些高级功能扩展之类的。

React Native平台的开发其实本身也是可以让你写纯原生代码并且还可以让你访问原生平台的功能。这是一个比较高级的功能不

过官方还是不推荐你在平时开发中使用这样的开发形式。但是如果你具备这样的开发能力,也是还是不错的。特别当在React Native

暂时未提供部分原生功能或者模块,那么你可以使用这样的方法进行封装扩展。今天我们就来看一下原生组件的封装扩展方法。

总体来说,就是当我们需要复用部分原生代码时,比如复用一个原生方法,此时就需要将原生方法进行封装,只暴露出一个接口

来让React-Native调用。本博客以一个Toast消息来作用案例来讲解如何封装原生模块。

步骤:

  • 用Android Studio打开一个已经存在的RN项目,即用AS打开 项目文件夹/android/build.gradle文件。

  • 在Android原生这边创建一个类继承ReactContextBaseJavaModule,这个类里边放我们需要被RN调用的方法,将其封装成一个原生模块。

  • 在Android原生这边创建一个类实现接口ReactPackage包管理器,并把第二步创建的类加到原生模块(NativeModule)列表里。

  • 将第三步创建的包管理器添加到ReactPackage列表里(getPackage方法里)

  • 在RN中去调用原生模块,必须import    NativeModule模块。

首先大家肯定已经安装好了Android Studio,打开build.gradle文件之后,会发现其实连android/app文件夹也一并打开了。其中java文件夹中存放原生代码,也就是将我们要复用的原生代码放进来。大家可以打开  项目文件夹/android/app自行查看各级目录。在默认的包下,创建上边第二步和第三步所需的类。截图如下:

 

MyNativeModule.java代码如下:

package com.reactnative;

import android.widget.Toast;
import android.content.Context;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; /**
* Created by Administrator on 2016/10/18.
*/ public class MyNativeModule extends ReactContextBaseJavaModule { private Context mContext; public MyNativeModule(ReactApplicationContext reactContext) {
super(reactContext); mContext = reactContext;
} @Override
public String getName() { //返回的这个名字是必须的,在rn代码中需要这个名字来调用该类的方法。
return "MyNativeModule";
} //函数不能有返回值,因为被调用的原生代码是异步的,原生代码执行结束之后只能通过回调函数或者发送信息给rn那边。 @ReactMethod
public void rnCallNative(String msg){ Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show(); } }

本类中存放我们要复用的原生方法,继承了ReactContextBaseJavaModule类,并且实现了其getName()方法,构造方法也是必须的。按着Alt+Enter程序会自动提示。接着定义了一个方法,该方法必须使用注解@ReactMethod标明,说明是RN要调用的方法。

MyReactPackage.java代码如下:

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; /**
* Created by Administrator on 2016/10/18.
*/ public class MyReactPackage implements ReactPackage { @Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules=new ArrayList<>();
//将我们创建的类添加进原生模块列表中
modules.add(new MyNativeModule(reactContext));
return modules;
} @Override
public List<Class<? extends JavaScriptModule>> createJSModules() { //返回值需要修改
return Collections.emptyList();
} @Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { //返回值需要修改
return Collections.emptyList();
}
}

MainApplication.java代码如下:

package com.reactnative;

import android.app.Application;
import android.util.Log; import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage; import java.util.Arrays;
import java.util.List; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
} @Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
//将我们创建的包管理器给添加进来
new MyReactPackage()
);
}
}; @Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}

MainActivity.java代码如下:

package com.reactnative;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

    /**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "reactNative";
}
}

接着我们需要编写index.android.js文件

代码如下:

/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/ import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
NativeModules,
View
} from 'react-native'; class reactNative extends Component {
render() {
return (
<View style={styles.container}> <Text style={styles.welcome}
onPress={this.call_button.bind(this)}
>
React Native 调用原生方法!
</Text> <Text style={styles.instructions}>
To get started, edit index.android.js
</Text> <Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu
</Text> </View>
);
} call_button(){ NativeModules.MyNativeModule.rnCallNative('调用原生方法的Demo');
} } const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
}); AppRegistry.registerComponent('reactNative', () => reactNative);

  我们将一个文本框绑定了一个事件,首先import导入NativeModule,当点击文本时将会调用call_button()方法。然后传入一个上下文对象,也就是一句话,通过调用原生方法rnCallNative( )方法来实现。

来分析一下程序运行流程:

(1)在配置文件AndroidManifest.xml中,android:name=".MainApplication",则MainApplication.java会执行。

(2)在MainApplication.java中,有我们创建的包管理器对象。程序加入MyReactPackage.java中。

(3)在MyReactPackage.java中,将我们自己创建的模块加入了原生模块列表中,程序进入MyNativeModule.java中。

(4)在MyNativeModule.java中,有我们需要被复用的原生方法rnCallNative( )。

程序运行结果如下所示,当点击第一行文本时,出现Toast消息。

封装原生方法升级篇:

(1)如何封装复杂方法,实现更多的功能?

在上文中,我们封装了一个简单的方法—弹出Toast 提醒框。但是大家看到可能很郁闷,心里想,我TM 要封装的方法也不会这么简单呀,能不能封装点复杂的方法,用来实现更多的功能?(毕竟是我们想要复用的方法,肯定实现了很多比较牛逼的功能。)

答:能!!!

如何实现呢?以上面所述项目为例讲解。

  • 首先,将原生的java 文件复制到RN项目中存放原生代码的位置,如下图所示。
  • 然后,在MyNativeMoudle.java中写一个可以被RN调用的方法,以注解@ReactMethod表明。
  • 其次,在上一步所述方法内部可以任意调用原生方法,实现更加复杂的功能。
  • 最后,在RN中调用第二步所写的方法。

Demo如下:

ManiActivity.java

package com.firstproject;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

    /**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "FirstProject";
}
}

ManiApplication.java

package com.firstproject;

import android.app.Application;
import android.util.Log; import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage; import java.util.Arrays;
import java.util.List; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
} @Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new MyPackage()
);
}
}; @Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}

MyModule.java

package com.firstproject;

import android.content.Context;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; /**
* Created by Administrator on 2016/10/30.
*/ public class MyModule extends ReactContextBaseJavaModule { //建立上下文对象
public Context myContext; public MyModule(ReactApplicationContext reactContext) { super(reactContext); myContext=reactContext;
} @Override
public String getName() { return "MyModule";
} @ReactMethod
public void showTime()
{
new Test().getTime(myContext);
} }

MyPackage.java

package com.firstproject;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; /**
* Created by Administrator on 2016/10/30.
*/ public class MyPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules=new ArrayList<>();
modules.add(new MyModule(reactContext)); return modules;
} @Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
} @Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

Test.java作为我们要复用的原生类,里边有要复用的原生方法。

package com.firstproject;

import android.content.Context;
import android.util.Log;
import android.widget.Toast; import java.text.SimpleDateFormat;
import java.util.Date; /**
* Created by Administrator on 2016/10/30.
*/ public class Test { public void getTime(Context ctx)
{
SimpleDateFormat formatDate=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date date=new Date(System.currentTimeMillis()); //获取当前时间
String s=formatDate.format(date); Log.e("HHH",s);
Toast.makeText(ctx,s,Toast.LENGTH_SHORT).show(); } }

index.android.js如下:

/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/ import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
NativeModules,
View
} from 'react-native'; export default class FirstProject extends Component {
render() {
return (
<View style={styles.container}> <Text style={styles.welcome}
//给此处的文字绑定一个事件,其中callNative为要调用的方法名。
onPress={this.callNative.bind(this)}
>
点击此处文字调用原生方法!
</Text> <Text style={styles.instructions}>
此Demo演示如何调用Android原生中的复杂方法。
</Text> </View>
);
} callNative()
{
NativeModules.MyModule.showTime();
}
} const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
}); AppRegistry.registerComponent('FirstProject', () => FirstProject);

程序运行结果如下:

在上边的Demo中,主要是原生类Test.java。我们在要被RN调用的方法中调用原生类中的方法。和最开始的案

不同的是,这里说明了,方法可以调用,我们N多原生类都可以直接粘贴复制过来。这样就可以实现调用复杂方

实现强大功能了。

注意:当Android原生中涉及到权限的使用时,记得在AndroidManifest.xml中添加相应权限,如下图所示。


(2)如何实现数据从Android 原生回调到RN前端界面?

我们都知道,要被RN调用的方法必须是void 类型,即没有返回值,但是项目中很多地方都需要返回数据。那怎

么实现呢?

       如图所示:我们定义一个方法,使用Callback, 在这个方法中,建立并且开启一个线程,

使用callback. invoke( XXXX)实现数据向RN前端的传递。

       其中,MyMainActivity.java文件为我们的一个原生类,按照上面的要求复制到RN项目中存放原生代码的地

方。而ReceiveData为这个类的一个变量。

如何在RN项目中调用?


代码如下:

/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/ import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
NativeModules,
View,
TouchableOpacity
} from 'react-native'; export default class lh extends Component {
constructor(props) {
super(props);
this.state = {
global: '这个是预定的接受信息',
}
}
render() { var Globle="null";
return ( <View style={styles.container}> <TouchableOpacity style={styles.button1}
onPress={this.call_button_show.bind(this)}>
<Text style={styles.welcome}
>
显示信息
</Text>
</TouchableOpacity> <Text style={styles.welcome} >
{this.state.global}
</Text> </View>
);
} call_button_show(){
Globle="null"; NativeModules.MyModule.getResult((result)=>{this.setState({global:result,});});
} } const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
button1:{
height: 40,
width: 100,
marginTop:1,
backgroundColor:'gray',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
}); AppRegistry.registerComponent('lh', () => lh);

当我们点击按钮时,将会调用getResult方法,并且将ReceiveData变量的值传递给RN前端的result变量中,利用

setState来实现界面的更新。

  至此,我们实现了RN复用原生代码,即将原生模块封装成一个接口,在RN中调用。并且可以封装更加复杂

的方法,同时实现了数据回调,即将数据从原生模块中传递到RN前端。

程序源代码1下载,请见GitHub:https://github.com/chaohuangtianjie994/ReactNative-call-NativeMethod

Demo2的改动不大,大家可以自行改动哦。

如果对你有帮助,记得点赞哦

React-Native开发之原生模块封装(Android)升级版的更多相关文章

  1. React—Native开发之原生模块向JavaScript发送事件

    首先,由RN中文网关于原生模块(Android)的介绍可以看到,RN前端与原生模块之 间通信,主要有三种方法: (1)使用回调函数Callback,它提供了一个函数来把返回值传回给JavaScript ...

  2. win10的react native 开发环境搭建,使用Android模拟器

    1.打开cmd的管理员模式,win+X,选择命令提示符(管理员)即可,运行如下命令: @"%SystemRoot%\System32\WindowsPowerShell\v1.0\power ...

  3. React Native微信分享 朋友圈分享 Android/iOS 通用

    超详细React Native实现微信好友/朋友圈分享功能-Android/iOS双平台通用   2016/06/16 |  React Native技术文章 |  Sky丶清|  暂无评论 |  1 ...

  4. Hybrid App 和 React Native 开发那点事

    简介:Hybrid App(混合模式移动应用)开发是指介于Web-app.Native-App这两者之间的一种开发模式,兼具「Native App 良好用户交互体验的优势」和「Web App 跨平台开 ...

  5. React Native开发入门

    目录: 一.前言 二.什么是React Native 三.开发环境搭建 四.预备知识 五.最简单的React Native小程序 六.总结 七.参考资料   一.前言 虽然只是简单的了解了一下Reac ...

  6. React Native开发技术周报2

    (1).资讯 1.React Native 0.22_rc版本发布 添加了热自动重载功能 (2).技术文章 1.用 React Native 设计的第一个 iOS 应用 我们想为用户设计一款移动端的应 ...

  7. 《React Native 精解与实战》书籍连载「Node.js 简介与 React Native 开发环境配置」

    此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...

  8. React Native知识12-与原生交互

    一:原生传递参数给React Native 1:原生给React Native传参 原生给JS传数据,主要依靠属性. 通过initialProperties,这个RCTRootView的初始化函数的参 ...

  9. React Native开发技术周报1

    (一).资讯 1.React Native 0.21版本发布,最新版本功能特点,修复的Bug可以看一下已翻译 重要:如果升级 Android 项目到这个版本一定要读! 我们简化了 Android 应用 ...

随机推荐

  1. Github只下载某一目录的文件

    比如要下载: https://github.com/xubo245/SparkLearning/tree/master/docs 将“tree/master”改成“trunk https://gith ...

  2. php判断是否使用手机访问

    直接上代码 /** * 检测是否使用手机访问 * @access public * @return bool */ public function isMobile() { if (isset($_S ...

  3. java API连接虚拟机上的hbase

    今天用本地的eclipse连接虚拟机上的hbase数据库,代码如下: public static void main(String[] args) throws Exception{ Configur ...

  4. 杭电OJ第11页2010-2019道题(C语言)

    2010. 水仙花数 问题描述 Problem Description 春天是鲜花的季节,水仙花就是其中最迷人的代表,数学上有个水仙花数,他是这样定义的: "水仙花数"是指一个三位 ...

  5. 添加或删除 HTML dom元素

    添加或删除 HTML dom元素 创建新的 HTML 元素 如需向 HTML DOM 添加新元素,您必须首先创建该元素(元素节点),然后向一个已存在的元素追加该元素. <div id=" ...

  6. jmeter -- 在beanshell中拿到请求body参数和header参数

    beanshell: import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.http.control ...

  7. C# 连接Paradox DB

    Paradox数据库是一个成名于15年前的数据库,那时候Borland公司还存在.最近客户提出需求,要在一套用了12年+的应用程序上作些功能更改.这套应用程序使用Delphi+Paradox数据库. ...

  8. 优化 JS 条件语句的 5 个技巧

    优化 JS 条件语句的 5 个技巧 原创: 前端大全 前端大全 昨天 (给前端大全加星标,提升前端技能) 编译:伯乐在线/Mr.Dcheng http://blog.jobbole.com/11467 ...

  9. Python -- Gui编程 -- Win32API的使用

    消息框 messageBox.py import win32api, win32con win32api.MessageBox(0, 'Hello World!', 'Come Here', win3 ...

  10. Chrome插件下载地址

    www.crx4chrome.com可以直接下载 Chrome Store 插件 在chrome web store好像只能安装插件.