I'm a newbie in the NDK world. I have a key placed in the C/C++ layer and I want to use it when I do a GET with Retrofit.
Here is my code
I have 2 .mk files but they are not relevant to my question and I have a keys.c file where I store the key:
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_hello_world_PlacesListActivity_getNativeKey(JNIEnv *env, jobject instance) {
return (*env)-> NewStringUTF(env, "my_key");
}
I'm able to read the key with this code in my PlacesListActivity:
static {
System.loadLibrary("keys");
}
public static native String getNativeKey();
and now I want to use the native String in the auth header of a GET request in a interface:
public interface TestService {
#Headers({
"client_id:" + PlacesListActivity.getNativeKey()
})
#GET("/some_end_point/")
}
The issue I'm getting is this:
Any ideas on how to fix this? Thank you in advance.
Have you tried implementing the interface and calling the C++ function from there. Interfaces are usually used to declare the function but not actually implement the function.
Ended up modifying the way I pass the auth header
#GET("/some_end_point/")
Observable<PlaceLocations> getPlaces(#Header("client_id") String authKey,#Path("name") String name);
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.
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.
Considering this Java class with the static method:
public class TestClass{
public string str;
public TestClass() {
str = "Test From Java";
}
public static String staticMethod() {
return "Test From Java";
}
}
I have written these lines of code in c++ file:
QAndroidJniObject str = QAndroidJniObject::callStaticObjectMethod(
"org/.../TestClass"
,"staticMethod"
,"(V)Ljava/lang/String;");
It seems everything is working but I don't know how can I use the str Object. I tried to convert it to a QString object using str.tostring() method but it always returns an empty string.
Why it does not work as expected? I've also tested ()Ljava/lang/String; for method signature but no success!
Thanks in advance.
You should specify the returned JNI type in <...> when calling the method :
QAndroidJniObject str = QAndroidJniObject::callStaticObjectMethod<jstring>(
"org/.../TestClass"
,"staticMethod");
QString string = str.toString();
Here there is no need to define the signature since your function has no argument.
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.