React Native 与android原生交互
有时候App需要访问平台API,但React Native可能还没有相应的??榘?;或者你需要复用一些Java代码,而不是用Javascript重新实现一遍;又或者你需要实现某些高性能的、多线程的代码,譬如图片处理、数据库、或者各种高级扩展等等。
React Native设计为可以在其基础上编写真正的原生代码,并且可以访问平台所有的能力。这是一个相对高级的特性,React Native并不认为它应当在日常开发的过程中经常出现,但具备这样的能力是很重要的。如果React Native还不支持某个你需要的原生特性,你应当可以自己实现该特性的封装。
Toast模块
本向导会用Toast作为例子。假设我们希望可以从Javascript发起一个Toast消息(Android中的一种会在屏幕下方弹出、保持一段时间的消息通知)
我们首先来创建一个原生???。一个原生??槭且桓黾坛辛?code>ReactContextBaseJavaModule的Java类,它可以实现一些JavaScript所需的功能。我们这里的目标是可以在JavaScript里写ToastExample.show('Awesome', ToastExample.SHORT);
,来调起一个Toast通知。
ReactContextBaseJavaModule要求派生类实现getName方法。这个函数用于返回一个字符串名字,这个名字在JavaScript端标记这个???。这里我们把这个??榻凶鯰oastExample,这样就可以在JavaScript中通过React.NativeModules.ToastExample访问到这个???。译注:RN已经内置了一个名为ToastAndroid的???,所以在练习时请勿使用ToastAndroid的名字,否则运行时会报错名字冲突!
@Override
public String getName() {
return "ToastExample";
}
要导出一个方法给JavaScript使用,Java方法需要使用注解@ReactMethod。方法的返回类型必须为void。React Native的跨语言访问是异步进行的,所以想要给JavaScript返回一个值的唯一办法是使用回调函数或者发送事件(参见下文的描述)。
@ReactMethod
public void show(String message) {
Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_LONG).show();
}
参数类型
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
注册???/h4>
在Java这边要做的最后一件事就是注册这个??椤N颐切枰谟τ玫腜ackage类的createNativeModules方法中添加这个??椤H绻?槊挥斜蛔⒉?,它也无法在JavaScript中被访问到。
package com.awesomeproject;
import com.facebook.react.ReactPackage;
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;
public class AnExampleReactPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new ToastModule(reactContext));
return modules;
}
}
这个package需要在MainApplication.java文件的getPackages方法中提供。这个文件位于你的react-native应用文件夹的android目录中。具体路径是: android/app/src/main/java/com/your-app-name/MainApplication.java.
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new AnExampleReactPackage()); // <-- 添加这一行,类名替换成你的Package类的名字.
}
完整代码
- ReactContextBaseJavaModule 创建Module
package com.awesomeproject;
import android.widget.Toast;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.IllegalViewOperationException;
/**
* @author xiongxiang
* @time 2018/5/8.
* @e-mail 276186694@qq.com
*/
public class ToastModule extends ReactContextBaseJavaModule {
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
// NativeModules.ToastExample.show("????????");
@Override
public String getName() {
return "ToastExample";
}
//提供给RN调用 原生土司
@ReactMethod
public void show(String message) {
Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_LONG).show();
}
//React Naitve 回传数据
@ReactMethod
public void measureLayout(
int tag,
int ancestorTag,
Callback errorCallback,
Callback successCallback) {
sendEvent("noticeName",100);
try {
successCallback.invoke(1f, 2f, 3f, 4f);
} catch (IllegalViewOperationException e) {
errorCallback.invoke(e.getMessage());
}
}
//主动发送数据
public void sendEvent(String eventName, int status) {
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, status);
}
}
- ReactPackage 注册???/li>
package com.awesomeproject;
import com.facebook.react.ReactPackage;
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;
public class AnExampleReactPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new ToastModule(reactContext));
return modules;
}
}
package com.awesomeproject;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new AnExampleReactPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
代码调用
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, {Component} from 'react';
import {Button, DeviceEventEmitter, NativeModules, View} from 'react-native';
export default class NativeInteractionDemo extends Component {
//注册监听
componentDidMount() {
this.subscription = DeviceEventEmitter.addListener('noticeName', function (msg) {
alert("接收到安卓端主动发送的数据" + msg)
});
}
componentWillUnmount() {
this.subscription.remove();
}
render() {
return <View>
<Button
onPress={() => {
this.measureLayout();
this.showToast();
}
}
title="登陆"
/>
</View>
}
//调用安卓原生代码并回传数据
measureLayout() {
NativeModules.ToastExample.measureLayout(100, 100, (msg) => {
console.log(msg);
}, (x, y, width, height) => {
console.log(x + ':' + y + ':' + width + ':' + height);
}
);
}
//调用原生安卓土司
showToast() {
NativeModules.ToastExample.show("土司")
}
}