Mentioned in the article:
http://androidcookbook.com/Recipe.seam?recipeId=77
Is:
"In the Activity class, outside any methods:
static {
System.loadLibrary("sqrt-demo");
}
// In a method of the Activity class where you need to use it:
double d = SqrtDemo.sqrtC(123456789.0);
"
If a designer wants to place all NDK actions (loading a library,defining a function) outside of the activity, can she/he?
I tried to solve this question by creating a new class and importing it into the activity. I placed the load library in the classes constructor, and I placed the method definition in the other class. The activity appears to load the library but will crash, with the error that It cannot find the function
Edit to add details:
I attempted this again but by placing the loadLibrary function back in the Activity. I get the same crash report:
java.lang.UnsatisfiedLinkError: Native method not found:
It appears I have found the issue, it was an overlook on my part.
You can load the library from the imported class, and you can place the method definition in the the imported class. YOU MUST however modify the function as so:
initial header: JNIEXPORT jobjectArray JNICALL Java_com_stackoverflow_MainAcitivty_helloWorld(){
to JNIEXPORT jobjectArray JNICALL Java_com_stackoverflow_newclass_helloWorld(){
Related
it's known that jni function usually named as follows (from Oracle docs):
Dynamic linkers resolve entries based on their names. A native method name is concatenated from the following components:
the prefix Java_
a mangled fully-qualified class name
an underscore (“_”) separator
a mangled method name
So method's name always should contain corresponding Java class name and cannot be called from outside of this class. In case u're call this method from other class Android studio inspector gives the warning like this:
Reports native method declarations in Java where no corresponding JNI function is found in the project.
And if u run that u will receive java.lang.UnsatisfiedLinkError: Native method not found exception.
And thus my question is: how to create shared library with native functions, which could be called from any Java class?
To be more concrete. I have Java class MyActivity. And i need to call some jni function from this class. As i understand according Oracle doc that function should be named smth like Java_xxx_yyy_zz_MyActivity_func1. Then imagine, that i also want to call this function from another android app. I copy my lib*.so to libs folder but i will not be able to call my func1, coz native function will not be found. For possibility of using that function in new app i need to create the same folders hierarchy that is xxx/yyy/zzz and class MyActivity there and after that i will be able to call func1 but only from that MyActivity class. But what should i do if i want to call that fron another class?
-- new answer, given question edits --
Thanks for the clarification. AFAIK, there is no automatic way to associate the same native method with multiple Java methods. However, there are a couple workarounds that you might find useful:
(1) Call a shared facade. Assuming you don't need to pass the Java instance object to the method, first declare a facade class as in my original answer, and implement it in C:
public class JniStuff {
public static native void method1(String s, int i);
}
JNIEXPORT void JNICALL Java_net_redpoint_scratch_JniStuff_method1
(JNIEnv *, jclass, jstring, jint);
then call it from any other class you want:
public class MyClass {
public static void method1(String s, int i) {
JniStuff.method1(s, i);
}
}
(2) Use the RegisterNatives call to associate the one C method with multiple java methods, as explained here. However, I doubt that you will find this to be convenient as the registration must be done from "C", so you would also need some startup method that is called first to do that.
--- old answer --
I'm not sure what you are after, exactly. If your problem is that you don't want to create an object containing the methods, just declare them static:
package net.redpoint.scratch;
public class JniStuff {
public static native void method1(String s, int i);
}
javah then generates this native method signature:
JNIEXPORT void JNICALL Java_net_redpoint_scratch_JniStuff_method1
(JNIEnv *, jclass, jstring, jint);
Your other Java code is free to call:
JniStuff.method1("something", 123);
without instantiating an object of type JniStuff
I am trying to use the android AccountManager from qt c++ code. To add a account, I want to create an instance of android.accounts.Account, I am trying to do this with this code:
jstring jUsername = QAndroidJniObject::fromString(username).object<jstring>();
jstring jPassword = QAndroidJniObject::fromString(password).object<jstring>();
jstring jType = QAndroidJniObject::fromString(type).object<jstring>();
qDebug()<<"Creating";
QAndroidJniObject accountObject("android.accounts.Account","(Ljava/lang/String;Ljava/lang/String;)V",jUsername,jType);
qDebug()<<"Inserting";
The code segfaults at the line, where the accountObject is created ("Creating" is printed, "Inserting" not):
JNI ERROR (app bug): accessed deleted global reference 0x100e46
JNI ERROR (app bug): accessed deleted global reference 0xe46
I read this occurs, if I call a method with a wrong signature, but the signature is right (see here).
By the looks of it, the way you're creating your strings are causing your problem.
jstring jUsername = QAndroidJniObject::fromString(username).object<jstring>();
What this does is create an anonymous temporary QAndroidJniObject (returned by fromString), which you then extract the wrapped jobject from (and cast it to a jstring). By the time execution of that statement finishes the lifetime of that QAndroidJniObject is over, and the reference it held to the wrapped jobject will be released.
You could change your code to somethine like this:
auto qjUsername = QAndroidJniObject::fromString(username);
auto jUsername = qjUsername.object<jstring>();
Or to:
jstring jUsername = env->NewLocalRef(QAndroidJniObject::fromString(username).object<jstring>());
Assuming that you have a way of getting the JNIEnv*.
If you create a new reference you should probably also delete it with DeleteLocalRef when you don't need it anymore.
I want to do some initialization job in library. Any clue would be great. thanks.
A constructor of a global/static C++ object might be a good place (but don't forget about extern "C"{} around JNI methods in the .cpp file). The constructor, however, doesn't get a JNIEnv pointer and can't do anything with the Java world.
Alternatively, introduce a static native method in the class that does the loadLibrary() call, and invoke this method right after the loadLibrary() call. This is probably simpler - no need to go C++.
I'm using JNI to get the music library from Android with Qt. I call upon the following Java method (which is already implemented),
public String getArtists(Context context)
...
I need to be able to get the Context of the application in order for it to work.
If it helps, when I was using Java, the following code provided the correct context.
MainActivity.this
Could anybody be of assistance in this problem?
Many thanks!
It will depend where you are making the call. Ideally you will cache the MainActivity pointer in C++.
One way to cache a pointer to use in a later JNI call is to add a native function in java such as native void onCreateNative() to you MainActivity class. In C++ you'll implement the method and cache the "thiz" pointer:
JNIEXPORT void JNICALL com_package_MainActivity_onCreateNative(JNIEnv *env, jobject thiz)
{
gCachedActivity = env->NewGlobalRef(thiz);
}
Now you can use gCachedActivity where you would have used MainActivity.this
env->CallObjectMethod(obj, s_getArtistsGetArtistsMethodID, gCachedActivity);
Of course replacing obj with the object you are calling the method on.
Lastly onCreateNative() should be called in the onCreate() method of MainActivity.
I can succesfully load native library with System.loadLibrary(""), but when I call native method from that library, I receive UnsatisfiedLinkError, no implementation found for that method.
But this code works in another application, somehow in mine not.
Did you change the packageName in the jni method, it should be Java_com_example_yourapp_methodname(JNIEnv * env, jobject thiz) where com_example_yourapp is the reference to your package of the class you call the method.
and after that, did you call ndk-build again?
The method Erik N is suggested fine, but it has some performance impact as the VM need to search a function call with the above signature. Instead you can map the functions with signatures and register them on JNI_OnLoad() function call.
http://docs.oracle.com/javase/1.4.2/docs/guide/jni/spec/functions.html#wp5833