I was recently given shared libraries (.so files) to incorporate into our Android application.
Along with the shared libraries I was given a few Java classes and the following file, that offers me some Java methods to call when I want to call the library methods:
public native long doSomething(String str, int i, int i2, int i3, boolean b);
public native Result doAnotherThing(CustomObject obj);
static {
System.loadLibrary("DoSomethingLib");
}
There are, of course, a lot more methods than in the example above. After some testing I figured out, that all the methods that only receive and return base types (int, byte, boolean, String, etc). work like a charm. So the linking to the library and all is working.
However, all methods that take custom Java objects, like the second one in the example above, return null.
I am assuming that the library is not able to handle these custom Java objects correctly.
How does the .so library know how to treat the custom Java objects that I pass to the native calls? Am I missing something? Do I have to tell the .so library where it can find my Java objects somehow? Or should all of this be handled inside the library itself?
Is there a way to find out why the library is not happy with the objects I pass to it?
I do not have access to the library source code.
Related
I'm researching using libbitcoin for a project I'm going to work on, and interested in using Djinni to create the glue code for ObjC/Java for iOS and Android, respectively.
It appears there are many global functions in libbitcoin, inside a namespace, but not inside a class. I have only seen Djinni to generate a few types (classes, enums, flags, records) but I have not been able to find if a global function can be accommodated.
Is this doable? Thanks in advance.
It is not feasible since djinni produces Java bindings and there are no global functions in Java.
What you might do is to create a class with a set of static methods that will forward calls to global functions:
Bitcoin = interface +c {
static callGlobalFun1();
static callGlobalFun2(param: string);
static callGlobalFun3(): string;
}
I have a JNI library that I've written to capture sensor (mostly accelerometer and gyro) data and do some feature detection algorithms on said data. The features detected are configurable via a few configuration files. When the features are detected, the JNI uses a callback to notify the java side of the application. All this works great.
Now I want to be able to have multiple instances of the same JNI library running simultaneously (so I can recognize features from multiple configuration files at once). To do this, I wrote a 'wrapper' class that implements the callbacks for the JNI library and takes care of all the initialization of the library as well. I was planning on simply instantiating this class and using each instance separately. What I've found is that while each wrapper instance is distinct, the library is reused across instances almost like it was statically declared. When I try to initialize the library from the second instance of the Wrapper class, I find it has already been initialized.
Here is a wrapper class similar to the code I've written:
public class JNIWrapper {
public native int initializeJNI(String configPath);
public native void endProcessing();
public native int getInstanceIdFromJNI();
public JNIWrapper(){
try {
System.loadLibrary("libjnicode.so");
}
catch (Exception e) {
Log.e("JNI", "WARNING: Could not load libjnicode.so: " + e.getMessage());
}
}
public int initialize(String configPath){
return initializeJNI(configPath);
}
public void stop(){
endProcessing();
}
public void callbackFromJNI(int output, int instanceId){
//notify the subscribed application(s) of the feature detection
//via message passing.
}
}
Does anyone know how I can instantiate multiple copies of a JNI library?
Thanks!
You can't do that. The dynamic linker will only load a given .so file into a process once.
Ideally you would modify the library to give it a light object-oriented style, allowing you to create instances and initialize those (rather than process-level static state) from your configuration files or calls. This isn't necessarily as complicated as it seems - basically put all your state in a struct and pass the pointer to it through all your calls. You'll have one marathon editor session resulting in a tired "paste" finger, and then some mistake cleanup. Fortunately once you remove the static variables you'll get compile errors on all remaining attempts to use them.
A very hacky alternative might be to declare some remote-process services in your AndroidManifest.xml and load the library into each of those. Or, really breaking the android model (and at theoretical risk of random killing), load the library into multiple created-on-demand native executables.
I have a android application, say 'OldApp', which uses some native methods within itself, and I have to use it as a library for a new app, say 'NewApp'.
This is the first time i work with this request and i have a doubt:
the OldApp has some classes, and one of these is the JNI Wrapper and contains the native methods, for example:
public class LibOldApp {
private static native int method1();
....
....
}
Can i use all the classes/methods of the OldApp or only the wrapper, and native methods?
thank you..
Yes sure you can use.
just use your old app as library for your new project.
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);
I have the follow scenario to work on. I was given a shared library (libeffect.so) to use in a Android project i am working for a client. I dont have the shared library source code, i have just the .so file with me. The library is pre-compiled to work on android devices. Along with the shared library I have the method signature
public static native void doEffect(int param1, IntBuffer intBuffer);
So now I have some questiosn on how to make the call to this native method, of source, if this is possible having just the .so file, so there they are:
Do I need to place the native method signature in the same package/class as those defined when the .so was or I can use this signature in any package/class in my project that during runtime the jvm will be able to find the method in the shared library? For example, if this shared library was firstly used in a class mypackage.MyClass, do I need to create the same package, class and then put the method signature there?
Where do I need to place this .so file inside my eclipse android project to get this file deployed inside my apk file?
These question might sound noob, but I have never worked with jndi before so I am a bit concerned if calling the method doEffect without any error can be achieve. Any answer that can guide me is very welcome.
Many Thanks
Thiago
Do I need to place the native method signature in the same package/class as
those defined when the .so was or I
can use this signature in any
package/class in my project that
during runtime the jvm will be able to
find the method in the shared library?
For example, if this shared library
was firstly used in a class
mypackage.MyClass, do I need to create
the same package, class and then put
the method signature there?
No need to create same package/class. You can put the method signature in any package.
public class NativeLib {
static {
System.loadLibrary("so_file");
}
public static native void doEffect(int param1, IntBuffer intBuffer);
}
2.Where do I need to place this .so file inside my eclipse android project
to get this file deployed inside my apk file?
You have put this .so file in lib folder of your application . IF lib folder is not there then you can create a lib folder and put the .so file. you can call it by using System.loadLibrary("so_ file");
1 Do I need to place the native method signature in the same
package/class as those defined when the .so was or I can use this
signature in any package/class in my project that during runtime the
jvm will be able to find the method in the shared library?
According to http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/design.html you have to use a matching package and class name.
I've only observed JNI methods where the C side functions are called things like Java_com_company_whatever_SomeClass_someMethod, which means that you have to put the 'native' declarations in a similarly-named Java class.
Use the tool 'nm' or 'nm++' (they're in the precompiled folders in the NDK) to look at the .so file and see what the functions defined in it are called. If you see any starting Java_, those're what you want.
I'm sceptical of the preceding claim that you can call functions which aren't named in the Java_PACKAGE_CLASS_METHOD format; it may be a legacy behaviour if it actually works, but even if you can, it seems dangerous - you might get the wrong one.
2 Where do I need to place this .so file inside my eclipse android
project to get this file deployed inside my apk file?
Your .so lives in libs/armeabi, libs/armeabi-v7a, libs/x86, and/or libs/mips depending on how many platforms you're working with, where 'libs' is a peer of 'src' and 'res'. I don't know whether Android looks in libs/ without the platform qualifier, but there's no evident benefit in that. The situation is slightly complicated by most/all Intel devices including fancy technology allowing them to execute most ARM libraries on x86 hardware.
Further, I like to declare an interface of a JNI class and provide a factory (it's a method here for brevity, but I prefer a factory class) that supplies a no-op implementation of the interface if things go wrong: it facilitates unit testing and also avoids having to mess about testing for null values before calling its methods (assuming you're comfortable that your shipped library will never have missing or changed method signatures - your integration tests should check that):
public interface YourLibI {
#Override
public native yourMethod();
public static final NO_OP = new YourLibI() {
#Override
public void yourMethod(){}
}
}
public class YourLib extends YourLibI {
public newYourLibI() {
try {
return new YourLib();
}
catch (UnsatisfiedLinkError e) {
Log.e("YourLibJNI", "Load failed, returning NO-OP dummy", e);
return YourLibI.NO_OP;
}
}
static {
System.loadLibrary("arbitronSDK");
}
private YourLib() {
}
#Override
public native void yourMethod();
}
I don't normally call interfaces 'xxxI' but I'm assuming your library's JNI class isn't called something nice like UtilityJNI (whereupon I'd call the interface 'Utility').