目录
- 1)搭建-原生集成rn
- 2)demo-包含搭建过程源码
- 3)访问原生侧常量
- 4)访问原生侧方法
- 4.1)void无返回
- 4.2)有返回回调
- 5)原生启动RN
- 5.1)RN获取Activity传递的Intent数据
- 5.2)startActivityForResult返回Intent数据给Activity
- 6)RN启动原生
- 6.1)Activity获取RN传递的Intent数据
- 6.2)startActivityForResult返回Intent数据给RN
- 6.3)原生发送通知给RN
- 7)UI互嵌
- 7.1)原生界面内部分使用RN的界面
- 7.2)RN界面内部分使用原生的界面
1)搭建-原生集成rn
- react-native init 工程名
- copy原Android工程至Rn工程/android中,配置gradle
- app的gradle中加入
dependencies {
...
compile "com.facebook.react:react-native:+" // From node_modules.
}
- 项目gradle中加入
allprojects {
repositories {
...
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
...
}
- 配置权限
- 配置Application
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
//Debug模式,这个模式才能在JS里作调试
return BuildConfig.DEBUG;
}
@Override
protected List getPackages() {
//返回带有官方已有的package的集合
return Arrays.asList(
new MainReactPackage()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
- Android 6.0(API level 23)或更高版本,开发版本中会要求打开悬浮窗(overlay)权限,生产版本则不要求,代码可不用特别配置
- 新建Activity
public class FirstReactActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "FirstReactComponent"; //注册的react组件名,RN里要用到
}
}
- 新建RN-Component
export default class FirstReactComponent extends Component {
render() {
return (
大家好,我是React界面release
);
}
}
- 在RN的入口文件处注册此Component,注意所有暴露给原生的Component都需要在入口文件处注册。
~/index.android.js
import FirstReactComponent from './src/FirstReactComponent';
...
//暴露给原生使用
AppRegistry.registerComponent('FirstReactComponent', () => FirstReactComponent);
- 打开Packager服务
~/工程根目录
npm start
- 生产打release包
//创建keystore并配置gradle
signingConfigs {
release {
storeFile file("/Users/tugaofeng/tgf/study/rn/studyRn_Native/studyRn_Native_keystore")
storePassword "123456"
keyAlias "studyRn_Native_keystore"
keyPassword "123456"
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
//创建assets目录 ./android/app/src/main/assets
//创建离线bundle和打包本地资源
react-native bundle --entry-file index.android.js --bundle-output ./android/app/src/main/assets/index.android.bundle --platform android --assets-dest ./android/app/src/main/res/ --dev false
//打签名包即可
cd android && ./gradlew assembleRelease
//进入目录安装apk ./android/app/build/outputs/apk/release
adb install app-release.apk
2)demo
- 创建一个原生模块,继承了ReactContextBaseJavaModule的Java类,内部可以实现一些JavaScript所需的功能
public class MyNativeBridgeModule extends ReactContextBaseJavaModule {
//定义全局回调,RN->原生 startActivityForResult 返回数据给之前的 RNActivity
private Callback secondActivityOnActivityResultCallback;
//定义全局rnContext
private ReactApplicationContext rnContext;
public MyNativeBridgeModule(ReactApplicationContext reactContext) {
super(reactContext);
rnContext = reactContext;
//定义监听 RN->原生 startActivityForResult 返回数据给之前的 RNActivity
reactContext.addActivityEventListener(mActivityEventListener);
}
//重写此方法,
//可在RN端通过React.NativeModules.NativeBridge标记使用此模块
@Override
public String getName() {
return "MyNativeBridgeModule";
}
//此方法可被RN端访问常量
@Nullable
@Override
public Map getConstants() {
final Map constants = new HashMap();
constants.put("Duration_Short", Toast.LENGTH_SHORT);
constants.put("Duration_Long", Toast.LENGTH_LONG);
constants.put("Native_Constants_A", "我是Native常量值");
return constants;
}
//@ReactMethod 此注解定义与RN端的调用方法
@ReactMethod
public void showToast(String msg, int duration) {
Toast.makeText(getReactApplicationContext(), msg, duration).show();
}
//方法的返回类型必须为void,跨端访问为异步进行,需要返回值须写回调
@ReactMethod
public void testCallBack(String msg, Callback successCB, Callback errorCB) {
try {
String result = msg + " ,但我被testCallBack方法修改了";
successCB.invoke(result);
} catch (Exception e) {
errorCB.invoke("异常:" + e.getMessage());
}
}
//实例:原生->RN 获取之前Activity传递的数据
@ReactMethod
public void getDataFromIntent(Callback successCB, Callback errorCB) {
try {
Activity currentActivity = getCurrentActivity();
String intentString = currentActivity.getIntent().getStringExtra("intentData");
successCB.invoke(intentString);
} catch (Exception e) {
errorCB.invoke("异常:" + e.getMessage());
}
}
//实例:原生->RN startActivityForResult 返回数据给之前的Activity
@ReactMethod
public void finishActivityForResult(String msg) {
Activity currentActivity = getCurrentActivity();
Intent intent = new Intent();
intent.putExtra("intentData", msg);
currentActivity.setResult(Activity.RESULT_OK, intent);
currentActivity.finish();
}
//实例: RN->原生 打开新的Activity
//实际项目中可进行实际封装
@ReactMethod
public void startNewActivityByString(String activityName, String data, Callback resultCB) {
Activity currentActivity = getCurrentActivity();
//此处采用隐式启动,方便封装。若显示启动,需要反射获取class,或业务层上做kv映射。
Intent intent = new Intent("com.tgf.studyrn_native_android.SecondActivity");
//此处采用显示启动,需要反射
// Class aimActivity = null;
// try {
// aimActivity = Class.forName("com.tgf.studyrn_native_android.SecondActivity");
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// }
// Intent intent = new Intent(currentActivity,aimActivity);
//将回调函数赋值给全局变量,以便全局callback在mActivityEventListener中使用
secondActivityOnActivityResultCallback = resultCB;
currentActivity.startActivityForResult(intent, 1002);
}
//实例:RN->原生 startActivityForResult 返回数据给之前的 RNActivity
//方式一 : Callback回调
//方式二 : 发送通知
private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
switch (requestCode) {
case 1002:
//方式一 : Callback回调
// try {
// secondActivityOnActivityResultCallback.invoke(intent.getStringExtra("intentData"));
// }catch (Exception e){
// }
//方式二 : 发送通知
rnContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("notify", intent.getStringExtra("intentData"));
break;
}
}
};
}
- 创建自定义的ReactPackage,并在其中注册第一步创建的原生模块
public class MyReactPackage implements ReactPackage {
@Override
public List createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList(); //return null编译时会出错,要改写成返回空集合
}
//注册Native模块
@Override
public List createNativeModules(ReactApplicationContext reactContext) {
List modules = new ArrayList<>();
//注册自定义的Native模块
modules.add(new MyNativeBridgeModule(reactContext));
return modules;
}
@Override
public List> createJSModules() {
return Collections.emptyList(); //return null编译时会出错,要改写成返回空集合
}
}
- 在工程Application中,加入自定义的ReactPackage
~/MainApplication.java
@Override
protected List getPackages() {
//返回带有官方已有的package的集合
return Arrays.asList(
new MainReactPackage(),
new MyReactPackage() //加入自定义的Package类
);
}
- Android侧的RNActivity类,继承了ReactActivity
~/FirstReactActivity.java
public class FirstReactActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "FirstReactComponent"; //注册的react组件名,一会在JS里要用到
}
//如果回调时不需要回到RN端处理,
//也可此处定义onActivityResult
//而不是在MyNativeBridgeModule中写监听
// @Override
// public void onActivityResult(int requestCode, int resultCode, Intent data) {
// super.onActivityResult(requestCode, resultCode, data);
// switch (requestCode){
// case 1002:
// Toast.makeText(this,data.getStringExtra("intentData"),Toast.LENGTH_SHORT).show();
// break;
// }
// }
}
- RN侧的示例代码
~/FirstReactComponent.js
export default class FirstReactComponent extends Component {
constructor(props) {
super(props);
this.state = {
title:'大家好,我是React界面',
intentData:'',
};
}
//调用原生方法 showToast
_showToast = ()=>{
NativeModules.MyNativeBridgeModule.showToast('调用原生Toast',NativeModules.MyNativeBridgeModule.Duration_Short);
}
//调用原生方法 并获取回调结果
_testCallback = ()=>{
NativeModules.MyNativeBridgeModule.testCallBack(this.state.title,
(result)=>{
console.log(result);
//获取了回调结果,并修改了state的title
this.setState({
title:result,
})
},
(error)=>{
console.log(error);
}
);
}
//代码层接管RN的物理返回键,
//return true不做RN层的返回处理
//交由Native层处理ActivityResult
_onBackAndroid = () => {
NativeModules.MyNativeBridgeModule.finishActivityForResult("回传onActivityResult值1");
return true;
}
componentDidMount() {
//调用原生方法,获取Activity的Intent数据
NativeModules.MyNativeBridgeModule.getDataFromIntent(
(result) => {
//获取了回调结果,并修改了state的title
this.setState({
intentData:result,
})
},
(error) => {
console.log(error);
}
);
//注册Android物理返回键监听
BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid);
//注册通知
this.subscription = DeviceEventEmitter.addListener('notify',(result)=>{
NativeModules.MyNativeBridgeModule.showToast(result,NativeModules.MyNativeBridgeModule.Duration_Short);
});
}
componentWillUnmount() {
//解绑Android物理返回键监听
BackHandler.removeEventListener('hardwareBackPress', this._onBackAndroid);
// 移除通知
this.subscription.remove();
}
//调用原生方法,打开新的Activity,并可传递数据
_startNewActivity = ()=>{
NativeModules.MyNativeBridgeModule.startNewActivityByString("","",(result)=>{
NativeModules.MyNativeBridgeModule.showToast(result,NativeModules.MyNativeBridgeModule.Duration_Short);
});
}
render() {
//获取原生常量值
const Native_Constants_A = NativeModules.MyNativeBridgeModule.Native_Constants_A;
return (
{this.state.title}
{this.state.intentData}
{Native_Constants_A}
点我调用原生Toast
点我调用原生方法并获取回调结果
点我打开原生Activity(RN->Native)
);
}
}
//暴露给原生使用
AppRegistry.registerComponent('FirstReactComponent', () => FirstReactComponent);
3)访问原生侧常量
在自定义的原生模块中,通过重写getConstants()即可被RN访问常量。
RN通过NativeModules.MyNativeBridgeModule.Native_Constants_A;即可调用。
~/MyNativeBridgeModule.java
//此方法可被RN端访问常量
@Nullable
@Override
public Map getConstants() {
final Map constants = new HashMap();
constants.put("Duration_Short", Toast.LENGTH_SHORT);
constants.put("Duration_Long", Toast.LENGTH_LONG);
constants.put("Native_Constants_A", "我是Native常量值");
return constants;
}
~/FirstReactComponent.js
NativeModules.MyNativeBridgeModule.Native_Constants_A;
4)访问原生侧方法
原生通过注解 @ReactMethod暴露
4.1)void无返回
//@ReactMethod 此注解定义与RN端的调用方法
@ReactMethod
public void showToast(String msg, int duration) {
Toast.makeText(getReactApplicationContext(), msg, duration).show();
}
//调用原生方法 showToast
_showToast = ()=>{
NativeModules.MyNativeBridgeModule.showToast('调用原生Toast',NativeModules.MyNativeBridgeModule.Duration_Short);
}
4.2)有返回回调
由于跨端的访问都是异步进行,可以通过Callback回调函数获取返回值
//方法的返回类型必须为void,跨端访问为异步进行,需要返回值须写回调
@ReactMethod
public void testCallBack(String msg, Callback successCB, Callback errorCB) {
try {
String result = msg + " ,但我被testCallBack方法修改了";
successCB.invoke(result);
} catch (Exception e) {
errorCB.invoke("异常:" + e.getMessage());
}
}
//调用原生方法 并获取回调结果
_testCallback = ()=>{
NativeModules.MyNativeBridgeModule.testCallBack(this.state.title,
(result)=>{
console.log(result);
//获取了回调结果,并修改了state的title
this.setState({
title:result,
})
},
(error)=>{
console.log(error);
}
);
}
5)原生启动RN
Intent intent = new Intent(this,FirstReactActivity.class);
intent.putExtra("intentData","我是MainActivity传递的intentData");
startActivityForResult(intent,1001);
5.1)RN获取Activity传递的Intent数据
//实例:原生->RN 获取之前Activity传递的数据
@ReactMethod
public void getDataFromIntent(Callback successCB, Callback errorCB) {
try {
Activity currentActivity = getCurrentActivity();
String intentString = currentActivity.getIntent().getStringExtra("intentData");
successCB.invoke(intentString);
} catch (Exception e) {
errorCB.invoke("异常:" + e.getMessage());
}
}
componentDidMount() {
//调用原生方法,获取Activity的Intent数据
NativeModules.MyNativeBridgeModule.getDataFromIntent(
(result) => {
//获取了回调结果,并修改了state的title
this.setState({
intentData:result,
})
},
(error) => {
console.log(error);
}
);
}
5.2)startActivityForResult返回Intent数据给Activity
//实例:原生->RN startActivityForResult 返回数据给之前的Activity
@ReactMethod
public void finishActivityForResult(String msg) {
Activity currentActivity = getCurrentActivity();
Intent intent = new Intent();
intent.putExtra("intentData", msg);
currentActivity.setResult(Activity.RESULT_OK, intent);
currentActivity.finish();
}
通过接管返回操作来优先调用@ReactMethod方法。若在componentWillUnmount()中处理会导致Activity优先接收到onActivityResult,而后进入@ReactMethod方法。
//代码层接管RN的物理返回键,
//return true不做RN层的返回处理
//交由Native层处理ActivityResult
_onBackAndroid = () => {
NativeModules.MyNativeBridgeModule.finishActivityForResult("回传onActivityResult值1");
return true;
}
componentDidMount() {
//注册Android物理返回键监听
BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid);
}
componentWillUnmount() {
//解绑Android物理返回键监听
BackHandler.removeEventListener('hardwareBackPress', this._onBackAndroid);
}
6)RN启动原生
RN启动原生可采用隐式或显示(须反射)启动
6.1)Activity获取RN传递的Intent数据
//实例: RN->原生 打开新的Activity
//实际项目中可进行实际封装
@ReactMethod
public void startNewActivityByString(String activityName, String data, Callback resultCB) {
Activity currentActivity = getCurrentActivity();
//此处采用隐式启动,方便封装。若显示启动,需要反射获取class,或业务层上做kv映射。
Intent intent = new Intent("com.tgf.studyrn_native_android.SecondActivity");
//此处采用显示启动,需要反射
// Class aimActivity = null;
// try {
// aimActivity = Class.forName("com.tgf.studyrn_native_android.SecondActivity");
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// }
// Intent intent = new Intent(currentActivity,aimActivity);
//将回调函数赋值给全局变量,以便全局callback在mActivityEventListener中使用
secondActivityOnActivityResultCallback = resultCB;
currentActivity.startActivityForResult(intent, 1002);
}
//调用原生方法,打开新的Activity,并可传递数据
_startNewActivity = ()=>{
NativeModules.MyNativeBridgeModule.startNewActivityByString("","",(result)=>{
NativeModules.MyNativeBridgeModule.showToast(result,NativeModules.MyNativeBridgeModule.Duration_Short);
});
}
6.2)startActivityForResult返回Intent数据给RN
须定义监听BaseActivityEventListener,通过其onActivityResult来获取。
方式一:全局变量secondActivityOnActivityResultCallback发出回调给RN
方式二:rnContext.getJSModule发出通知给RN
//定义全局回调,RN->原生 startActivityForResult 返回数据给之前的 RNActivity
private Callback secondActivityOnActivityResultCallback;
//定义全局rnContext
private ReactApplicationContext rnContext;
public MyNativeBridgeModule(ReactApplicationContext reactContext) {
super(reactContext);
rnContext = reactContext;
//定义监听 RN->原生 startActivityForResult 返回数据给之前的 RNActivity
reactContext.addActivityEventListener(mActivityEventListener);
}
//实例:RN->原生 startActivityForResult 返回数据给之前的 RNActivity
//方式一 : Callback回调
//方式二 : 发送通知
private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
switch (requestCode) {
case 1002:
//方式一 : Callback回调
//try {
//secondActivityOnActivityResultCallback.invoke(intent.getStringExtra("intentData"));
//}catch (Exception e){
//}
//方式二 : 发送通知
rnContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("notify", intent.getStringExtra("intentData"));
break;
}
}
};
6.3)原生发送通知给RN
发送部分参见6.2
componentDidMount() {
//注册通知
this.subscription = DeviceEventEmitter.addListener('notify',(result)=>{
NativeModules.MyNativeBridgeModule.showToast(result,NativeModules.MyNativeBridgeModule.Duration_Short);
});
}
componentWillUnmount() {
// 移除通知
this.subscription.remove();
}
7)UI互嵌
7.1)原生界面内部分使用RN的界面
*实例:在原生Activity中部分使用RN的UI,应将Rn的组件作为Android中的fragment来使用
- 新建MyReactFragment继承Fragment
public abstract class MyReactFragment extends Fragment {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
//返回RN 的 Appregistry注册的组件名字.
public abstract String getMainComponentName();
@Override
public void onAttach(Context context) {
super.onAttach(context);
mReactRootView = new ReactRootView(context);
mReactInstanceManager =
((MainApplication) getActivity().getApplication())
.getReactNativeHost()
.getReactInstanceManager();
}
//这里改写根View为ReactRootView,因此继承此类的fragment实现`getMainComponentName`即可
@Override
public ReactRootView onCreateView(LayoutInflater inflater, ViewGroup group, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
return mReactRootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mReactRootView.startReactApplication(
mReactInstanceManager,
getMainComponentName(),
null
);
}
}
- 新建ThirdReactFragment继承第一步创建的MyReactFragment
public class ThirdReactFragment extends MyReactFragment {
@Override
public String getMainComponentName() {
//对应RN 的 Appregistry注册的组件名字.
return "ThirdReactComponent";
}
}
- RN侧创建ThirdReactComponent组件给原生侧使用
export default class ThirdReactComponent extends Component {
//调用原生方法 showToast
_showToast = ()=>{
NativeModules.MyNativeBridgeModule.showToast('调用原生Toast',NativeModules.MyNativeBridgeModule.Duration_Short);
}
render() {
return (
大家好 我是React的UI,我准备嵌入到原生UI里
);
}
}
//暴露给原生使用
AppRegistry.registerComponent('ThirdReactComponent', () => ThirdReactComponent);
- Android创建Activity
public class SecondActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mReactInstanceManager =
((MainApplication) getApplication()).getReactNativeHost().getReactInstanceManager();
Fragment viewFragment = new ThirdReactFragment();
getSupportFragmentManager().beginTransaction().add(R.id.container,viewFragment).commit();
}
//挂载的Activity还需要实现DefaultHardwareBackBtnHandler接口,以保证RN的生命周期=========
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}
}
7.2)RN界面内部分使用原生的界面
*示例:在RN的界面中部分使用原生的TextView组件。
- 创建一个视图管理类MyTextViewManager
public class MyTextViewManager extends SimpleViewManager {
@Override
public String getName() {
//方法返回的名字会用于在JavaScript端引用这个原生视图
return "MyTextViewManager";
}
@Override
protected ReactTextView createViewInstance(ThemedReactContext reactContext) {
ReactTextView textView = new ReactTextView(reactContext);
return textView;
}
//@ReactProp
//(name = "text") 这个参数指定了对应属性在JavaScript端的名字
@ReactProp(name = "text")
public void setText(ReactTextView textView,String text){
textView.setText(text);
}
}
- 在MyReactPackage注册第一步新建的ViewManager
~/MyReactPackage.java
@Override
public List createViewManagers(ReactApplicationContext reactContext) {
List modules = new ArrayList<>();
modules.add(new MyTextViewManager()); //注册自定义的ViewManager模块
return modules;
}
- 实现对应的JavaScript模块
import { PropTypes } from 'react';
import { requireNativeComponent, View } from 'react-native';
var myTextView = {
name: 'MyTextView',
propTypes: {
text:PropTypes.string,
...View.propTypes //包含默认的View的属性,如果没有这句会报‘has no propType for native prop’错误
},
};
module.exports = requireNativeComponent('MyTextViewManager', myTextView);
- 在Rn组件中引用此JS模块
import MyTextView from './MyTextView';
export default class SecondReactComponent extends Component {
constructor(props) {
super(props);
this.state = {
title:'大家好,我是SecondReactComponent界面',
};
}
render() {
return (
{this.state.title}
);
}
}
//暴露给原生使用
AppRegistry.registerComponent('SecondReactComponent', () => SecondReactComponent);
- 新建SecondReactActivity继承ReactActivity
public class SecondReactActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "SecondReactComponent"; //注册的react组件名,一会在JS里要用到
}
}
参考资料
官网
https://github.com/ipk2015/RN-Resource-ipk/tree/master/react-native-docs