React Native 原生交互(調(diào)用原生代碼,數(shù)據(jù)交互)

React Native 與android原生交互

有時(shí)候App需要訪(fǎng)問(wèn)平臺(tái)API,但React Native可能還沒(méi)有相應(yīng)的模塊包裝;或者你需要復(fù)用一些Java代碼,而不是用Javascript重新實(shí)現(xiàn)一遍;又或者你需要實(shí)現(xiàn)某些高性能的、多線(xiàn)程的代碼,譬如圖片處理、數(shù)據(jù)庫(kù)、或者各種高級(jí)擴(kuò)展等等。

React Native設(shè)計(jì)為可以在其基礎(chǔ)上編寫(xiě)真正的原生代碼,并且可以訪(fǎng)問(wèn)平臺(tái)所有的能力。這是一個(gè)相對(duì)高級(jí)的特性,React Native并不認(rèn)為它應(yīng)當(dāng)在日常開(kāi)發(fā)的過(guò)程中經(jīng)常出現(xiàn),但具備這樣的能力是很重要的。如果React Native還不支持某個(gè)你需要的原生特性,你應(yīng)當(dāng)可以自己實(shí)現(xiàn)該特性的封裝。

Toast模塊

本向?qū)?huì)用Toast作為例子。假設(shè)我們希望可以從Javascript發(fā)起一個(gè)Toast消息(Android中的一種會(huì)在屏幕下方彈出、保持一段時(shí)間的消息通知)

我們首先來(lái)創(chuàng)建一個(gè)原生模塊。一個(gè)原生模塊是一個(gè)繼承了ReactContextBaseJavaModule的Java類(lèi),它可以實(shí)現(xiàn)一些JavaScript所需的功能。我們這里的目標(biāo)是可以在JavaScript里寫(xiě)ToastExample.show('Awesome', ToastExample.SHORT);,來(lái)調(diào)起一個(gè)Toast通知。

ReactContextBaseJavaModule要求派生類(lèi)實(shí)現(xiàn)getName方法。這個(gè)函數(shù)用于返回一個(gè)字符串名字,這個(gè)名字在JavaScript端標(biāo)記這個(gè)模塊。這里我們把這個(gè)模塊叫做ToastExample,這樣就可以在JavaScript中通過(guò)React.NativeModules.ToastExample訪(fǎng)問(wèn)到這個(gè)模塊。譯注:RN已經(jīng)內(nèi)置了一個(gè)名為T(mén)oastAndroid的模塊,所以在練習(xí)時(shí)請(qǐng)勿使用ToastAndroid的名字,否則運(yùn)行時(shí)會(huì)報(bào)錯(cuò)名字沖突!

  @Override
  public String getName() {
    return "ToastExample";
  }

要導(dǎo)出一個(gè)方法給JavaScript使用,Java方法需要使用注解@ReactMethod。方法的返回類(lèi)型必須為void。React Native的跨語(yǔ)言訪(fǎng)問(wèn)是異步進(jìn)行的,所以想要給JavaScript返回一個(gè)值的唯一辦法是使用回調(diào)函數(shù)或者發(fā)送事件(參見(jiàn)下文的描述)。

    @ReactMethod
    public void show(String message) {
        Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_LONG).show();
    }

參數(shù)類(lèi)型

Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array

注冊(cè)模塊

在Java這邊要做的最后一件事就是注冊(cè)這個(gè)模塊。我們需要在應(yīng)用的Package類(lèi)的createNativeModules方法中添加這個(gè)模塊。如果模塊沒(méi)有被注冊(cè),它也無(wú)法在JavaScript中被訪(fǎng)問(wèn)到。

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;
    }

}

這個(gè)package需要在MainApplication.java文件的getPackages方法中提供。這個(gè)文件位于你的react-native應(yīng)用文件夾的android目錄中。具體路徑是: android/app/src/main/java/com/your-app-name/MainApplication.java.

protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new AnExampleReactPackage()); // <-- 添加這一行,類(lèi)名替換成你的Package類(lèi)的名字.
}

完整代碼

  • ReactContextBaseJavaModule 創(chuàng)建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調(diào)用 原生土司
    @ReactMethod
    public void show(String message) {
        Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_LONG).show();
    }

    //React Naitve  回傳數(shù)據(jù)
    @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());
        }
    }
    //主動(dòng)發(fā)送數(shù)據(jù)
    public void sendEvent(String eventName, int status) {
        getReactApplicationContext()
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, status);
    }
}
  • ReactPackage 注冊(cè)模塊
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);
    }
}

代碼調(diào)用

/**
 * 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 {
    //注冊(cè)監(jiān)聽(tīng)
    componentDidMount() {
        this.subscription = DeviceEventEmitter.addListener('noticeName', function (msg) {
            alert("接收到安卓端主動(dòng)發(fā)送的數(shù)據(jù)" + msg)
        });
    }

    componentWillUnmount() {
        this.subscription.remove();
    }

    render() {
        return <View>
            <Button
                onPress={() => {
                    this.measureLayout();
                    this.showToast();
                }
                }
                title="登陸"
            />
        </View>
    }

    //調(diào)用安卓原生代碼并回傳數(shù)據(jù)
    measureLayout() {
        NativeModules.ToastExample.measureLayout(100, 100, (msg) => {
                console.log(msg);
            }, (x, y, width, height) => {
                console.log(x + ':' + y + ':' + width + ':' + height);
            }
        );
    }

    //調(diào)用原生安卓土司
    showToast() {
        NativeModules.ToastExample.show("土司")
    }



}



?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容