Я пытаюсь (буквально) преодолеть разрыв между JavaScript (React Native 0.61.5) и C ++ для Android, чтобы использовать OpenCV (3.2.0). Примечательно, что я использую устаревший NDK (16), потому что в противном случае я не могу использовать gnustl_static
.
Проблема
Всякий раз, когда я пытаюсь вернуть String
из моего родного C ++ код на Java, приложение вылетает на собственной стороне. Однако возврат простых типов данных (в моем примере int
) не является проблемой.
Dev Environment
- macOS Catalina Version
10.15.4 (19E266)
- Версия React Native
0.61.5
- Android Версия Studio
4.0
- Версия NDK
16.1.4479499
- Версия CMake
3.6.4111459
Исходные файлы
Application.mk
APP_ABI := all
APP_OPTIM := release
APP_PLATFORM := android-8
APP_STL := gnustl_static
APP_CPPFLAGS := -std=c++11
index.js
Сторона JavaScript, вызывающая собственные методы test()
и testWithString()
.
import {NativeModules, Platform} from "react-native";
const {RNSKOpenCVModule} = NativeModules // RNSKOpenCVModule.java
// success
RNSKOpenCVModule.test(Platform.OS)
.then((result) => console.warn (`RNSKOpenCVModule.test(${Platform.OS}) result: ` + JSON.stringify(result, null, 2)))
.catch(console.error)
// error
RNSKOpenCVModule.testWithString(Platform.OS)
.then((result) => console.warn (`RNSKOpenCVModule.testWithString(${Platform.OS}) result: ` + JSON.stringify(result, null, 2)))
.catch(console.error)
RNSKOpenCVModule.java
Вызывает фактический C ++ методы и возвращает WritableMap
, которое можно использовать как Object
в JavaScript.
package com.skizzo.opencv;
// imports
public class RNSKOpenCVModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext reactContext;
RNSKOpenCVModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
static {
System.loadLibrary("native-lib"); // native-lib.cpp
}
@Override
public String getName() {
Log.w ("opencvtest", "RNSKOpenCVModule.java getName() called");
return "RNSKOpenCVModule"; // NativeModules.RNSKOpenCVModule
}
@ReactMethod
public void test(final String platform, final Promise promise) {
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
promise.reject("Activity doesn't exist.");
return;
}
Log.w("RNSKOpenCVModule", "RNSKOpenCVModule.java.test()");
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
StructTestResult testResult = RNSKOpenCVNativeClass.test(platform);
WritableMap res = testResult.toWritableMap();
promise.resolve(res);
}
catch (Exception e) {
promise.reject("ERR", e);
}
}
};
AsyncTask.execute(runnable);
}
@ReactMethod
public void testWithString(final String platform, final Promise promise) {
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
promise.reject("Activity doesn't exist.");
return;
}
Log.w("RNSKOpenCVModule", "RNSKOpenCVModule.java.testWithString()");
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
StructTestWithStringResult testWithStringResult = RNSKOpenCVNativeClass.testWithString(platform);
WritableMap res = testWithStringResult.toWritableMap();
promise.resolve(res);
}
catch (Exception e) {
promise.reject("ERR", e);
}
}
};
AsyncTask.execute(runnable);
}
}
native-lib.cpp
#include <android/log.h>
#include <jni.h>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdexcept>
#include <dirent.h>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
#define LOG_TAG "native-lib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define PLATFORM_OPENCV_ANDROID
#ifdef PLATFORM_OPENCV_ANDROID
#define printf(...) __android_log_print (ANDROID_LOG_DEBUG, "SKOpenCv", __VA_ARGS__);
#endif
typedef struct {
int testInt;
} StructTestResult, *pStructTestResult;
typedef struct {
int testInt;
char* testString;
} StructTestWithStringResult, *pStructTestWithStringResult;
extern "C"
{
// StructTestResult
jclass STRUCT_TEST_RESULT;
jmethodID STRUCT_TEST_RESULT_CONSTRUCTOR;
jfieldID STRUCT_TEST_RESULT_TESTINT;
// StructTestWithStringResult
jclass STRUCT_TEST_WITH_STRING_RESULT;
jmethodID STRUCT_TEST_WITH_STRING_RESULT_CONSTRUCTOR;
jfieldID STRUCT_TEST_WITH_STRING_RESULT_TESTINT;
jfieldID STRUCT_TEST_WITH_STRING_RESULT_TESTSTRING;
jclass JAVA_EXCEPTION;
jint JNI_OnLoad (JavaVM *vm, void *reserved) { // called once onLoad
JNIEnv *env;
if (vm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
// StructTestResult
STRUCT_TEST_RESULT = (jclass) env->NewGlobalRef (env->FindClass("com/skizzo/opencv/StructTestResult"));
STRUCT_TEST_RESULT_CONSTRUCTOR = env->GetMethodID (STRUCT_TEST_RESULT, "<init>", "(I)V");
STRUCT_TEST_RESULT_TESTINT = env->GetFieldID (STRUCT_TEST_RESULT, "testInt", "I");
// StructTestWithStringResult
STRUCT_TEST_WITH_STRING_RESULT = (jclass) env->NewGlobalRef (env->FindClass("com/skizzo/opencv/StructTestWithStringResult"));
STRUCT_TEST_WITH_STRING_RESULT_CONSTRUCTOR = env->GetMethodID (STRUCT_TEST_WITH_STRING_RESULT, "<init>", "(ILjava/lang/String;)V");
STRUCT_TEST_WITH_STRING_RESULT_TESTINT = env->GetFieldID (STRUCT_TEST_WITH_STRING_RESULT, "testInt", "I");
STRUCT_TEST_WITH_STRING_RESULT_TESTSTRING = env->GetFieldID (STRUCT_TEST_WITH_STRING_RESULT, "testString", "Ljava/lang/String;");
// Exception
JAVA_EXCEPTION = (jclass)env->NewGlobalRef (env->FindClass("java/lang/Exception"));
return JNI_VERSION_1_6;
}
jobject JNICALL Java_com_skizzo_opencv_RNSKOpenCVNativeClass_test (JNIEnv *env, jobject instance, jstring platform) {
const char* platformStr = env->GetStringUTFChars (platform, NULL);
LOGD("platformStr: %s", platformStr); // "android"
StructTestResult testResult;
testResult.testInt = 123; // int -> OK
// turn the C struct back into a jobject and return it
return env->NewObject(STRUCT_TEST_RESULT, STRUCT_TEST_RESULT_CONSTRUCTOR, testResult.testInt);
}
jobject JNICALL Java_com_skizzo_opencv_RNSKOpenCVNativeClass_testWithString (JNIEnv *env, jobject instance, jstring platform) {
const char* platformStr = env->GetStringUTFChars (platform, NULL);
LOGD("platformStr: %s", platformStr); // "android"
StructTestWithStringResult testWithStringResult;
testWithStringResult.testInt = 456; // int -> OK
char* openCvVersion = CV_VERSION;
LOGD("openCvVersion: %s", openCvVersion);
testWithStringResult.testString = CV_VERSION; // adding this line produces the crash
// turn the C struct back into a jobject and return it
return env->NewObject(STRUCT_TEST_WITH_STRING_RESULT, STRUCT_TEST_WITH_STRING_RESULT_CONSTRUCTOR, testWithStringResult.testInt, testWithStringResult.testString);
}
}
StructTestResult.java
package com.skizzo.opencv;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
public class StructTestResult {
int testInt;
public StructTestResult () {}
public StructTestResult (int testInt) {
this.testInt = testInt;
}
public StructTestResult (ReadableMap map) {
this (
(int)map.getDouble("testInt")
);
}
public WritableMap toWritableMap() {
WritableMap map = Arguments.createMap();
map.putInt("testInt", this.testInt);
return map;
}
}
StructTestWithStringResult.java
package com.skizzo.opencv;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
public class StructTestWithStringResult {
int testInt;
String testString;
public StructTestWithStringResult () {}
public StructTestWithStringResult (int testInt, String testString) {
this.testInt = testInt;
// this.testString = "testFromJavaConstructor"; // success
this.testString = testString; // error
}
public StructTestWithStringResult (ReadableMap map) {
this (
(int)map.getDouble("testInt"),
map.getString("testString")
);
}
public WritableMap toWritableMap() {
WritableMap map = Arguments.createMap();
map.putInt("testInt", this.testInt);
map.putString("testString", this.testString);
return map;
}
}
Любая помощь приветствуется, я действительно застрял здесь. Спасибо!