JNI : When should I release resources? - java-native-interface

JNIEXPORT jstring JNICALL Java_com_xxx_xpdf_PdfToText_getTextOnly(JNIEnv *env, jclass obj,
jstring pdf_path) {
const char *pdf = env->GetStringUTFChars(pdf_path, 0);
std::string content;
unsigned int i = getTextFromPDF(pdf, &content);
env->ReleaseStringUTFChars(pdf_path, pdf);
const char *result = content.c_str();
jstring str = env->NewStringUTF(result);
return str;
}
Do I need to relase str and content here ? And why ?

No. str refers to a Java object which continues to exist beyond this JNI method, as it is the return value. content is a C++ local object which is auto-destroyed when its declaring scope exits.

Related

Get int value from java

im trying to get the value of an int which is non static
java code
public int valuex = 500;
public native int intfromjni();
native code
JNIEXPORT jint JNICALL Java_com_samplejni_cls_intfromjni(JNIEnv* env, jobject thiz)
{
jclass x = env->GetObjectClass(thiz);
jfieldID f = env->GetFieldID(x, "valuex", "I");
int i = env->GetIntField(x, f);
return i;
}
but it throws crash with log
JNI DETECTED ERROR IN APPLICATION: jfieldID int com.samplejni.cls.valuex not valid for an object of class java.lang.Class<com.samplejni.cls>
here
int i = env->GetIntField(x, f);
you trying to use jclass to get a particular object's value. It would work for static fields.
For non-static firelds you should use jobject:
int i = env->GetIntField(thiz, f);

Convert jobject to Integer in JNI C++ and parse the intValue

I have following code which accepts int value at JAVA level
dataObj.property(20);
This is the definition of property function
void property(Object value){
// JNI called
}
JNI function definition
Java_com_XXX_XXXX_JNIProperty(JNIEnv *a_pEnv, jclass, **jobject a_oPropertyValue**) {
// how to convert jobject to Integer object and then get the intValue
jclass cls = a_pEnv->FindClass("java/lang/Integer");
jmethodID getVal = a_pEnv->GetMethodID(cls, "intValue", "()I");
int i = a_pEnv->CallIntMethod(value, getVal);
}
How to convert jobject which was of type Object in Java to Integer object in JNI and parse int value ?
For a class in Java
public class PassObject {
public static native void displayInt(Object obj);
public static void property(int value) {
PassObject.displayInt(value);
}
public static void main(String[] args) {
PassObject.property(44);
// make sure to be careful in your case,
// as you make assumption that "Object" will be of type "Int"
// It's really easy to be very nasty.
PassObject.displayInt("44");
}
}
you can use following JNI code
JNIEXPORT void JNICALL Java_recipeNo055_PassObject_displayInt
(JNIEnv * env, jclass obj, jobject objarg) {
jclass cls = (*env)->GetObjectClass (env, objarg);
jmethodID integerToInt = (*env)->GetMethodID(env, cls, "intValue", "()I");
jint result = (*env)->CallIntMethod(env, objarg, integerToInt);
printf("Passed value: %d\n", result);
}
if you want to be extra careful, you can always do
jclass cls = (*env)->GetObjectClass (env, objarg);
if( (*env)->IsInstanceOf(env, objarg, (*env)->FindClass(env, "java/lang/Integer")) ) {
jmethodID intValue = (*env)->GetMethodID(env, cls, "intValue", "()I");
jint result = (*env)->CallIntMethod(env, objarg, intValue);
printf("%-22s: %d\n", "Passed value (int)", result);
} else {
// error
}

Setting char* properties of a class

I have 10 char* properties of my class called Car,what is the best way to write the setters of these 10 char* properties? One way is to directly set the value in it :
void Car<T>::setKey(const char* toCopyKey)
{
delete[] key;
int length=strlen(toCopyKey);
key=new char[length+1];
strcpy(key,toCopyKey);
}
and do this 10 times ,other solution I thought of , is to make a function that creates a copy of the passed char* and then assigns it in the setter :
char* Car<T>::copyString(const char* s)
{
int length=strlen(s);
char* property=new char[length+1];
strcpy(property,s);
return property;
}
and use the copyString method in every setter like this :
void Car<T>::setModel(const char* toCopyModel)
{
delete[] model;
model=copyString(toCopyModel);
}
But I was wondering if this second solution is correct and if there is a better way to do this copying?I cannot use std::string and vector.
I guess this is an assignment of some C++ course or tutorial, because otherwise I would recommend to question the whole design.
In general, I would learn as early as possible to not do manual memory management at all and use C++ standard library smart pointers. This relieves you from the burden to write destructors, copy|move-assignment and copy|move constructors.
In your example, you could use std::unique_ptr<char[]> to hold the string data. This is also exception safe and prevents memory leaks. Creation of the unique_ptr<char[]> objects can be centralized in a helper method.
class Car {
private:
std::unique_ptr<char[]> model;
std::unique_ptr<char[]> key;
static std::unique_ptr<char[]> copyString(char const* prop) {
auto const len = std::strlen(prop);
auto p = std::make_unique<char[]>(len+1);
std::copy(prop, prop + len, p.get());
p[len] = '\0';
return p;
}
public:
void setModel(char const* newModel) {
model = copyString(newModel);
}
void setKey(char const* k) {
key = copyString(k);
}
char const* getModel() const {
return model.get();
}
};
If you don't know them, I would recommend to read about the rule of zero.
You can combine your two methods by using a reference parameter:
static void Car<T>::setStringProp(char *&prop, const char *toCopyString) {
delete[] prop;
prop = new char[strlen(toCopyString)+1];
strcpy(prop, toCopyString);
}
void Car<T>::setModel(const char *toCopyModel) {
setStringProp(model, toCopyModel);
}
And make sure that your constructor initializes all the properties to NULL before calling the setters, because delete[] prop requires that it be an initialized pointer.
Car<T>::Car<T>(const char *model, const char *key, ...): model(nullptr), key(nullptr), ... {
setModel(model);
setKey(key);
...
}

Cannot convert 'const bool' to 'jobject

I need to add a bool to an object map, my bool is defined as a primitive, and I need to convert it to an object.
How do I do that ?
You can convert your bool to a jboolean then box the jboolean into a Boolean jobject with JNI calls like this:
jboolean value = true;
jclass booleanClass = env->FindClass("java/lang/Boolean");
jmethodID methodID = env->GetMethodID(booleanClass, "<init>", "(Z)V", false);
jobject booleanObject = env->NewObject(booleanClass, methodID, value);

JNI C++ templates to acquire/release resources

What is recommended template set or library to acquire/release JNI resources from C++ code?
"Bad" example:
//C++ code
extern "C"
JNIEXPORT void JNICALL Java_ClassName_MethodName
(JNIEnv *env, jobject obj, jstring javaString)
{
//Get the native string from javaString
const char *nativeString = env->GetStringUTFChars(javaString, 0);
//Do something with the nativeString
//DON'T FORGET THIS LINE!!!
env->ReleaseStringUTFChars(javaString, nativeString);
}
Obviously, everybody uses a template set, not the code above.
For jstring it calls GetStringUTFChars to acquire resource and ReleaseStringUTFChars to release, when object goes "out of scope".
Gotta be something similar to auto_ptr template, but tailored to JNI to call GetStringUTFChars/ReleaseStringUTFChars for jstring, for example.
You can use std::unique_ptr in C++11, which allows you to set the deleter yourself:
#include <memory>
#include <functional>
typedef std::unique_ptr<char const[], std::function<void(char const*)>> jni_string_ptr;
// the "std::function" is needed to store any callable†
extern "C"
JNIEXPORT void JNICALL Java_ClassName_MethodName
(JNIEnv *env, jobject obj, jstring javaString)
{
//Get the native string from javaString
jni_string_ptr nativeString(env->GetStringUTFChars(javaString, 0),
[=](char const* p) mutable{ env->ReleaseStringUTFChars(javaString, p); });
// lambda as the deleter
// mutable needed if "ReleaseStringUTFChars" is a non-const method
//Do something with the nativeString
// nativeString automatically calls the passed deleter
}
If you're stuck in C++03 and don't have access to std::unique_ptr, boost::shared_array provides a.. viable alternative.
#include <boost/shared_ptr.hpp>
typedef boost::shared_array<char const*> jni_string_ptr;
struct jni_string_deleter{
jni_string_deleter(JNIEnv* e, jstring js)
: _env(e), _java_string(js) {}
void operator()(char const* p) const{
_env->ReleaseStringUTFChars(_java_string, p);
}
private:
JNIEnv* _env;
jstring _java_string;
};
extern "C"
JNIEXPORT void JNICALL Java_ClassName_MethodName
(JNIEnv *env, jobject obj, jstring javaString)
{
//Get the native string from javaString
jni_string_ptr nativeString(env->GetStringUTFChars(javaString, 0),
jni_string_deleter(env, javaString));
//Do something with the nativeString
// nativeString automatically calls the passed deleter
}
† You can also use a specific delter type here, as with the shared_array example, to avoid the type-erasure overhead of std::function:
struct jni_string_deleter{
jni_string_deleter(JNIEnv* e, jstring js)
: _env(e), _java_string(js) {}
void operator()(char const* p) const{
_env->ReleaseStringUTFChars(_java_string, p);
}
private:
JNIEnv* _env;
jstring _java_string;
};
typedef std::unique_ptr<char const[], jni_string_deleter> jni_string_ptr;
// create
jni_string_ptr(env->get_the_chars(), jni_string_deleter(env, javaString));
The keyword here is RAII:
class jni_string {
public:
jni_string(JNIEnv *env, jstring javaString)
: env_(env), javaString_(javaString)
{ nativeString_ = env_->GetStringUTFChars(javaString_, 0); }
~jni_string() { env_->ReleaseStringUTFChars(javaString_, nativeString_); }
operator const char *() const { return nativeString_; }
private:
jni_string(const jni_string &x);
jni_string &operator=(const jni_string &x);
JNIEnv *env_;
jstring javaString_;
const char *nativeString_;
};
Usage would be:
//C++ code
extern "C"
JNIEXPORT void JNICALL Java_ClassName_MethodName
(JNIEnv *env, jobject obj, jstring javaString)
{
//Get the native string from javaString
jni_string nativeString(env, javaString);
//Do something with the nativeString
}