我正在开发构建Android应用程序的项目的C ++方面。我需要传递给Java应用程序(通过JNI)的一些信息(通过字符串和字符串数组)。我之前从未这样做过,反向工作的人没有C ++经验,并承认他们无法真正帮助。
我找到了以下代码(来自here)
#include <jni.h>
#include "ArrayHandler.h"
JNIEXPORT jobjectArray JNICALL Java_ArrayHandler_returnArray (JNIEnv *env, jobject jobj){
jobjectArray ret;
int i;
char *message[5]= {"first","second","third","fourth","fifth"};
ret= (jobjectArray)env->NewObjectArray(5,env->FindClass("java/lang/String"),env->NewStringUTF(""));
for(i=0;i<5;i++) {
env->SetObjectArrayElement(ret,i,env->NewStringUTF(message[i]));
}
return(ret);
}
但这对我没有意义。大多数情况下,我不确定我应该如何将其纳入程序的C ++方面,而我却无法确切了解其工作原理。代码是否在执行return(ret);
行时发送消息?或者在for循环中执行行?
理想情况下,我希望字符串/字符串数组在行中“实时”发送,而不是在函数末尾发送,这样我就不必合并新函数。
我发现的代码是否适用于我想要的(通过一些改编)?我正在寻找甚至可能吗?如果是这样,我该怎么做?
修改/更新: 花了一天时间研究JNI和术语,我认为我未能正确地传达我在这里想要实现的内容以及对@jogabonito的答案/回复的评论。
话虽如此。我正在处理的代码是一个IM客户端,需要将消息和状态更新推送到Android Java应用程序(通过JNI),以便Android应用程序不会轮询更新。我已经设法学习如何设置java代码的函数来调用requrest信息。但是,我不知道如何将新消息或状态信息(jabber节字符串)推送到java代码中。所有关于如何执行此操作的代码(请参阅下面的示例)似乎需要从java代码(env,class,methodid等)获取信息。
当我不是调用函数的java代码,而是我的c ++代码时,这对我来说是不可能的。任何解释/帮助将非常感激。
#include <string.h>
#include <stdio.h>
#include <jni.h>
jstring Java_the_package_MainActivity_getJniString( JNIEnv* env, jobject obj){
jstring jstr = (*env)->NewStringUTF(env, "This comes from jni.");
jclass clazz = (*env)->FindClass(env, "com/inceptix/android/t3d/MainActivity");
jmethodID messageMe = (*env)->GetMethodID(env, clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
jobject result = (*env)->CallObjectMethod(env, obj, messageMe, jstr);
const char* str = (*env)->GetStringUTFChars(env,(jstring) result, NULL); // should be released but what a heck, it's a tutorial :)
printf("%s\n", str);
return (*env)->NewStringUTF(env, str);
}
答案 0 :(得分:13)
根据@Sam的要求,这是一种避免使用修改过的UTF-8的方法,因为我们不知道这样做是安全的。
NewStringUTF根据修改后的UTF-8编码创建一个字符串。 将它与用户数据一起使用是不正确的 - 它不太可能用修改后的UTF-8编码。我们可以希望数据中的字符被限制以保持兼容。相反,我们可以正确地转换它。
JNI在其API中使用修改后的UTF-8字符串。我们可以使用我们知道兼容的字符串,特别是Java标识符的文字(除非所有货币符号除外)。
以下是两个本机方法实现。第二种在大多数方面都更好。
对于这种原生方法:
private static native String getJniString();
这是一个实现:
JNIEXPORT jstring JNICALL
Java_the_Package_MainActivity_getJniString(JNIEnv *env, jclass)
{
std::string message = "Would you prefer €20 once "
"or ₹10 every day for a year?";
int byteCount = message.length();
jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
jbyteArray bytes = env->NewByteArray(byteCount);
env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);
// find the Charset.forName method:
// javap -s java.nio.charset.Charset | egrep -A2 "forName"
jclass charsetClass = env->FindClass("java/nio/charset/Charset");
jmethodID forName = env->GetStaticMethodID(
charsetClass, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;");
jstring utf8 = env->NewStringUTF("UTF-8");
jobject charset = env->CallStaticObjectMethod(charsetClass, forName, utf8);
// find a String constructor that takes a Charset:
// javap -s java.lang.String | egrep -A2 "String\(.*charset"
jclass stringClass = env->FindClass("java/lang/String");
jmethodID ctor = env->GetMethodID(
stringClass, "<init>", "([BLjava/nio/charset/Charset;)V");
jstring jMessage = reinterpret_cast<jstring>(
env->NewObject(stringClass, ctor, bytes, charset));
return jMessage;
}
JNI很尴尬。所以,如果我们可以将原生字符串为“UTF-8”的知识转移到Java端,我们可以这样做:
private static String getJniString2()
{
return new String(getJniStringBytes(), Charset.forName("UTF-8"));
}
private static native byte[] getJniStringBytes();
更简单的实施:
JNIEXPORT jbyteArray JNICALL Java_the_Package_MainActivity_getJniStringBytes(JNIEnv *env, jclass)
{
std::string message = "Would you prefer €20 once "
"or ₹10 every day for a year?";
int byteCount = message.length();
jbyte* pNativeMessage = reinterpret_cast<const jbyte*>(message.c_str());
jbyteArray bytes = env->NewByteArray(byteCount);
env->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage);
return bytes;
}
答案 1 :(得分:3)
在您共享的函数中,您在c ++代码中创建了一个NewObjectArray
的对象数组。然后在for循环中,您将使用NewStringUTF
创建一个字符串,并使用SetObjectArrayElement
将其存储在数组中的索引处。到目前为止,您的对象数组仅为您的c ++代码所知,而不是您的java代码。只有当你返回它时,你的java应用程序才能访问它
我可以想到几种方法将字符串从c ++发送到java,尽管它可能不是你想要的。
将String数组传递给本机函数。在本机代码中,您可以使用GetObjectArrayElement
访问每个元素,并使用SetObjectArrayElement
进行更新。这可能毫无意义,因为你最终不得不调用一个我不想要的函数。
如果您已经在java代码中将字符串定义为字段,则可以使用GetFieldID
和GetObjectField
从本机访问该字段,然后使用{{1}更新它}}。我不知道你将如何发信号通知你的java代码该字段已被更新(如果你需要)
修改强>
您编写的更新函数旨在从java层调用。这个的线索是函数SetObjectField
的名称。要从本机上下文调用java代码,您需要从java引用Java_the_package_MainActivity_getJniString
和env
。请查看How do I load my own Java class in C on Android?获取此方法的方法。您可能还必须查找如何在JNI中使用全局引用
答案 2 :(得分:1)
通常使用JNI,调用从JVM进入C代码。通常的范例是:
class
的几个方法创建Java native
(无实现)class
javac
javah
文件运行.class
,这将生成.h
头文件#include
新的头文件并实现接口我看到的反向执行此操作的唯一示例(C代码启动与Java的联系)涉及让C代码实际创建JVM。
要回答关于代码示例的问题,正在创建的Java字符串将在代码执行结束时返回return语句,逻辑上,这是将该执行线程的程序流返回给JVM的时间。 / p>
答案 3 :(得分:0)
您可以将c字符串转换为jstring并返回它。一个例子看起来像是:
JNIEXPORT jstring JNICALL Java_Class_Method(jstring data)
{
// jstring to char *
const char *cStr = (*env)->GetStringUTFChars(env, data, NULL);
// convert char * to jstring and return it
return ((*env)->NewStringUTF(env, cStr));
}