ReactNative学习笔记之调用原生模块(进阶)之CallbackPromise使用

Posted mictoy_朱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReactNative学习笔记之调用原生模块(进阶)之CallbackPromise使用相关的知识,希望对你有一定的参考价值。

前言

前文ReactNative学习笔记——调用原生模块(Android)简单说了下ReactNative调用android原生模块的基本用法,下面讲解下调用原生模块经常会用到的CallbackPromise
很多时候我们可能不仅仅是调用Native的方法,还要通过原生方法获取它的返回值,但是,前面说过,要导出一个方法给javascript使用,Java方法的返回类型必须为void,React Native的跨语言访问是异步进行的,我们无法像Java中常规方法一样直接通过return方式获取返回值。想要给JavaScript返回一个值的唯一方法是使用回调函数(Callback)或者发送事件(Promise

1.使用回调函数Callback

这里首先讲解CallBack的使用方法,它就是提供了一个回调函数来把返回值回传给JavaScript。
比如,我们通过原生模块获取手机型号:

package com.getnativemodule;
import android.os.Build;
import com.facebook.react.bridge.Callback;
...

public class SplashScreenModule extends ReactContextBaseJavaModule 

    public SplashScreenModule(ReactApplicationContext reactContext) 
        super(reactContext);
    

    ...

    /**
     * 获取手机型号
     */
    @ReactMethod
    public void getSystemModel(Callback result)
        String sysModel= Build.MODEL;
        result.invoke(sysModel);
    

这里,我们将CallBack作为参数传入方法中,然后调用invoke()方法,并将我们最终需要获取的返回值传入invoke方法的参数列表中(比如这里的sysModel),这样,我们就可以在RN的js中获取该返回值。
我们看下Callback的源码:

package com.facebook.react.bridge;

public interface Callback 

  /**
   * Schedule javascript function execution represented by this @link Callback instance
   *
   * @param args arguments passed to javascript callback method via bridge
   */
  public void invoke(Object... args);

就是一个接口,里面就一个invoke方法,可以说,这就是一个约定俗成的规则,没啥好研究的。

接下来,在RN中调用获取该返回值,写法如下:

...
import SplashScreen from "./jscode/SplashScreen";

export default class App extends Component<> 

    constructor(props)
        super(props);
        this.state=
            systemModel:'',
        
    
    ...

    componentDidMount() 
        ...
        SplashScreen.getSystemModel((sysModel)=>
            this.setState(systemModel:sysModel);
        );
    

    render() 
        return (
            <View style=styles.container>
                ...
                <Text style=styles.welcome>
                    this.state.systemModel
                </Text>
            </View>
        );
    

之前忘了把SplashScreen.js代码添加进来,现在补上(也就两行代码)

import NativeModules from 'react-native'
export default NativeModules.SplashScreen;

在前面一文我们知道,Callback映射到JavaScript中是function,在ES6中使用箭头函数就如上面写法,这里的sysModel就是我们最终要获取的返回值。
我们运行查看效果:

当然,我们还可以传多个参数,比如,我们还要获取系统版本号,在Java中写法如下:

@ReactMethod
    public void getSystemModel(Callback result)
        String sysModel= Build.MODEL;
        String sysVersion=Build.VERSION.RELEASE;//获取系统版本号
        result.invoke(sysModel,sysVersion);     //将值传入invoke
    

前面我们知道,invoke方法的参数是可变长度参数,所以你可以传任意多个参数值。
相应的,我们在JavaScript中修改代码如下:

...
import SplashScreen from "./jscode/SplashScreen";

export default class App extends Component<> 

    constructor(props)
        super(props);
        this.state=
            systemModel:'',
            systemVersion:'',
        
    
    ...

    componentDidMount() 
        ...
        SplashScreen.getSystemModel((sysModel,sysVersion)=>//增加传参sysVersion
            this.setState(systemModel:sysModel,systemVersion:sysVersion);
        );
    

    render() 
        return (
            <View style=styles.container>
                ...
                <Text style=styles.welcome>
                    this.state.systemModel
                </Text>
                <Text style=styles.welcome>
                    this.state.systemVersion
                </Text>
            </View>
        );
    

运行查看效果:

由此我们知道,JavaScript中传入的参数就是对应Java中invoke传入的参数。

2.使用Promise机制

原生模块还可以使用promise来简化代码,搭配ES2016(ES7)标准的async/await语法则效果更佳。如果桥接原生方法的最后一个参数是一个Promise,则对应的JS方法就会返回一个Promise对象。
它同样可以实现Callback的功能,我们先简单介绍一下Promise。
Promise是ES6中提供给原生的一个对象,它可以传递异步操作的消息,代表了某个未来才会知道结果的事件。

Promise有两个特点
  1. Promise对象的状态不会受到外界操作的影响

    Promise有三种状态:Pending、Resolved和 Rejected,只有等异步操作结束,得到操作结果后,由Promise根据结果决定自身是什么状态。

  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果

    Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。

Promise的三种状态

  • pending:进行时,无法知道处理进度,只知道目前正在处理异步操作;
  • resolve:已完成,表示异步操作正确返回结果回调方法;
  • reject:已失败,表示处理中途抛出异常,或者处理结果不正确的回调方法。

关于Promise还有很多内容,这里不做详解,下面我们就用Promise机制来实现刚刚Callback实现的功能。

首先在Java中实现使用PromiseReactMethod方法:

...
    @ReactMethod
    public void getSystemModelByPromise(Promise promise)
        String sysModel= Build.MODEL;
        promise.resolve(sysModel);
    

    @ReactMethod
    public void getSystemVersionByPromise(Promise promise)
        String sysVersion=Build.VERSION.RELEASE;
        promise.resolve(sysVersion);
    

对应在JavaScript中实现方法如下:

...
import SplashScreen from "./jscode/SplashScreen";

export default class App extends Component<> 

    constructor(props)
        super(props);
        this.state=
            systemModel:'',
            systemVersion:'',
        
    
    ...

    async componentDidMount() //注意此时要声明为`async`表示该方法为异步方法
        ...
        this.setState(
            systemModel:await SplashScreen.getSystemModelByPromise(), //声明await表示等待异步操作结果
            systemVersion:await SplashScreen.getSystemVersionByPromise()
        );
    

    render() 
        return (
            <View style=styles.container>
                ...
                <Text style=styles.welcome>
                    this.state.systemModel
                </Text>
                <Text style=styles.welcome>
                    this.state.systemVersion
                </Text>
            </View>
        );
    

运行,同样可以得到我们想要的效果(效果图就不贴了)

通过上面可知,Promise异步操作结果一次只能传递一个值,而无法像Callback一样可以通过可变长度参数一次性传递多个值,必须写多个方法来获取。当然,我们也可以用封装的方式,将我们所需要的值封装成一个对象传给resolve方法。
当然,Promise的使用是为了简化Callback的层层回调,只是在这个例子中没有体现出来,可以说各有利弊。

Promise的缺点

这里也简单说下Promise的缺点:

  1. 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
  3. 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

好了,今天的文章先到这

参考文献
【React Native】Promise的简单使用
ReactNative中文网——原生模块

以上是关于ReactNative学习笔记之调用原生模块(进阶)之CallbackPromise使用的主要内容,如果未能解决你的问题,请参考以下文章

ReactNative进阶:ReactNative学习资料汇总

Python学习----第七模块笔记(Web开发进阶之Django数据库操作)

Android学习笔记进阶16之BitmapShader

React Native学习笔记:集成到现有原生应用

Flutter笔记-调用原生IOS高德地图sdk

ReactNative-JS 调用原生方法实例代码(转载)