Android Studio 2.3.2
Gradle experimental 0.9.2
Last NDK.
I wrote a small function in C that is invoked through JNI in Java but returns a string that precedes characters that should not be there.
This is the C function:
char * text() {
char * txt = "a";
char * ret = malloc(strlen(a)+2);
strcat(ret,txt);
strcat(ret,"b");
return ret;
}
This is the JNI C to return to Java
JNIEXPORT jstring JNICALL
Java_com_myapp_Main_getMsg(JNIEnv *env, jobject instance, jstring var)
{
char * msg = text();
return (*env)->NewStringUTF(env, msg );
}
This is Java call:
static {
System.loadLibrary("myapp");
}
public native String getMsg(String var);
String stringreturnedfromC = getMsg("eee");//eee is not used
This show this:
_Main_getMsgab
but should return only "ab".
This is a strange behavior. Why? And how can I get just the right string?
Noticed your function text() is not correct.
Do something like this
char * text() {
char * txt = "a";
char * ret = (char *)malloc(strlen(txt)); //malloc(strlen(a)+2); is incorrect statment
strcat(ret,txt);
strcat(ret,"b");
return ret;
}
Related
I have a Java instance method which returns a String and I'm calling this method through JNI in C++. I have written the following code:
const char *DiagLayerContainer_getDESC(JNIEnv *env, jobject diagLayer) {
jclass diagLayerClass = env->FindClass(PARSER_CLASS);
jmethodID getDESCDiagLayerMethodID = env->GetMethodID(diagLayerClass, "getDESCDiagLayer", "(Ljava/lang/Object;)Ljava/lang/String;");
jstring returnString = (jstring) env->CallObjectMethod(diagLayer, getDESCDiagLayerMethodID);
return env->GetStringUTFChars(returnString, JNI_FALSE);
}
How do I get the string and convert it to a const char *?
My program crashes on the last line with access violation to 0x00000000. returnString is not NULL.
According to GetStringUTFChars, the last parameter is a pointer to jboolean.
Change
return env->GetStringUTFChars(returnString, JNI_FALSE);
to
return env->GetStringUTFChars(returnString, NULL);
Or better yet, return a std::string
std::string DiagLayerContainer_getDESC(...) {
...
const char *js = env->GetStringUTFChars(returnString, NULL);
std::string cs(js);
env->ReleaseStringUTFChars(returnString, js);
return cs;
}
I've built a similar simple example and the code as is, seems fine so far.
Although, there are two possible error sources.
The first one is the method signature. Try "()Ljava/lang/String;" instead of "(Ljava/lang/Object;)Ljava/lang/String;".
The second one is in the java source itself. If the java method returns a null string, CallObjectMethod() will return a NULL jstring and GetStringUTFChars() fails.
Add a
if (returnString == NULL)
return NULL;
after CallObjectMethod().
So look into the java source and see, whether the method getDESCDiagLayer() might return a null string.
How to get pdf meta-data using MuPdf in Android ? I'm using MuPdf V1.7.
I can get Author & PDF name but I cannot get creation date, creator and etc. I used below function to get information:
fz_lookup_metadata(ctx, glo->doc, FZ_META_INFO_TITLE, info, sizeof(info));
fz_lookup_metadata(ctx, glo->doc, FZ_META_INFO_AUTHOR, info, sizeof(info));
Can anybody help?
hi all i can get creation date from pdf by the below code.
add this into document.h
#define FZ_META_INFO_CREATIONDATE "info:CreationDate"
paste the below code into mupdf.c
JNIEXPORT jstring
JNICALL JNI_FN(MuPDFCore_metaPublishDate)(JNIEnv * env, jobject thiz)
{
char info[64];
globals *glo = get_globals(env, thiz);
fz_context *ctx = glo->ctx;
pdf_document *idoc = pdf_specifics(ctx, glo->doc);
fz_lookup_metadata(ctx, glo->doc, FZ_META_INFO_CREATIONDATE, info, sizeof(info));
return (*env)->NewStringUTF(env, info);
}
then we can able to get this by core.metaPublishDate().
you can easily do this with mupdf library.This function returns a string array which contains metadata information, respectively to keys in keys array. If there is no such info for a key, it returns an empty string
JNIEXPORT jobjectArray JNICALL
JNI_FN(MuPDFCore_metadataInternal)(JNIEnv * env, jobject thiz)
{
char info[64];
globals *glo = get_globals(env, thiz);
jobjectArray arr;
jclass stringClass;
const int nkeys = 4;
const char *keys[nkeys];
int i;
keys[0] = "Title";
keys[1] = "Author";
keys[2] = "Subject";
keys[3] = "Keywords";
stringClass = (*env)->FindClass(env, "java/lang/String");
arr = (*env)->NewObjectArray(env, nkeys, stringClass, NULL);
LOGI("Getting metadata");
for(i=0; idoc, FZ_META_INFO, info, sizeof(info));
LOGI("%s : %s", keys[i], info);
jstring s = (*env)->NewStringUTF(env, info);
if (s != NULL) {
(*env)->SetObjectArrayElement(env, arr, i, s);
}
(*env)->DeleteLocalRef(env, s);
}
return arr;
}
I have implemented Jansson in Android with C and made a function which calculates values from json and that works in C, I tried to use that code in NDK with JNI it builds with no errors, but as i tried to arrange the code to work with JNI it gives me pointer error warning: return from incompatible pointer type. I have read that i need to use jlong for pointers but i cant figure out how that works, it is my first time working in it.
This is my code from C (gives no errors and compiles)
char *doCalc (char *invoice_str) {
json_error_t error;
json_t *invoice = json_loads (invoice_str, JSON_DISABLE_EOF_CHECK, &error);
...
char *result = json_dumps (json_data, JSON_PRESERVE_ORDER);
return result;
}
C code Arranged to work with JNI (gives me error warning: return from incompatible pointer type, which if im correct is because of jchar)
JNIEXPORT jchar JNICALL *Java_com_example_test_doCalc (JNIEnv* env, jobject obj,char const *invoice_str) {
json_error_t error;
json_t *invoice = json_loads (invoice_str, JSON_DISABLE_EOF_CHECK, &error);
...
char *result = json_dumps (json_data, JSON_PRESERVE_ORDER);
return result;
}
Then in my Activity I like to would run doCalc(charJ);, charJ has Json in it. Which would then give me dump of calculated values.
Also I might be looking at this completely wrong, any help is appreciated.
Try to use jstring instead of char*
JNIEXPORT jchar JNICALL * Java_com_example_test_doCalc(JNIEnv * env, jobject obj, jstring invoice_jstring) {
//convert invoice_jstring to char* link bellow
json_error_t error;
json_t * invoice = json_loads(invoice_str, JSON_DISABLE_EOF_CHECK, & error);
...
char * result = json_dumps(json_data, JSON_PRESERVE_ORDER);
return result;
}
for conversion jstring to char* you can use this answer:
JNI converting jstring to char *
OK, so I have the native code below.
I'm trying to return an array of FilePermissionInfo from it, populated with some data returned by stat().
The problem is that I get the following error when NewObject is called the first time:
06-15 20:25:17.621: W/dalvikvm(2287): Invalid indirect reference
0x40005820 in decodeIndirectRef 06-15 20:25:17.621: E/dalvikvm(2287):
VM aborting
It's odd, because the only reference object I have is the jclass (for FilePermissionInfo) and I turn it to a global reference.
The code is:
JNIEXPORT jobjectArray JNICALL
Java_com_mn_rootscape_utils_NativeMethods_getFilesPermissions( JNIEnv* env, jobject thizz, jobjectArray filePathsArray )
{
jobjectArray result;
int size = (*env)->GetArrayLength(env, filePathsArray);
jboolean isCopy;
jclass filePermInfoCls = (*env)->FindClass(env, kFilePermissionInfoPath);
if(!filePermInfoCls)
{
LOGE("getFilesPermissions: failed to get class reference.");
return NULL;
}
gFilePermInfoClass = (jclass)(*env)->NewGlobalRef(env, filePermInfoCls);
LOGI("got gFilePermInfoClass");
jmethodID filePermInfoClsConstructor = (*env)->GetMethodID(env, gFilePermInfoClass, "<init>", kFilePermInfoConstructorSig);
if(!filePermInfoClsConstructor)
{
LOGE("getFilesPermissions: failed to get method reference.");
return NULL;
}
struct stat sb;
LOGI("starting...");
result = (jobjectArray)(*env)->NewObjectArray(env, size, gFilePermInfoClass, NULL);
for(int i = 0; i != size; ++i)
{
jstring string = (jstring) (*env)->GetObjectArrayElement(env, filePathsArray, i);
const char *rawString = (*env)->GetStringUTFChars(env, string, &isCopy);
if(stat(rawString, &sb) == -1)
{
LOGE("stat error for: %s", rawString);
}
LOGI("%ld %ld %ld %ld %ld %ld %ld %ld", sb.st_dev, sb.st_mode, sb.st_nlink, sb.st_uid, sb.st_gid, sb.st_atime, sb.st_mtime, sb.st_ctime);
jobject permInfo = (*env)->NewObject(env,
gFilePermInfoClass,
filePermInfoClsConstructor,
(long)sb.st_dev,
(long)sb.st_mode,
(long)sb.st_nlink,
(long)sb.st_uid,
(long)sb.st_gid,
(long)sb.st_atime,
(long)sb.st_mtime,
(long)sb.st_ctime,
"",
"",
1,
"");
LOGI("xxx1");
(*env)->SetObjectArrayElement(env, result, i, permInfo);
LOGI("xxx2");
(*env)->ReleaseStringUTFChars(env, string, rawString);
LOGI("xxx3");
}
(*env)->DeleteLocalRef(env, filePermInfoCls);
return result;
}
The Java class constructor signature and path are:
const char* kFilePermissionInfoPath = "com/mn/rootscape/utils/FilePermissionInfo";
const char* kFilePermInfoConstructorSig = "(JJJJJJJJLjava/lang/String;Ljava/lang/String;ZLjava/lang/String;)V";
Please note that if I call NewObject on the default constructor then it works fine.
OK, found it.
It was a problem with the jstring parameters. It turns out you cannot pass empty strings (or even NULL for that matter) as a jstring.
Instead I used (*env)->NewStringUTF(env, NULL) to create a NULL jstring.
Seems to work OK now.
Since this question generated somewhat a high activity, I'm posting the final solution below. Note that the nullString variable is being deallocated at the end of its scope (or when you're done using it):
jstring nullString = (*env)->NewStringUTF(env, NULL);
...
jobject permInfo = (*env)->NewObject(env,
gFilePermInfoClass,
filePermInfoClsConstructor,
(jbyte)permsOwner,
(jbyte)permsGroup,
(jbyte)permsOthers,
(jlong)sb.st_uid,
(jlong)sb.st_gid,
(jlong)sb.st_atime,
(jlong)sb.st_mtime,
(jlong)sb.st_ctime,
nullString,
nullString,
(jboolean)1,
nullString);
...
(*env)->DeleteLocalRef(env, nullString);
As you can see, I get jbyte *str form the utf string. Then each character of string has two jbytes else one byte?
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) {
char buf[128];
const jbyte *str;
str = (env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; / OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than * 127 characters */
scanf("%s", buf);
return (*env)->NewStringUTF(env, buf);
}
Here:
str = (env)->GetStringUTFChars(env, prompt, NULL);
you receive buffer of single byte chars. You may even edit this like here:
const char *str = (env)->GetStringUTFChars(env, prompt, NULL);
because GetStringUTFChars() is declared as returning const char *.