Android App巧妙集成React Native最详教程 React Native 实现热部署、增量热更新

  Android App巧妙集成React Native最详教程

  React Native 实现热部署、增量热更新


本篇内容同样和React Native 与 原生App有关,可以说更加深入了两者之间的感情,为培养下一代做出准备:React Native与原生App的通信交互。

Android系统为我们提供了webview来加载网页,为了让webview加载的网页可以与App交互,系统提供了一套机制帮助我们更方便的实现通信。同样为了实现React Native与原生App之间的通信,FB也实现了自己的一套交互机制。

(1)RCTDeviceEventEmitter 事件方式

(2)Callback 回调方式

(3)Promise

(4)直传常量数据(原生向RN)

三种方式各具不同优缺点

1. RCTDeviceEventEmitter

   优点:可任意时刻传递,Native主导控制。

2. Callback

   优点:JS调用,Native返回。

   缺点:CallBack为异步操作,返回时机不确定

3. Promise

   优点:JS调用,Native返回。

   缺点:每次使用需要JS调用一次

了解了三者的通信方式,怎么能少了代码的描述!我们来看看代码如何实现。大致的实现步骤如下:

(1)定义Module类,继承ReactContextBaseJavaModule

         在Module类中,我们定义交互的方法,例如RN调用Native的方法,Native调用RN的方法等。

(2)定义Package类,继承ReactPackage 

         实现Package的createNativeModules方法,将Module实例添加到集合。

(3)定义Application,继承ReactApplication

         实现getPackages方法将Package实例添加到getPackages下的集合。

4. 直传常量数据(原生向RN)

         跨域传值,只能从原生端向RN端传递。RN端可通过 NativeModules.[module名].[参数名] 的方式获取

1.Module类中的核心代码

[java]  view plain  copy
  1. /** 
  2.  * 在rn代码里面是需要这个名字来调用该类的方法 
  3.  * @return 
  4.  */  
  5. @Override  
  6. public String getName() {  
  7.     return MODULE_NAME;  
  8. }  

 名称可以自定义,对接时协商好即可。

[java]  view plain  copy
  1. /** 
  2.  * RN调用Native的方法 
  3.  * @param phone 
  4.  */  
  5. @ReactMethod  
  6. public void rnCallNative(String phone) {  
  7.   
  8.     // 跳转到打电话界面  
  9.     Intent intent = new Intent();  
  10.     intent.setAction(Intent.ACTION_CALL);  
  11.     intent.setData(Uri.parse("tel:" + phone));  
  12.     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 跳转需要添加flag, 否则报错  
  13.     mContext.startActivity(intent);  
  14. }  

  在module中定义一个方法,并用@ReactMethod 注解标注:表明该方法会被RN调用。即被RN调用的原生方法必须使用@ReactMethod注解标注。

  注意:RN层调用Native层进行界面跳转时,需要设置FLAG_ACTIVITY_NEW_TASK标志,否则会出现如下错误:

  

[java]  view plain  copy
  1. /** 
  2.  * Native调用RN 
  3.  * @param msg 
  4.  */  
  5. public void nativeCallRn(String msg) {  
  6.     mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)  
  7.             .emit(EVENT_NAME,msg);  
  8. }  

  上面代码定义了原生方法,通过在Android层调用RN层。使用ReactContext的getJSModule方法,emit来发送消息。同样,emit的第一个参数要与RN层中addListener方法的第一个参数相同。

2.自定义Package的核心代码

[java]  view plain  copy
  1. /** 
  2.  * 通信Package类 
  3.  * Created by Song on 2017/2/17. 
  4.  */  
  5. public class CommPackage implements ReactPackage {  
  6.   
  7.     public CommModule mModule;  
  8.   
  9.     /** 
  10.      * 创建Native Module 
  11.      * @param reactContext 
  12.      * @return 
  13.      */  
  14.     @Override  
  15.     public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {  
  16.         List<NativeModule> modules = new ArrayList<>();  
  17.         mModule = new CommModule(reactContext);  
  18.         modules.add(mModule);  
  19.         return modules;  
  20.     }  
  21.   
  22.     @Override  
  23.     public List<Class<? extends JavaScriptModule>> createJSModules() {  
  24.         return Collections.emptyList();  
  25.     }  
  26.   
  27.     @Override  
  28.     public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {  
  29.         return Collections.emptyList();  
  30.     }  
  31. }  

 在createNativeModules方法中,初始化集合,并将module实例添加进集合,返回集合实例。

3.Application核心代码

[java]  view plain  copy
  1.  private static final CommPackage mCommPackage = new CommPackage();  
[java]  view plain  copy
  1. /** 
  2.   * 获取 reactPackage 
  3.   * @return 
  4.   */  
  5.  public static CommPackage getReactPackage() {  
  6.      return mCommPackage;  
  7.  }  

在getPackages方法中,将Package实例添加到Arrays中即可完成注册。以上就是Android层核心代码配置,继续来看React Native层核心代码:

1.调用原生代码

[java]  view plain  copy
  1. /** 
  2.  * 调用原生代码 
  3.  */  
  4.  skipNativeCall() {  
  5.     let phone = '18637070949';  
  6.     NativeModules.commModule.rnCallNative(phone);  
  7.  }  

在React Native层,通过NativeModules调用commModule,继而调用原生方法即可。注意:commModule要与Module类的getNames方法返回的名称对应。

2. 接收原生调用

[java]  view plain  copy
  1. /** 
  2.  * 接收原生调用 
  3.  */  
  4. componentDidMount() {  
  5.     DeviceEventEmitter.addListener('nativeCallRn',(msg)=>{  
  6.          title = "React Native界面,收到数据:" + msg;  
  7.          ToastAndroid.show("发送成功", ToastAndroid.SHORT);  
  8.     })  
  9. }  

通过DeviceEventEmitter注册监听,类似于Android中的监听事件。第一个参数标识名称,要与Module中emit的Event Name相同。第二个参数即为处理回掉。

3.界面代码

[html]  view plain  copy
  1. render() {  
  2.    return (  
  3.      <View style={styles.container}>  
  4.        <Text style={styles.welcome} >  
  5.            {title}  
  6.        </Text>  
  7.         <Text style={styles.welcome} onPress={this.skipNativeCall.bind(this)}>  
  8.            跳转到拨号界面  
  9.         </Text>  
  10.          
  11.         <Image source={require('./images/ic.png')} />  
  12.      </View>  
  13.    );  
  14.  }  

在Text中注册单击事件,RN层调用原生代码,跳转到拨号界面。

4.Android层调用RN的代码

[java]  view plain  copy
  1. /** 
  2.  * 向RN发送消息 
  3.  * @param v 
  4.  */  
  5. public void sendMsgToRN(View v) {  
  6.     Log.e("---","sendMsgToRN");  
  7.     MainApplication.getReactPackage().mModule.nativeCallRn("hello");  
  8. }  

调用Module中定义的nativeCallRn方法,继而出发RN层代码。以上就是通过 RCTDeviceEventEmitter 模式进行通信交互。可以很清晰的看出,交互都是以主动方式为主。

RN中剩下的两种通信方式,存在一个共同的特点:

从RN层调用Native层,Native层处理完成后,回调RN层

直接看代码实现:

(1)Callback

同样还是在Module类中定义交互方法:

[java]  view plain  copy
  1. /** 
  2.  * Callback 方式 
  3.  * rn调用Native,并获取返回值 
  4.  * @param msg 
  5.  * @param callback 
  6.  */  
  7. @ReactMethod  
  8. public void rnCallNativeFromCallback(String msg, Callback callback) {  
  9.   
  10.     // 1.处理业务逻辑...  
  11.     String result = "处理结果:" + msg;  
  12.     // 2.回调RN,即将处理结果返回给RN  
  13.     callback.invoke(result);  
  14. }  

RN中定义回调:

[java]  view plain  copy
  1. /** 
  2.  * Callback 通信方式 
  3.  */  
  4.  callbackComm(msg) {  
  5.      NativeModules.commModule.rnCallNativeFromCallback(msg,(result) => {  
  6.           ToastAndroid.show("CallBack收到消息:" + result, ToastAndroid.SHORT);  
  7.      })  
  8.  }  

(2)Promise

Module类中定义交互方法:

[java]  view plain  copy
  1. /** 
  2.  * Promise 方式 
  3.  * @param msg 
  4.  * @param promise 
  5.  */  
  6. @ReactMethod  
  7. public void rnCallNativeFromPromise(String msg, Promise promise) {  
  8.   
  9.     Log.e("---","adasdasda");  
  10.     // 1.处理业务逻辑...  
  11.     String result = "处理结果:" + msg;  
  12.     // 2.回调RN,即将处理结果返回给RN  
  13.     promise.resolve(result);  
  14. }  

 RN中定义回调:

[java]  view plain  copy
  1. /** 
  2.  * Promise 通信方式 
  3.  */  
  4. promiseComm(msg) {  
  5.     NativeModules.commModule.rnCallNativeFromPromise(msg).then(  
  6.         (result) =>{  
  7.             ToastAndroid.show("Promise收到消息:" + result, ToastAndroid.SHORT)  
  8.         }  
  9.     ).catch((error) =>{console.log(error)});  
  10. }  

布局中触发单击事件:

[html]  view plain  copy
  1. <Text style={styles.welcome} onPress={this.skipNativeCall.bind(this)}>  
  2.            跳转到拨号界面  
  3.         </Text>  
  4.         <Text style={styles.welcome} onPress={this.callbackComm.bind(this,'callback发送啦')}>  
  5.            Callback通信方式  
  6.         </Text>  
  7.         <Text style={styles.welcome} onPress={this.promiseComm.bind(this,'promise发送啦')}>  
  8.            Promise通信方式  
  9.         </Text>  

(3)直传常量数据(原生向RN)

自定义Module类中实现getConstants方法

[java]  view plain  copy
  1. @Nullable  
  2. @Override  
  3. public Map<String, Object> getConstants() {  
  4.     return super.getConstants();  
  5. }  
以上是默认实现,getConstants方法中调用了super.getConstants(),跟踪源码可以看到

[java]  view plain  copy
  1. /** 
  2.  * @return a map of constants this module exports to JS. Supports JSON types. 
  3.  */  
  4. public @Nullable Map<String, Object> getConstants() {  
  5.   return null;  
  6. }  
从源码注释中可以看出,该方法是返回一个Map类型的常量,导出到JS端(即RN)。支持JSON 类型。所以,我们只需要重写方法,声明Map集合,向其中添加常量后,返回即可。所以就有了如下代码

[java]  view plain  copy
  1. @Nullable  
  2. @Override  
  3. public Map<String, Object> getConstants() {  
  4.     Map<String,Object> params = new HashMap<>();  
  5.     params.put("Constant","我是常量,传递给RN");  
  6.     return params;  
  7. }  
此时,在RN端,我们可以通过NativeModules来接收即可

[java]  view plain  copy
  1. componentWillMount() {  
  2.     let result = NativeModules.MyModule.Constant  
  3. }  



最终效果:

  Android调用React Native:

  

  React Native调用Android:

  

  

  源码以上传到github,希望大家star,follow支持哦~

  源码下载

转载https://blog.csdn.net/u013718120/article/details/55506238

猜你喜欢

转载自blog.csdn.net/sinat_17775997/article/details/80585513
今日推荐