React Native 調用 iOS / Android (Toast) 原生模塊學習筆記

參考官方文檔初始化一個react-native項目

初始化項目

react-native init androidToast

生成如下目錄:

運行命令查看項目

react-native run-android

如圖:

接入Android原生模塊

按照官方的說法,第一步需要創建一個java類 本例中為:ToastModule ,并繼承 ReactContextBaseJavaModule ,然后復寫 getName() 方法,其返回值,就是在 react-native 中引用的 組件名稱

復寫 getConstants() 方法可以返回一些 常量 用于react-native中調用,官方文檔中 return "ToastAndroid" 這個名稱在原生的組件中已經存在,返回相同的名稱將會沖突,so:改個名字吧!!

<p>@ReactMethod 注解:用于java返回一個 react-native 中可調用的 方法 ,其不能有返回值所以使用 void</p>

注冊模塊:創建java類 本例中為:ExampleReactPackage ,實現 ReactPackage 接口

復寫createJSModules() , createViewManagers() 方法,返回 Collections.emptyList() 空集合

createNativeModules() 方法中添加我們需注冊的模塊對象, new ToastModule() ,并返回模塊集合

添加已注冊模塊對象到返回集合中,向react-native拋出模塊,如:第三步

在react-native中調用,如:第四步

android目錄結構

注意:引入包的名稱不要弄錯了

Java React-native基本類型對照

Java RN
Boolean Bool
Integer Number
Double Number
Float Number
String String
Callback function
ReadableMap Object
ReadableArray Array

第一步 創建模塊類

在androidtoast目錄下,創建一個ToastModule.java的類

package com.androidtoast; //包名

import android.widget.Toast; //引入調用的類

import com.非死book.react.bridge.ReactApplicationContext; import com.非死book.react.bridge.ReactContextBaseJavaModule; import com.非死book.react.bridge.ReactMethod; import com.非死book.react.uimanager.IllegalViewOperationException;

import java.util.Map; import java.util.HashMap;

public class ToastModule extends ReactContextBaseJavaModule {

private static final String DURATION_SHORT_KEY = "SHORT";
private static final String DURATION_LONG_KEY = "LONG";

public ToastModule(ReactApplicationContext reactContext) {
    super(reactContext);
}

// 復寫方法,返回react-native中調用的 組件名
@Override
public String getName() {
    return "ToastNative";
}
// 復寫方法,返回常量
@Override
public Map<String, Object> getConstants() {
    final Map<String, Object> constants = new HashMap<>();
    constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
    constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
    return constants;
}
// 使用 @ReactMethod注解返回react-native中可調用的 方法
@ReactMethod
public void show(String message, int duration) {
    Toast.makeText(getReactApplicationContext(), message, duration).show();
}

}</code></pre>

第二步 注冊模塊

在androidtoast目錄下,創建一個ExampleReactPackage.java的類

package com.androidtoast;

import android.widget.Toast;

import com.非死book.react.bridge.NativeModule; import com.非死book.react.bridge.ReactApplicationContext; import com.非死book.react.bridge.ReactMethod; import com.非死book.react.ReactPackage; import com.非死book.react.bridge.JavaScriptModule; import com.非死book.react.uimanager.ViewManager;

import java.util.ArrayList; import java.util.Collections; import java.util.List;

public class ExampleReactPackage implements ReactPackage {

@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Collections.emptyList();
}

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

}</code></pre>

第三步 添加注冊類

添加到 MainApplication.java 中的 getPackages() 方法中

@Override
protected List<ReactPackage> getPackages() {
  return Arrays.<ReactPackage>asList(
      new MainReactPackage(), // 這個是自動創建
      new ExampleReactPackage() // 這個類是我們創建的
  );
}

項目結構如下:

Java部分的代碼就結束了,再次提醒下:包名啊!!不要弄錯了!!!

第四步 修改react-native代碼引入原生模塊

修改index.android.js

  • 引入react-native所需模塊 NativeModules
  • 獲取導出組件 NativeModules.ToastNative
  • 調用方法 show()

修改了下index.android.js文件,代碼如下:

/**

import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View, TouchableOpacity, NativeModules } from 'react-native';

let toast = NativeModules.ToastNative;

export default class androidToast extends Component { render() { return ( <View style={styles.container}> <Text style={styles.title}>react-native 調用android原生模塊</Text> <TouchableOpacity onPress={()=>{ toast.show('Toast message',toast.SHORT); }}> <Text style={styles.btn}>Click Me</Text> </TouchableOpacity> </View> ); } }

const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, title:{ fontSize:16, }, btn:{ fontSize:16, paddingVertical:7, paddingHorizontal:10, borderColor:'#f00', borderWidth:1, borderRadius:5, marginTop:10, color:'#f00' } });

AppRegistry.registerComponent('androidToast', () => androidToast);</code></pre>

運行程序

react-native run-android

效果如下:

react-native回調函數

*java中提供了一個 Callback 的數據類型對應了react-native中的 function *

具體操作就是在@ReactMethod注解的返回函數中 添加 類型 為 Callback 的參數,并通過 invoke(...params) 調用

RN中通過調用show方法時提供對應的回調函數就可以了, :smile:

  • 修改 ToastModule.java 代碼中 show() 方法,添加回調

注意引包!! import com.非死book.react.bridge.Callback;

// 說明下:count,flag是我自定義的變量

@ReactMethod public void show(String message, int duration ,Callback successCallback, Callback errorCallback) { Toast.makeText(getReactApplicationContext(), message, duration).show(); // 通過invoke調用,隨便你傳參 if(flag) successCallback.invoke("success", ++count); else errorCallback.invoke("error", ++count); flag = !flag; }</code></pre>

  • 修改 index.android.js 中調用函數
<TouchableOpacity onPress={()=>{
  toast.show('Toast message',toast.SHORT,(message,count)=>{console.log("==",message,count)},(message,count)=>{console.log("++",message,count)});
}}>

:ok_hand: ,試試看吧~~

觸發事件

首先我們定義一個發送事件的方法

private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params){
    reactContext
    .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
    .emit(eventName, params);
}

引包

import javax.annotation.Nullable;

import com.非死book.react.bridge.Arguments; import com.非死book.react.bridge.WritableMap; import com.非死book.react.bridge.ReactContext;

import com.非死book.react.modules.core.DeviceEventManagerModule;</code></pre>

繼續改造 show 方法,添加參數,并調用預先定義的方法

// 靜態方法
WritableMap map = Arguments.createMap();
map.putBoolean("boolean",true);
map.putDouble("double",0.003);
map.putString("string","string");
sendEvent(this.reactContext, "eventName",map);

改造 index.android.js 啦 ,添加事件監聽,這里的 eventName 就是我們 sendEvent 中定義的事件名稱

componentWillMount(){
  DeviceEventEmitter.addListener('eventName',(e)=>{
    console.log(e)
  });
}

效果如下:

接入IOS原生模塊

IOS 日歷模塊

創建一個名為 CalendarManager.h 的OC的接口 interface 文件,以及一個 CalendarManager.m 的實現類 implementation

CalendarManager.h 需要繼承 NSObject 并實現 RCTBridgeModule 接口

CalendarManager.m 需要添加 RCT_EXPORT_MODULE() 宏,導出的方法需要通過 RCT_EXPORT_METHOD() 宏來實現

react-native中通過 NativeModules.類名.方法 調用 (本例中為:NativeModules.CalendarManager 獲取iOS拋出模塊,并通過模塊調用拋出方法)

IOS React-native 類型對照

IOS React-native
NSString string
NSInteger, float, double, CGFloat, NSNumber number
BOOL, NSNumber boolean
NSArray array
NSDictionary object
RCTResponseSenderBlock function

除此以外,任何RCTConvert類支持的的類型也都可以使用(參見RCTConvert了解更多信息)。RCTConvert還提供了一系列輔助函數,用來接收一個JSON值并轉換到原生Objective-C類型或類

創建IOS模塊

  • CalendarManager.h
// CalendarManager.h

import "RCTBridgeModule.h"

@interface CalendarManager : NSObject <RCTBridgeModule> @end</code></pre>

  • CalendarManager.m
// CalendarManager.m
@implementation CalendarManager

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(addEvent:(NSString )name location:(NSString )location) { RCTLogInfo(@"Pretending to create an event %@ at %@", name, location); }

@end</code></pre>

react-native 調用

import { NativeModules } from 'react-native';
let CalendarManager = NativeModules.CalendarManager;
CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');

調用效果

  • 官方文檔中還給出了一些類型轉換和詞典運用的例子,請自行查看!

回調函數

通過 RCTResponseSenderBlock 聲明回調函數類型,但RCTResponseSenderBlock只接受一個參數——傳遞給JavaScript回調函數的參數數組

RCT_EXPORT_METHOD(addEvents:(RCTResponseSenderBlock)callback)
{
  NSString *message = @"callback message!!!";
  callback(@[[NSNull null], message]);
}
CalendarManager.addEvents((error, message) => {
  if (error) {
    console.error(error);
  } else {
    console.log("message:",message)
  }
})

導出常量

- (NSDictionary *)constantsToExport
{
  return @{ @"YEAR": @"2016" };
}
CalendarManager.YEAR

發送事件

#import "RCTBridge.h"

import "RCTEventDispatcher.h"

[self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name": @"xing.he"}];}</code></pre>

import { NativeAppEventEmitter } from 'react-native';

var subscription = NativeAppEventEmitter.addListener( 'EventReminder', (reminder) => console.log(reminder.name) );

// 千萬不要忘記忘記取消訂閱, 通常在componentWillUnmount函數中實現。 subscription.remove();</code></pre>

曾走過的路

曾想在返回的方法中定義一個 Object 類型的變量,但pa! 報錯了!!不支持滴,請查看類型對應表格

cloudn't find argument class : Object

參照官方文檔時,各種類找不到,瞬間醉了!

# 百度吧,一般不管用

stackoverflow,Google 有時可以搜到,尼瑪!英文。。。

github

react-native 源碼 !!!這里面有個ReactAndroid的目錄就是各種Java類啦

react-native/ReactAndroid/src/main/java/com/非死book/

https://github.com/非死book/react-native</code></pre>

RCTBridgeModule.h file not found

npm install

npm WARN jest-react-native@17.0.0 requires a peer of whatwg-fetch@^1.0.0 but none was installed

npm install whatwg-fetch@^1.0.0

implicit declaration of function'RCTLogInfo' in invalid

#import "RCTLog.h"

http://bbs.reactnative.cn/topic/1429/rctloginfo-%E6%8A%A5%E9%94%99%E7%9A%84%E9%97%AE%E9%A2%98/2</code></pre>

參考文檔

江清清 ModulesDemo

react-native Android 中文

react-native Android 官方英文

react-native IOS 中文

react-native IOS 官方英文

RN-Resource-ipk github

 

來自:https://github.com/Xing-He/react-native-nativeModule/blob/master/README.md

 

 本文由用戶 WanVillarre 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!