I know that using the following method, I could get a string from the Android main activity to Qt/C++ side.
QAndroidJniObject my_string = QtAndroid::androidActivity().callObjectMethod<jstring>("someMethod");
QString my_qsrting = my_string.toString();
Above is great if someMethod returns a Java String. But what if someMethod returns an int? Following way does not work in Qt? It has compilation errors.
QAndroidJniObject my_int = QtAndroid::androidActivity().callObjectMethod<jint>("someMethod");
How can I collect an int or a jint using QtAndroid::androidActivity().callObjectMethod?
Going through this link, I could not find the answer to my question.
Qt version:
Qt 5.12 commercial version
You don't use callObjectMethod for methods that return primitives; you use callMethod for that:
jint my_int = QtAndroid::androidActivity().callMethod<jint>("someMethod");
Related
Is there any way to use jniRegisterNativeMethods to map JNI functions in a NDK app? i.e. use a method_table to map native (C/C++) functions via the JNI instead of using ridiculously long JNI method names?
For example, in one exercise I saw, there was a C file added onto the platform it self,
#include "core_jni_helpers.h"
#include "jni.h"
static jlong init_native(JNIEnv *env, jobject clazz)
{
return 0;
}
// ...
static JNINativeMethod method_table[] = {
{ "init_native", "()J", (void*)init_native },
{ "finalize_native", "(J)V", (void*)finalize_native },
// ...
};
int register_android_server_ExampleService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/OpersysService",
method_table, NELEM(method_table));
};
But then the register_android_server_ExampleService was was manually invoked in services/core/jni/onload.cpp (on the platform)
Is there any way to do this or something similar with the NDK though?
My guess is no, as JNIHelp.h and core_jni_helpers.h aren't available in the NDK, and the Kotlin tools in Android Studio likely wouldn't be able to run a function in order to perform the auto complete. However I thought it was worth asking in the small case I could somehow avoid naming functions like Java_vendor_<name>_<name>_<name>_test_MainActivity_stringFromJNI
I'm calling native function from Java:
String pathTemp = Environment.getExternalStorageDirectory().getAbsolutePath()+Const.PATH_TEMP
String pathFiles = Environment.getExternalStorageDirectory().getAbsolutePath()+Const.PATH_FILES
engine.init(someInt, pathTemp, pathFiles);
And I have the native function:
extern "C" JNIEXPORT void Java_com_engine_init(JNIEnv *env, jobject __unused obj, jint someInt, jstring pathTemp, jstring pathFiles) {
const char *pathTemp_ = env->GetStringUTFChars(pathTemp, JNI_FALSE);
const char *pathFiles_ = env->GetStringUTFChars(pathFiles, JNI_FALSE); // <-- CRASH
// More init code
env->ReleaseStringUTFChars(pathTemp, pathTemp_);
env->ReleaseStringUTFChars(pathRecording, pathRecording_);
}
The problem: pathTemp is arriving good, but pathFiles==NULL in native function.
Rechecked, and confirmed - both strings are non NULL in java.
One more strange thing - The problem is on LG-G3 (android 6.0).
On Meizu PRO 5 (android 7.0) - everything works good - both strings are intact.
What is this JNI magic? Any clue?
I had the same problem as this and while I can't guarantee this is the same, I found a better solution than re-ordering the parameters.
tldr; Ensure the code works for 32bit and 64bit platforms as pointers have different sizes. I was running 32bit native code and passed nullptr as a parameter and java expected a long which resulted in all parameters after the nullptr to be invalid.
(JJLjava/lang/String;Z)V -> (final long pCallback, final long pUserPointer, final String id, final boolean b)
pCallback was always set to a valid value (pointer casted to jlong in c++) and pUserPointer was always nullptr. I found this answer and tried switching the order around and it 'just worked' but I knew that fix was never going to be approved.
After looking at the JNI documentation on the Android website again (https://developer.android.com/training/articles/perf-jni) I took note of the "64-bit considerations" section and took a stab at my assumption of the data size. This feature was developed with a 64bit device (Pixel 3) but issues had been reported on a 32bit device (Amazon Fire Phone) so nullptr would be 32bit but the java function still expected a long (64bit).
In my situation the offending parameter was always unused so I could safely remove it and everything "just worked" (including some other parameters which were broken).
An alternative would be to have a define/function/macro for JniLongNullptr which is just 0 casted to jlong.
Not an answer, but workaround. Moved the strings (in parameters) to be before int parameter. Now it's working. I have no idea why is this.
This question already has answers here:
Access android context in ndk application
(2 answers)
Closed 5 years ago.
I want to use ARCore inside a native c++ android application. Therefore, I need the JNI env and context. The JNI env is provided inside the android_app struct but no context is given. I found a solution to get the android context by using the VM. But there is no valid pointer to the context.
JNIEnv *env = 0; // env: 0x0000007ed9bdb0c8
jobject contextObj = 0; // env: 0x0000000000000011
_pAndroidApp->activity->vm->AttachCurrentThread(&env, NULL);
jclass activityClass = env->FindClass("android/app/NativeActivity"); // activityClass: 0x0000000000000001
// or use: jclass activityClass = env->GetObjectClass(_pAndroidApp->activity->clazz);
jmethodID contextMethod = env->GetMethodID(activityClass, "getApplicationContext", "()Landroid/content/Context;"); // contextMethod: 0x000000709660f0
contextObj = env->CallObjectMethod(_pAndroidApp->activity->clazz, contextMethod);
After this I want to create an ARCore session:
ArSession_create(_pAndroidApp->activity->env, contextObj, &ar_session_);
The pointer to the contextObj seems to be wrong. In fact, the activityClass seems to be wrong as well. The result creating the session is a SIGINT (signal SIGINT).
The java instance of the activity can be used as the context. I created the session be calling:
ANativeActivity *activity = app->activity;
CHECK(ArSession_create(activity->env, activity->clazz, &ar_session_) == AR_SUCCESS);
CHECK(ar_session_);
ArConfig* ar_config = nullptr;
ArConfig_create(ar_session_, &ar_config);
CHECK(ar_config);
const ArStatus status = ArSession_checkSupported(ar_session_, ar_config);
CHECK(status == AR_SUCCESS);
CHECK(ArSession_configure(ar_session_, ar_config) == AR_SUCCESS);
ArConfig_destroy(ar_config);
ArFrame_create(ar_session_, &ar_frame_);
CHECK(ar_frame_);
Remember that the session creation can cause some Java exceptions for things like the phone is unsupported, or the companion app is not installed. This will result in the the return code from ArSession_create() not being AR_SUCCESS, but something like AR_UNAVAILABLE_ARCORE_NOT_INSTALLED. In that case, asserting is probably not the best thing to do, but rather show a message to the user.
I have spent a ridiculous amount of time trying to figure this out and I am at an absolute loss.
I am working with the JUCE library and have modified one of their sample projects. My goal is to have a very simple Android app that is written in C++ and then ported to Android. I need a function in C++ that I can call that will then call a function on the Android side that will return my heap size and other characteristics to my C++ code so that I can manage memory there.
If anyone has a simple solution that would be amazing. Right now my current snag is this:
char const buf[] = "From JNI";
jstring jstr = env->NewStringUTF(buf);
jclass clazz = env->FindClass("android/os/Debug");
But I keep getting an error saying that 'NewStringUTF' is not a _JNIEnv member... but if I right click on the method and jump to the definition, I see it in my jni.h file... any suggestions? I'm working in Xcode by the way...
Is it plain C, not C++? Perhaps your file has a .c extension.
If it's plain C it should be
JNIEnv* env;
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
(*env)->NewStringUTF(env, buf);
I'm writing an Android app using the NDK and NativeActivity. My app depends on a few bits of third party code that are shipped as assets. Currently I'm working on trying to extract those assets while keeping the folder structure intact.
I've tried using the AssetManager, but to keep the folder structure intact it seemed like there would a huge amount of code involved, for a simple task such as what I've mentioned. I've since switched focus to try to implement treating the APK as a ZIP file and extract its contents that way. But that requires I find the exact path to the APK.
In a normal Android app one would use getPackageCodePath, but this is an abstract method attached to the Context class. My question is how do I obtain the exact path to the APK when not using a normal Activity?
Also I tried calling getPackageCodePath via JNI, but that crashed the app on account of not being able to find the method.
EDIT:
Is this even possible?
I was actually able to call getPackageCodePath via JNI and get it to work. The following code put at the top of android_main in the native-activity sample in NDK r7 logs the correct path and doesn't crash:
void android_main(struct android_app* state) {
struct engine engine;
ANativeActivity* activity = state->activity;
JNIEnv* env = activity->env;
jclass clazz = (*env)->GetObjectClass(env, activity->clazz);
jmethodID methodID = (*env)->GetMethodID(env, clazz, "getPackageCodePath", "()Ljava/lang/String;");
jobject result = (*env)->CallObjectMethod(env, activity->clazz, methodID);
const char* str;
jboolean isCopy;
str = (*env)->GetStringUTFChars(env, (jstring)result, &isCopy);
LOGI("Looked up package code path: %s", str);
...
}
I feel like this might not be a great solution, though. There are two things that worry me:
Thread safety - there's an ugly warning about only using the env member of ANativeActivity in the main Java thread, and if I understand things correctly, this code is going to get run in the native activity's thread.
ANativeActivity's clazz member appears to be misnamed and is actually the instance of the Java NativeActivity instead of the class object. Otherwise this code wouldn't work. I really hate relying on something that is obviously misnamed like this.
Aside from that, it works, and I'm actually about to use it myself to try to extract the assets out of the .apk using libzip and into the data directory.
Since I just had to search for exactly how to do the attach/detach calls I'll paste the updated version here.
The following seems to get the right location without crashing (after minimal testing)
ANativeActivity* activity = state->activity;
JNIEnv* env=0;
(*activity->vm)->AttachCurrentThread(activity->vm, &env, 0);
jclass clazz = (*env)->GetObjectClass(env, activity->clazz);
jmethodID methodID = (*env)->GetMethodID(env, clazz, "getPackageCodePath", "()Ljava/lang/String;");
jobject result = (*env)->CallObjectMethod(env, activity->clazz, methodID);
const char* str;
jboolean isCopy;
str = (*env)->GetStringUTFChars(env, (jstring)result, &isCopy);
LOGI("Looked up package code path: %s", str);
(*activity->vm)->DetachCurrentThread(activity->vm);
Had to modify it to this in the year 2014.
ANativeActivity* activity = state->activity;
JNIEnv* env=0;
activity->vm->AttachCurrentThread(&env, NULL);
jclass clazz = env->GetObjectClass(activity->clazz);
jmethodID methodID = env->GetMethodID(clazz, "getPackageCodePath", "()Ljava/lang/String;");
jobject result = env->CallObjectMethod(activity->clazz, methodID);
jboolean isCopy;
std::string res = env->GetStringUTFChars((jstring)result, &isCopy);
LOG_DEBUG("Looked up package code path: %s", res.c_str());
activity->vm->DetachCurrentThread();
Did you try to read /proc/self/cmdline from your application?
You should be able to open it as a normal (as far as proc files are normal :-) so you can read from the file until EOF, but not seek) c FILE and read from it.
As an example for the phone app, I can see from ps in android that the name of the applications is the expected app name:
# ps | grep phone
radio 1588 839 1467420 103740 SyS_epoll_ 7f7de374ac S com.android.phone
And checking the cmdline for that pid returns a good app name:
# cat /proc/1588/cmdline
com.android.phone
Call getPackageCodePath() in Java and pass jstring to your C++ app via native method