I have a relative large size std::string. And I want to pass it to Java without copying. And then pass back to another JNI lib. What is the best approach?
jlong some_jni_call() {
string str = createLargeString(); // say this is from 3rd lib only returns string
string* strInHeap = new string(str); // this should just increase the reference count?
jlong handle = (long)strInHeap;
return handle;
}
Then I can pass back to JNI:
void use_string(jlong handle) {
string* str = (string*)handle;
// use the str...
delete str; // doesn't look so nice, since people can forget to delete
}
Is this a good approach?
This is surely a feasible approach; you can even use similar trick to pass function pointers back and forth.
Related
I'm struggling to access the data returned by camera_parameters->getSupportedPreviewFpsRange() with C++Builder 11.
The getSupportedPreviewFpsRange() function is described here: https://developer.android.com/reference/android/hardware/Camera.Parameters#getSupportedPreviewFpsRange()
I don't have any issue with wrapping data which is not an array returned by other functions, but I could not find a way to wrap the List<int[]> data type.
The Delphi way to handle this seems to be:
camera_parameters: JCamera_Parameters;
pointer: Pointer;
jobject: JObject;
list_fps_ranges: JList;
currentFpsRange, suitableFPSRange: TJavaArray<Integer>;
...
list_fps_ranges := camera_parameters.getSupportedPreviewFpsRange();
jobject := list_fps_ranges.get(I);
pointer := (jobject as ILocalObject).GetObjectID;
fpsRange := TJavaArray<Integer> (WrapJNIArray(pointer, TypeInfo(TJavaArray<Integer>)));
Unfortunately, I could not find a way to properly translate:
fpsRange := TJavaArray<Integer> (WrapJNIArray(pointer, TypeInfo(TJavaArray<Integer>)));
into C++.
I tried with:
TJavaObjectArray__1<_di_JInteger>
without any luck, as there is no WrapJNIArray() function.
How could it be done?
Unfortunately, I could not find a way to properly translate:
fpsRange := TJavaArray<Integer> (WrapJNIArray(pointer, TypeInfo(TJavaArray<Integer>)));
into C++.
Try something like this:
_di_JCamera_Parameters camera_parameters;
TJavaArray__1<int> *currentFpsRange;
TJavaArray__1<int> *suitableFPSRange;
...
_di_JList list_fps_ranges = camera_parameters->getSupportedPreviewFpsRange();
_di_JObject jobject = list_fps_ranges->get(I);
void *objID = ((_di_ILocalObject)jobject)->GetObjectID();
// alternatively:
//
// #include <Androidapi.Helpers.hpp>
// void *objID = TAndroidHelper::JObjectToID(jobject);
TJavaArray__1<int> *fpsRange = (TJavaArray__1<int>*) WrapJNIArray(objID, __typeinfo(TJavaArray__1<int>));
I tried with:
TJavaObjectArray__1<_di_JInteger>
without any luck, as there is no WrapJNIArray() function.
First, TJavaArray<Integer> translates to TJavaArray__1<int>, not to TJavaObjectArray__1<_di_JInteger>.
Second, WrapJNIArray() is a helper function declared in <Androidapi.JNIBridge.hpp>.
The List<int[] list_fps_ranges example you posted works fine but I'm still confused about how to use WrapJNIArray properly in the case of a simple List<Integer>
I tried to replicate your solution with the getZoomRatios() function which is also part of Camera.Parameters
The getZoomRatios() function returns a List<Integer>
int n_zoomratios;
void *pointer;
_di_JList list_zoomratios;
list_zoomratios= jcamera_parameters->getZoomRatios(); // return List<Integer>
_di_JObject jobject= list_zoomratios; //fishy...
pointer= TAndroidHelper::JObjectToID(jobject);
TJavaArray__1<int> *zoomratios = (TJavaArray__1<int>*) WrapJNIArray(pointer, __typeinfo(TJavaArray__1<int>));
Unfortunately I can not figure which parameters of list_zoomratio should be used by jobject.
In the fps ranges example, the list_preview_fps->get() function returns a JObject which is attributed to jobject but in the case of the list_zoomratios there is no obvious JObject parameter I could use. I tried many different ways but without any success.
What is the proper way to wrap the integer array?
By the way, is there a book/document which describes these wrapping mechanisms? I could not find any information online on this subject.
Thank you for your help
Problem solved this way:
int n_zoomratios;
int i;
void *pointer;
_di_JList list_zoomratios;
_di_JInteger zoomratio;
list_zoomratios= jcamera_parameters->getZoomRatios(); // List<Integer>
n_zoomratios=list_zoomratios->size();
for(i=0;i<n_zoomratios;i++)
{
_di_JObject jobject=list_zoomratios->get(i);
pointer= TAndroidHelper::JObjectToID(jobject);
zoomratio = TJInteger::Wrap(pointer);
sprintf(txt,"%d %d",i,zoomratio->intValue());
log(txt);
}
However I can not figure why I can not convert a full array with WrapJNIArray and I have to convert each element one by one instead.
In a C++ file, I want to convert a const char* to KString, so that I can then pass the KString to a Kotlin file using Kotlin/Native.
I believe the answer lies in the function
OBJ_GETTER(utf8ToUtf16, const char* rawString, size_t rawStringLength)
that I found in KString.cpp. But even though I discovered the used define statements in Memory.h, I have not yet managed to properly call the function utf8ToUtf16 from my own C++ file to get a KString. Any help is appreciated.
It depends on how you want to interact with Kotlin code. If you produce dynamic library with -produce dynamic, then string are converted automatically, see for example https://github.com/JetBrains/kotlin-native/blob/adf8614889e8cf5038a79960aa9651ca7d45e409/samples/python_extension/src/main/c/kotlin_bridge.c#L72.
So no additional magic is required at all. Same with Objective-C strings and -produce framework. And for other cases, there shall be no need to pass strings C -> Kotlin (callbacks produced with staticCFunction also do autoconversion).
I ended up taking the pieces to write my own function:
KString getKString(const char* rawString) {
size_t rawStringLength = strlen(rawString);
ObjHeader** OBJ_RESULT;
uint32_t charCount = utf8::unchecked::distance(rawString, rawString + rawStringLength);
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, charCount, OBJ_RESULT)->array();
KChar* rawResult = CharArrayAddressOfElementAt(result, 0);
auto convertResult =
utf8::unchecked::utf8to16(rawString, rawString + rawStringLength, rawResult);
ObjHeader* obj = result->obj();
UpdateReturnRef(OBJ_RESULT, obj);
return (const ArrayHeader*)obj;
}
In my test code (C++), I use it like this:
...
RuntimeState* state = InitRuntime();
KString inMessage;
{
ObjHolder args;
inMessage = getKString("Hello from C++");
}
...
DeinitRuntime(state);
and include Memory.h, Natives.h, Runtime.h, KString.h, utf8.h, stdlib.h, and string. You may be able to get rid of some of these.
As a side remark, you may realize how AllocArrayInstance is used in the function. It would be nice, if one simply could do the same thing for getting a KString, something like:
ObjHeader** OBJ_RESULT;
KString kstr = utf8ToUtf16(rawString, rawStringLength, OBJ_RESULT);
This did not work from my function, since utf8ToUtf16 was not found. I believe the reason is, that (at the time of writing) the respective function in KString.cpp is inside a namespace {...} block, such that it cannot be used from another file. That's why I ended up mimicking the function as shown above.
jclass queueCls = (jclass)(*env)->FindClass(env, "java/util/concurrent/LinkedBlockingQueue");
if(queueCls == NULL) {
LOGE("can not get class for blocking queue");
return;
}
jmethodID take = (*env)->GetMethodID(env, queueCls, "take",
"()[B");
if (take == NULL) {
LOGE("can not get take method for blocking queue");
return;
}
and the value of take is always NULL. See this. I am using LinkedBlockingQueue<byte[]>.
Appreciate if any example is provided.
Take a look at this answer.
You should consider the LinkedBlockingQueue<byte[]> object as a simple LinkedBlockingQueue on JNI side, because Java Generics are resolved at compile time.
I suggest you to use javah to generate the proper native header file with correct JNI signatures starting from the compiled class.
Say I have a Java class,
public static String helloWorld() {
return "hello world!";
}
How, in Qt, do I get what this function returned? The example for notifications has the following:
QAndroidJniObject javaNotification = QAndroidJniObject::fromString(m_notification);
QAndroidJniObject::callStaticMethod<void>("org/qtproject/example/notification/NotificationClient",
"notify",
"(Ljava/lang/String;)V",
javaNotification.object<jstring>());
Should by method descriptor be ()Ljava/lang/String;? What should be in the chevrons after callStaticMethod?
Edit. Fixed and I don't know how. I didn't have the chevrons, and the descriptor was correct.
QAndroidJniObject string = QAndroidJniObject::callStaticObjectMethod<jstring>("com/your/project/YourClass", "helloWorld");
Is equivalent to:
QAndroidJniObject string = QAndroidJniObject::callStaticObjectMethod("com/your/project/YourClass", "helloWorld", "()Ljava/lang/String;");
For trivial signatures, that is, signatures that takes no arguments and returns one of the know jni types, you can write the short version by supplying the template type.
I've never really done much C and am a bit stumped on the best way to send a boolean from an Android app to the Pebble Watch.
I have strings working fine, but there doesn't seem to be an addBoolean method on PebbleDictionary. As a work around I am trying to use addUint8 to send a 1 or 0, but am having trouble handling the message on the Pebble.
Here is my Android code:
PebbleDictionary data = new PebbleDictionary();
if (isGPSFix()){
data.addUint8(GPS_HAS_FIX_KEY, Byte.valueOf("1"));
} else {
data.addUint8(GPS_HAS_FIX_KEY, Byte.valueOf("0"));
}
PebbleKit.sendDataToPebble(app.getContext(), UUID, data);
And in my Pebble I have a data struct:
static struct MyData {
uint8_t haveGPS[1];
.... // other stuff ommitted
AppSync sync;
uint8_t sync_buffer[256];
} s_data;
And then I am trying to compare it like this in my sync_tuple_changed callback.
static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) {
(void) old_tuple;
switch (key) {
case GPS_HAS_FIX_KEY:
if (memcmp(s_data.haveGPS, new_tuple->value->data, 8) == 0){
memcpy(s_data.haveGPS,new_tuple->value->data, new_tuple->length);
vibes_short_pulse();
}
break;
default:
return;
}
}
The watch doesn't crash, it just never vibrates when the phone drops or acquires GPS.
Things look good on the Android side. I think this is more of an AppSync problem.
Here are a few things to check in the watch application:
Make sure you create a list of tuples with initial values on the watch. This list needs to to contain your key GPS_HAS_FIX_KEY;
Tuplet initial_values[] = {
TupletInteger(GPS_HAS_FIX_KEY, (uint8_t) 0),
/* Other tuplets that you will synchronize */
};
Make sure you pass those tuplets to the app_sync_init() function:
app_sync_init(&sync, sync_buffer, sizeof(sync_buffer),
initial_values, ARRAY_LENGTH(initial_values),
sync_tuple_changed_callback, sync_error_callback, NULL);
Those two steps are required for app_sync to work (cf AppSync reference documentation).
As far as I understand you have to send an Dictionary of keys and objects to the watch. In Objective C it looks like this:
NSDictionary *update = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
Keys are all integer and objects I would think can be of Boolean. In my case only String what works well.
In a loop I perform something like this on the watch:
t=dict_find(received,count);
strcpy(strs[count], dict_find(received,count)->value->cstring);
But I have to tell you that I am still a rookie.
Thanks to sarfata's accepted answer above, I found that I had not added this new item to the Tuple that the Pebble was expecting.
Once I added that, my switch statement started working and I just had to get the memory compare to work. Here is the working code for my memory compare in case it helps anyone.
case GPS_HAS_FIX_KEY:
if (memcmp(s_data.haveGPS, new_tuple->value->data, 1) != 0){
memcpy(s_data.haveGPS,new_tuple->value->data, 1);
vibes_short_pulse();
}
break;
It really was a simple as expecting one byte (not 8 bytes - I thought it was doing a bit compare) and negating the logic for the case where the new value is NOT like the old one.