一、前言
二、React/JS界面调用原生界面
首先我们来看一下从React/JS界面进入原生界面,其实这一步是非常简单的。还记得我们上面一篇文章原生模块封装篇-基础篇,我们那时候讲解了如何封装一个Toast弹出提示的模块让React/JS进行使用。如果还记得我们在自定义继承ReactContextBaseJavaModule
的类中定义了一个使用@ReactMethod
注解的方法,该方法用于在React/JS前端来调用方法这个方法。在该方法中我们调用原生的Toast.show(xxx)
的方法来弹出toast提醒。OK,这个原理搞清楚了之后,那么这边处理打开原生的界面就非常简单了。
在Android原生开发中,我们知道打开一个Activity一般有两种方法:显式和隐式。隐式方法一般通过AndroidManifest 配置文件中的Activity Intent-Filter中进行相关拦截配置。那么这边我们主要讲解的是显示启动Activity。
下面我们这边在创建继承ReactContextBaseJavaModule
的IntentModule
模块,具体代码如下:
import android.app.Activity;
import android.content.Intent;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class IntentModule extends ReactContextBaseJavaModule {
public IntentModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "IntentModule";
}
/**
* 从JS页面跳转到原生activity 同时也可以从JS传递相关数据到原生
* @param name 需要打开的Activity的class
* @param params
*/
@ReactMethod
public void startActivityFromJS(String name, String params){
try{
Activity currentActivity = getCurrentActivity();
if(null!=currentActivity){
Class toActivity = Class.forName(name);
Intent intent = new Intent(currentActivity,toActivity);
intent.putExtra("params", params);
currentActivity.startActivity(intent);
}
}catch(Exception e){
throw new JSApplicationIllegalArgumentException(
"不能打开Activity : "+e.getMessage());
}
}
}
看到以上的代码,我们会发现和之前封装Toast模块差不多,我们也在这边加入了一个@ReactMethod
注解的startActivityFromJS
方法,该用于在JS代码进行调用,从JS端传过来是两个参数,第一个参数为需要打开的Activity的class,第二个参数为传递过来的数据。至于为什么用class,那是因为原生代码这边用反射的形式更加方便了,其他方式都有局限性。这边我们根据传入的class,然后直接调用startActivity直接打开相应的Activity即可,甚至也可以携带一些参数值。具体调用方法方式如下:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Button
} from 'react-native';
import ToastExample from './ToastExample';
import { NativeModules } from "react-native";
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
RN的界面
</Text>
<Button
title='Toast'
onPress={()=>{
ToastExample.show("Awesome", ToastExample.SHORT);
}}/>
<Button
title='measureLayout'
onPress={()=>{
ToastExample.measureLayout((msg)=>{
ToastExample.show(x + '坐标,' + y + '坐标,' + w + '宽,' + h+'高', ToastExample.SHORT);
},(x,y,w,h)=>{
ToastExample.show(x + '坐标,' + y + '坐标,' + w + '宽,' + h+'高', ToastExample.SHORT);
})
}}/>
<Button
title='跳转到原生界面'
onPress={()=>{
NativeModules.IntentModule.startActivityFromJS("com.zxj.myandroidtest.TwoActivity","我是从JS传过来的参数信息")
}}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
}
});
请关注以上的主要两行代码:
var { NativeModules } = require('react-native');
NativeModules.IntentModule.startActivityFromJS("com.zxj.myandroidtest.TwoActivity","我是从JS传过来的参数信息")}
三、React/JS界面调用原生界面 -等待原生返回数据
经过上面的讲解,大家应该知道了启动原生界面怎么样搞了吧,还是非常简单的吧!下面我们来看另外一种情况。如果我们的React/JS界面打开了原生界面,同时获取到原生的返回数据呢?做过原生开发的童鞋肯定知道,原生Activity中回调的数据一般在onActivityResult
中或者其他回调方法中的,但是如果需要返回给React/JS
的数据是通过封装的原生模块方法中的Callback
进行传输的。为了解决这个问题,我们这边创建一个阻塞的队列来实现,一旦有原生回调数据加入到队列中,那么数据就会从阻塞队列中取出来,再通过回调方法传入到React/JS
界面中。
下面我们看一下重载了onActivityResult
方法的ReactPageActivity
类中的写法:
package com.zxj.myandroidtest;
import android.content.Intent;
import com.facebook.react.ReactActivity;
import java.util.concurrent.ArrayBlockingQueue;
public class ReactPageActivity extends ReactActivity{
//构建一个阻塞的单一数据的队列
public static ArrayBlockingQueue<String> mQueue = new ArrayBlockingQueue<String>(1);
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "RNApp";
}
/**
* 打开 带返回的Activity
* @param requestCode
* @param resultCode
* @param data
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == 200) {
String result = data.getStringExtra("three_result");
if (result != null && !result.equals("")) {
mQueue.add(result);
} else {
mQueue.add("无数据啦");
}
} else {
mQueue.add("没有回调...");
}
}
}
然后在IntentModule
类中添加startActivityFromJSGetResult
方法:
/**
* 从JS页面跳转到Activity界面,并且等待从Activity返回的数据给JS
* @param className
* @param successBack
* @param errorBack
*/
@ReactMethod
public void startActivityFromJSGetResult(String className,int requestCode,Callback successBack, Callback errorBack){
try {
Activity currentActivity=getCurrentActivity();
if(currentActivity!=null) {
Class toActivity = Class.forName(className);
Intent intent = new Intent(currentActivity,toActivity);
currentActivity.startActivityForResult(intent,requestCode);
//进行回调数据
successBack.invoke(ReactPageActivity.mQueue.take());
}
} catch (Exception e) {
errorBack.invoke(e.getMessage());
e.printStackTrace();
}
}
请注意上面的方法中,启动Activity是通过startActivityForResult()
方法,这样打开的Activity有数据返回之后,才会调用之前的onActivityResult()
方法,然后我们在这个方法中把回调的数据添加到阻塞队列中。
接下来我们看一下React/JS界面调用的方法:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Button
} from 'react-native';
import ToastExample from './ToastExample';
import { NativeModules } from "react-native";
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
RN的界面
</Text>
<Button
title='Toast'
onPress={()=>{
ToastExample.show("Awesome", ToastExample.SHORT);
}}/>
<Button
title='measureLayout'
onPress={()=>{
ToastExample.measureLayout((msg)=>{
ToastExample.show(x + '坐标,' + y + '坐标,' + w + '宽,' + h+'高', ToastExample.SHORT);
},(x,y,w,h)=>{
ToastExample.show(x + '坐标,' + y + '坐标,' + w + '宽,' + h+'高', ToastExample.SHORT);
})
}}/>
<Button
title='跳转到原生界面'
onPress={()=>{
NativeModules.IntentModule.startActivityFromJS("com.zxj.myandroidtest.TwoActivity","我是从JS传过来的参数信息.456")
}}/>
<Button
title='跳转到Activity界面,并且等待数据返回...'
onPress={()=>{
NativeModules.IntentModule.startActivityFromJSGetResult("com.zxj.myandroidtest.ThreeActivity",
200, (msg)=>{
ToastExample.show('JS界面:从Activity中传输过来的数据为:'+msg,ToastExample.SHORT);
},(result)=>{
ToastExample.show('JS界面:错误信息为:'+result,ToastExample.SHORT);
});
}}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
}
});
ThreeActivity.java代码
public class ThreeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_three);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent();
intent.putExtra("three_result","From Activity的数据回调过来啦~");
ThreeActivity.this.setResult(Activity.RESULT_OK,intent);
ThreeActivity.this.finish();
}
});
}
}
四、原生界面调用React/JS界面
打开React/JS界面是非常简单的,但是另外一个问题又出现了,我们如果想在打开React/JS界面同时,从原生Activity中传点数据过去呢?其实非常简单了,我们知道Activity之间打开传递数据会通过Intent的,那么我们就可以在承载React/JS界面的Activity中获取当前Intent中的数据,然后通过Callback方法回调即可。下面来一下实例源代码:
我们看一下IntentModule类中的dataToJS()方法:
/**
* Activtiy跳转到JS页面,传输数据
* @param successBack
* @param errorBack
*/
@ReactMethod
public void dataToJS(Callback successBack, Callback errorBack){
try{
Activity currentActivity = getCurrentActivity();
String result = currentActivity.getIntent().getStringExtra("data");
if (TextUtils.isEmpty(result)){
result = "没有数据";
}
successBack.invoke(result);
}catch (Exception e){
errorBack.invoke(e.getMessage());
}
}
接着在React/JS端componentDidMount()方法中调用原生封装的方法去获取传过来的数据:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Button
} from 'react-native';
import ToastExample from './ToastExample';
import { NativeModules } from "react-native";
export default class App extends Component {
//当组件挂载之后,去获取Activity传输过来的数据...
componentDidMount(){
//进行从Activity中获取数据传输到JS
NativeModules.IntentModule.dataToJS((msg) => {
console.log(msg);
ToastExample.show('JS界面:从Activity中传输过来的数据为:'+msg,ToastExample.SHORT);
},
(result) => {
ToastExample.show('JS界面:错误信息为:'+result,ToastExample.SHORT);
})
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
RN的界面
</Text>
<Button
title='Toast'
onPress={()=>{
ToastExample.show("Awesome", ToastExample.SHORT);
}}/>
<Button
title='measureLayout'
onPress={()=>{
ToastExample.measureLayout((msg)=>{
ToastExample.show(x + '坐标,' + y + '坐标,' + w + '宽,' + h+'高', ToastExample.SHORT);
},(x,y,w,h)=>{
ToastExample.show(x + '坐标,' + y + '坐标,' + w + '宽,' + h+'高', ToastExample.SHORT);
})
}}/>
<Button
title='跳转到原生界面'
onPress={()=>{
NativeModules.IntentModule.startActivityFromJS("com.zxj.myandroidtest.TwoActivity","我是从JS传过来的参数信息.456")
}}/>
<Button
title='跳转到Activity界面,并且等待数据返回...'
onPress={()=>{
NativeModules.IntentModule.startActivityFromJSGetResult("com.zxj.myandroidtest.ThreeActivity",
200, (msg)=>{
ToastExample.show('JS界面:从Activity中传输过来的数据为:'+msg,ToastExample.SHORT);
},(result)=>{
ToastExample.show('JS界面:错误信息为:'+result,ToastExample.SHORT);
});
}}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
}
});
网友评论