非常教程

React native参考手册

指南(Android) | Guides (Android)

Native Modules

需要本机代码的项目

此页面仅适用于react-native init使用Create React Native App 制作的或使用此类应用程序弹出的项目。有关弹出的更多信息,请参阅创建React Native App存储库的指南。

有时候应用程序需要访问平台API,React Native目前还没有相应的模块。也许你想重用一些现有的Java代码,而不必用JavaScript重新实现它,或者编写一些高性能,多线程的代码,例如图像处理,数据库或任何数量的高级扩展。

我们设计了React Native,因此您可以编写真实的本机代码并访问平台的全部功能。这是一个更高级的功能,我们不希望它成为通常开发过程的一部分,但它存在必不可少。如果React Native不支持您需要的本地功能,您应该可以自己构建它。

启用Gradle

如果您打算对Java代码进行更改,我们建议启用Gradle Daemon以加快构建。

Toast模块

本指南将使用Toast示例。假设我们希望能够从JavaScript创建Toast消息。

我们首先创建一个本地模块。本地模块是一个Java类,通常扩展ReactContextBaseJavaModule该类并实现JavaScript所需的功能。我们的目标是能够使用ToastExample.show('Awesome', ToastExample.SHORT);JavaScript 编写在屏幕上显示简短的敬酒。

package com.facebook.react.modules.toast;

import android.widget.Toast;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

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

ReactContextBaseJavaModule要求实现一个被调用的方法getName。此方法的目的是NativeModule在JavaScript中返回表示该类的字符串名称。所以我们在这里调用这个,ToastExample以便我们可以通过React.NativeModules.ToastExampleJavaScript 访问它。

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

一个可选的方法调用getConstants返回暴露给JavaScript的常量值。它的实现不是必需的,但对于需要同步从JavaScript到Java进行通信的关键预定义值非常有用。

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

要向JavaScript公开方法,必须使用Java方法注释Java方法@ReactMethod。桥接方法的返回类型始终是void。React Native桥是异步的,因此将结果传递给JavaScript的唯一方法是使用回调或发射事件(请参见下文)。

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

参数类型

下面的参数类型支持注释为的方法,@ReactMethod并且它们直接映射到它们的JavaScript等价物

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

阅读有关ReadableMap和ReadableArray的更多信息

注册模块

Java中的最后一步是注册模块; 这发生在createNativeModules你的应用程序包中。如果一个模块没有注册,它将不能从JavaScript获得。

package com.facebook.react.modules.toast;

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

}

该包需要getPackagesMainApplication.java文件的方法中提供。该文件存在于react-native应用程序目录中的android文件夹下。这个文件的路径是:android/app/src/main/java/com/your-app-name/MainApplication.java

protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new AnExampleReactPackage()); // <-- Add this line with your package name.
}

为了更简单地从JavaScript访问您的新功能,通常会将本机模块包装到JavaScript模块中。这不是必须的,但是可以节省您的图书馆的消费者NativeModules每次都需要关闭它。这个JavaScript文件也成为您添加JavaScript端功能的好地方。

'use strict';
/**
 * This exposes the native ToastExample module as a JS module. This has a
 * function 'show' which takes the following parameters:
 *
 * 1. String message: A string with the text to toast
 * 2. int duration: The duration of the toast. May be ToastExample.SHORT or
 *    ToastExample.LONG
 */
import { NativeModules } from 'react-native';
module.exports = NativeModules.ToastExample;

现在,从您的其他JavaScript文件中,您可以调用像这样的方法:

import ToastExample from './ToastExample';

ToastExample.show('Awesome', ToastExample.SHORT);

Beyond Toasts

回调

本机模块还支持一种特殊的参数 - 回调。在大多数情况下,它用于将函数调用结果提供给JavaScript。

public class UIManagerModule extends ReactContextBaseJavaModule {

...

  @ReactMethod
  public void measureLayout(
      int tag,
      int ancestorTag,
      Callback errorCallback,
      Callback successCallback) {
    try {
      measureLayout(tag, ancestorTag, mMeasureBuffer);
      float relativeX = PixelUtil.toDIPFromPixel(mMeasureBuffer[0]);
      float relativeY = PixelUtil.toDIPFromPixel(mMeasureBuffer[1]);
      float width = PixelUtil.toDIPFromPixel(mMeasureBuffer[2]);
      float height = PixelUtil.toDIPFromPixel(mMeasureBuffer[3]);
      successCallback.invoke(relativeX, relativeY, width, height);
    } catch (IllegalViewOperationException e) {
      errorCallback.invoke(e.getMessage());
    }
  }

...

这个方法可以用JavaScript访问:

UIManager.measureLayout(
  100,
  100,
  (msg) => {
    console.log(msg);
  },
  (x, y, width, height) => {
    console.log(x + ':' + y + ':' + width + ':' + height);
  }
);

本地模块应该只调用一次回调。但是,它可以存储回调并稍后调用它。

强调在本地函数完成后不立即调用回调是非常重要的 - 记住,桥接通信是异步的,这也与运行循环有关。

承诺

原生模块也可以实现承诺,这可以简化您的代码,特别是在使用ES2016的async/await语法时。当桥接本机方法的最后一个参数是a时Promise,其相应的JS方法将返回一个JS Promise对象。

重构上述代码以使用promise而不是回调,如下所示:

import com.facebook.react.bridge.Promise;

public class UIManagerModule extends ReactContextBaseJavaModule {

...
  private static final String E_LAYOUT_ERROR = "E_LAYOUT_ERROR";
  @ReactMethod
  public void measureLayout(
      int tag,
      int ancestorTag,
      Promise promise) {
    try {
      measureLayout(tag, ancestorTag, mMeasureBuffer);

      WritableMap map = Arguments.createMap();

      map.putDouble("relativeX", PixelUtil.toDIPFromPixel(mMeasureBuffer[0]));
      map.putDouble("relativeY", PixelUtil.toDIPFromPixel(mMeasureBuffer[1]));
      map.putDouble("width", PixelUtil.toDIPFromPixel(mMeasureBuffer[2]));
      map.putDouble("height", PixelUtil.toDIPFromPixel(mMeasureBuffer[3]));

      promise.resolve(map);
    } catch (IllegalViewOperationException e) {
      promise.reject(E_LAYOUT_ERROR, e);
    }
  }

...

此方法的JavaScript对应方返回Promise。这意味着您可以使用await异步函数中的关键字来调用它并等待其结果:

async function measureLayout() {
  try {
    var {
      relativeX,
      relativeY,
      width,
      height,
    } = await UIManager.measureLayout(100, 100);

    console.log(relativeX + ':' + relativeY + ':' + width + ':' + height);
  } catch (e) {
    console.error(e);
  }
}

measureLayout();

思路

原生模块不应该对它们被调用的线程有任何假设,因为当前的任务将来可能会发生变化。如果需要阻塞调用,那么应该将繁重的工作分派给内部管理的工作者线程,并从那里分发任何回调。

发送事件到JavaScript

原生模块可以向JavaScript发送事件而不用直接调用。最简单的方法是使用RCTDeviceEventEmitter可以从ReactContext下面的代码片段中获得的。

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

然后,JavaScript模块可以通过addListenerOn使用Subscribablemixin 进行注册以接收事件。

import { DeviceEventEmitter } from 'react-native';
...

var ScrollResponderMixin = {
  mixins: [Subscribable.Mixin],


  componentWillMount: function() {
    ...
    this.addListenerOn(DeviceEventEmitter,
                       'keyboardWillShow',
                       this.scrollResponderKeyboardWillShow);
    ...
  },
  scrollResponderKeyboardWillShow:function(e: Event) {
    this.keyboardWillOpenTo = e;
    this.props.onKeyboardWillShow && this.props.onKeyboardWillShow(e);
  },

您也可以直接使用DeviceEventEmitter模块来监听事件。

...
componentWillMount: function() {
  DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) {
    // handle event.
  });
}
...

从中获取活动结果 startActivityForResult

onActivityResult如果您想从您开始的活动中获得结果,您需要听取意见startActivityForResult。要做到这一点,您必须扩展BaseActivityEventListener或实施ActivityEventListener。前者是首选,因为它对API更改具有更强的适应性。然后,你需要在模块的构造函数中注册监听器,

reactContext.addActivityEventListener(mActivityResultListener);

现在您可以onActivityResult通过执行以下方法来收听:

@Override
public void onActivityResult(
  final Activity activity,
  final int requestCode,
  final int resultCode,
  final Intent intent) {
  // Your logic here
}

我们将实现一个简单的图像选择器来演示这一点。图像选择器会将该方法暴露pickImage给JavaScript,它将在调用时返回图像的路径。

public class ImagePickerModule extends ReactContextBaseJavaModule {

  private static final int IMAGE_PICKER_REQUEST = 467081;
  private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
  private static final String E_PICKER_CANCELLED = "E_PICKER_CANCELLED";
  private static final String E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER";
  private static final String E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND";

  private Promise mPickerPromise;

  private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {

    @Override
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
      if (requestCode == IMAGE_PICKER_REQUEST) {
        if (mPickerPromise != null) {
          if (resultCode == Activity.RESULT_CANCELED) {
            mPickerPromise.reject(E_PICKER_CANCELLED, "Image picker was cancelled");
          } else if (resultCode == Activity.RESULT_OK) {
            Uri uri = intent.getData();

            if (uri == null) {
              mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "No image data found");
            } else {
              mPickerPromise.resolve(uri.toString());
            }
          }

          mPickerPromise = null;
        }
      }
    }
  };

  public ImagePickerModule(ReactApplicationContext reactContext) {
    super(reactContext);

    // Add the listener for `onActivityResult`
    reactContext.addActivityEventListener(mActivityEventListener);
  }

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

  @ReactMethod
  public void pickImage(final Promise promise) {
    Activity currentActivity = getCurrentActivity();

    if (currentActivity == null) {
      promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
      return;
    }

    // Store the promise to resolve/reject when picker returns data
    mPickerPromise = promise;

    try {
      final Intent galleryIntent = new Intent(Intent.ACTION_PICK);

      galleryIntent.setType("image/*");

      final Intent chooserIntent = Intent.createChooser(galleryIntent, "Pick an image");

      currentActivity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST);
    } catch (Exception e) {
      mPickerPromise.reject(E_FAILED_TO_SHOW_PICKER, e);
      mPickerPromise = null;
    }
  }
}

监听LifeCycle事件

监听活动的LifeCycle事件(例如onResumeonPause等等)与我们的实施方式非常相似ActivityEventListener。该模块必须执行LifecycleEventListener。然后,你需要在模块的构造函数中注册一个监听器,

reactContext.addLifecycleEventListener(this);

现在您可以通过实施以下方法来收听活动的LifeCycle事件:

@Override
public void onHostResume() {
    // Activity `onResume`
}

@Override
public void onHostPause() {
    // Activity `onPause`
}

@Override
public void onHostDestroy() {
    // Activity `onDestroy`
}
React native

React Native 是一个 JavaScript 的框架,用来撰写实时的、可原生呈现 iOS 和 Android 的应用。

主页 https://facebook.github.io/react-native/
源码 https://github.com/facebook/react-native
发布版本 0.49

React native目录

1.开始 | Getting Started
2.指南 | Guides
3.APIs
4.组件:ActivityIndicator | Components: ActivityIndicator
5.组件:按钮 | Components: Button
6.组件:CheckBox | Components: CheckBox
7.组件:DatePickerIOS | Components: DatePickerIOS
8.组件:DrawerLayoutAndroid | Components: DrawerLayoutAndroid
9.组件:FlatList | Components: FlatList
10.组件:图像 | Components: Image
11.组件:KeyboardAvoidingView | Components: KeyboardAvoidingView
12.Components: ListView
13.Components: MaskedViewIOS
14.Components: Modal
15.Components: NavigatorIOS
16.Components: Picker
17.Components: PickerIOS
18.Components: ProgressBarAndroid
19.Components: ProgressViewIOS
20.Components: RefreshControl
21.Components: ScrollView
22.Components: SectionList
23.Components: SegmentedControlIOS
24.Components: Slider
25.Components: SnapshotViewIOS
26.Components: StatusBar
27.Components: Switch
28.Components: TabBarIOS
29.Components: TabBarIOS.Item
30.Components: Text
31.Components: TextInput
32.Components: ToolbarAndroid
33.Components: TouchableHighlight
34.Components: TouchableNativeFeedback
35.Components: TouchableOpacity
36.Components: TouchableWithoutFeedback
37.Components: View
38.Components: ViewPagerAndroid
39.Components: VirtualizedList
40.Components: WebView
41.创建 | Contributing
42.指南(Android) | Guides (Android)
43.指南(IOS) | Guides (iOS)
44.其他 | Miscellaneous