calling jni function from outside of corresponding java class - android

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

Related

Android NDK, and placing function outside activity class

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(){

Android JNI/NDK Application Context

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.

JNI functions in Android webkit source

I read that JNI functions (the native C part) is very complex and must contain the java package name.
However, when reading Android webkit source. For example the nativeMoveGeneration functions in WebView.java
private native int nativeMoveGeneration();
It calls the JNI functions in WebView.cpp
static int nativeMoveGeneration(JNIEnv *env, jobject obj)
{
WebView* view = GET_NATIVE_VIEW(env, obj);
if (!view)
return 0;
return view->moveGeneration();
}
Ihis JNI function does not follow naming rule. Why is it?
P/S: The function above is just for demonstration. I'm reading Android 4.0.3 source, so it may be different from the github source above
UPDATE
Thanks to #Alex Cohn and this JNI Tips, I know that we can use JNI_Onload or use complex name. But where should we put JNI_Onload ?
JNI defines special function, JNI_OnLoad. It is called before any JNI method is called, and it can populate the table of native methods using pointers to any C functions. See the official document

FindClass and GetMethodID failes on android jni

I have c++ lib used with my application. I passed java object to jni and saved it to global reference. Then, I wish to call method of this java object from jni from antoher thread (I use pthread).
Java class is:
public class WaitingServiceReadyCallback {
public void ready(String serviceName) throws Exception { ... // some code }
}
To call java method I use next code:
jvm->AttachCurrentThread(&env, 0);
cls = env->GetObjectClass(__obj__); // __obj__ is global reference to object.
if (!cls)
goto detach;
mid = env->GetMethodID(cls, "ready", "(Ljava/lang/String;)V");
There GetMethodID fails to find method.
When I use
cls = env->FindClass("com/mypackage/WaitingServiceReadyCallback");
instead of GetObjectClass, FindClass fails to find this class.
I tried to check class name of the object referencd by my __obj__ global reference (used getName from com/java/Class, made call to getName in the same place of my code as above call to ready), I got right class name - com.mypackage.WaitingServiceReadyCallback.
I am sure that class exists and loaded (java code executed before jni and instance of this class is created there), I am sure that method exists in the class.
So, I can't understand, what I done wrong?
I met this problem. The reason in short: within another thread VM does not provide us an info about loaded classes. I've solved this by storing the classloader of some sample java object and then using it for manual loading of needed classes from another threads.

cannot use external JNI function in two different classes, unsatisfied link error

For an Android application, I have implemented an external function in C, which I would like to use in two separate classes.
In the first class (my main Activity UI), I call the appropriate loadLibrary:
System.loadLibrary(...);
In the same class, I define the function as native:
public native int dissectPacket(byte[] header, byte[] data, int encap);
After doing this, I can call the native function with no problem in the first class. I do not get any unsatisfied link error.
Now, I want to use this function in another class. I figure I do not need to load the library again. In the second class, at the bottom, I also define:
public native int dissectPacket(byte[] header, byte[] data, int encap);
However, when I try to use the native function in the second class, I get:
07-22 23:13:13.083: ERROR/AndroidRuntime(6737): Caused by: java.lang.UnsatisfiedLinkError: dissectPacket
What is the proper way to use the function in both classes? If I do not redefine the function as native in the second class (called Packet), I get the error:
The method dissectPacket(byte[], byte[], int) is undefined for the type Packet
BTW, I do NOT want to use: class1.dissectPacket(...); I am trying to avoid passing the class.
You defined actually two separate functions. One for the first class and another one for the second. They will need two separate JNI stubs. You, probably, only have stub and implementation for the first one.
JNI and Java, in general, always refer to methods of the specific class.
"BTW, I do NOT want to use: class1.dissectPacket(...); I am trying to avoid passing the class."
If you want to do that, the member functions need to be static, otherwise the class is implicitly passed as a parameter (I don't know how because I've never done it, static functions have always worked for me, but it has to happen to work properly).
So change your method stubs to:
public static native int dissectPacket(byte[] header, byte[] data, int encap);

Categories

Resources