React Native (Android):无法通过 JNI 在 jobject 中返回字符串
React Native (Android): Can't return String in a jobject via JNI
我试图(字面意思(弥合JavaScript(React Native 0.61.5(和Android C++之间的差距,以便使用OpenCV(3.2.0(。可能值得注意的是,我使用了已弃用的 NDK (16(,否则我无法使用gnustl_static
.
问题所在
每当我尝试将String
从本机C++代码返回到 Java 时,应用程序都会在本地崩溃。返回简单的数据类型(在我的示例中,int
(不是问题。
开发环境
- macOS 卡特琳娜版本
10.15.4 (19E266)
- React Native Version
0.61.5
- 安卓工作室版本
4.0
- NDK 版本
16.1.4479499
- CMake 版本
3.6.4111459
- NDK 版本
源文件
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
,该可以用作JavaScript中的Object
。
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;
}
}
非常感谢任何帮助,我真的被困在这里。谢谢!
testWithStringResult.testString
是结构体的成员,属于字符数组类型。尚未分配针对变量的内存。您必须先分配内存,然后执行 strcpy(( 操作。这是您可以通过替换麻烦的行来尝试的代码块。
testWithStringResult.testString = new char(strlen(CV_VERSION));
strcpy(testWithStringResult.testString, CV_VERSION);
希望它能解决问题。如果没有,请提供崩溃日志。
相关文章:
- 通过JNI传递数据数组的最快方法是什么
- 使用赋值运算符重载从类中返回jobject
- 为 NewObjectA() 函数创建 jvalues 的参数数组时出错 - JNI Invocation API
- 将 C# DLL 导入 C++ 以用于 JNI
- 用C++包装 Java 库 (JNI)
- 如何通过 JNI 将 C 字符串表情符号传递给 Java
- JNI从Android调用C++方法
- React Native (Android):无法通过 JNI 在 jobject 中返回字符串
- JNI 在应用程序中检测到错误:在为 innerclass 调用 NewObject 时使用了无效的 jobject
- 如何调用传递给 JNI 'jobject' 的 Java 对象的子类/子类的方法
- 如何在 JNI 中访问 jobject 的值
- 将 JNI -> jobject(基本上是 java 文件中的映射和/或映射的映射)转换为 std::map(c++)
- jni deleteRef for jclass and jobject
- JNI:MAP jobject到本机C 对象
- Android NDK / General JNI 问题:将对象/jobject 转换为 c++ 用户 defiend
- 对JNI类型(如jobject)的C++引用
- C++/JNI-存储对象(jobject)在向量和数组中发生意外更改,C++或JNI问题
- 在JNI jobject中存储一个c++对象实例,稍后检索
- 分段错误-如何在JNI中为C++创建的新线程不断调用java实例jobject
- JNI:给定jobject,获取它的缓存方法