I am writing an Android application. I am implementing some of the app in C++ using JNI. Is there a way to actually separate completely the JNI (C++) code from the Java Android code? Like creating a JNI project or something ? Because my C++ code will become a little library that I will use in another app. I tried to create a C++ project but I can't compile with ndk-build.
In actuality, the tie-in between the Java and native code is fairly loose, in that it is done by the names of the methods at runtime.
The NDK is just a special environment and libraries for a C compiler. A suitable dynamic library file must be produced, but this can be done using the ndk script, using an ndk generated "stand alone tool chain" or even done before (or after, see later) the java project build, done on another machine, etc.
Normally the .so dynamic library gets copied into the appropriate place in the application project tree such that it will end up packaged in the .apk and installed on the device where it can be automatically found by the System.loadLibrary() method. But you could just insert it into the otherwise finished .apk (which is a zip file with special alignment and jarsigner signatures). But even the .pak is not actually not the only way to deliver it to the device (though it is the only recommended way)- a library can also be stored in a place such as the app's private folder and loaded using System.load() which takes a full pathname instead of a library name.
hotverspicy does have a point that the java code needs a native method "declaration" for what is implemented in the jni library, and that this needs to match the actual package name as encoded in the name of the native function implementation. However, that package name does not have to be the same as the rest of the java code - it could be something generic for the re-usable C/C++ library, which would require only one brief matching java class file in the matching java package name.
But in addition to Neevek's idea of using one native lib to interface to another, it is also likely possible to use an object factory mechanism to hook up arbitrary native functions at runtime, given just one association set up at compile time - see the relevant answer to C/C++ Reflection and JNI - A method for invoking native code which hasn't been written yet
If you use JNI in your C++ code, then it's bound to Android, and it must be an Android project with properly defined Android.mk, Application.mk(optional).
If you don't use JNI API in your code, say you want to create a generic library, you can just write your C++ code, compile it, test it as usual, no ndk-build is needed. When you finish coding with the library, build the code as a static library, and when you want to use this static library in your Android project, write some JNI wrappers around functionalities in the static library that you want to use, link against the static library in your Android.mk, something like this:
include $(CLEAR_VARS)
LOCAL_MODULE := libgeneric
LOCAL_SRC_FILES := prebuilt/libgeneric.a
include $(PREBUILT_STATIC_LIBRARY)
Related
I am trying to use the .so external library in my Android project. It's not clear to me how can I access the classes in the library once it's loaded via System.load("path to lib")?
The library is already there, and it's already compiled for use on mobile devices.
The loading process does not cause errors.
Due to the specifics of the project, I cannot use .so files from the unpacked apk along the standard path - I need to connect it in the code via System.load("path to lib").
Please, tell me, what is the syntax for referring to classes in a loaded library?
Create method in one of the your class like below
public static native String methodName();
Now you can access classes in cpp file using below syntax
Java_your_package_name_classname_methodName(JNIEnv *env, jclass clazz) {
return something
}
Get your something by calling methodName. Thats it.
for more information refer https://developer.android.com/studio/projects/add-native-code
Android can load arbitrary .so libraries via System.load() that is correct (if the architecture is correct). But that a library can be loaded does not mean you can use it from within your app.
Android bases on Java and Java uses a special system for calling methods in native libraries/.so files: Java Native Interface (JNI).
Based on the Java method name and it's parameters JNI defines the function name and it's parameters in the .so file and exactly this way you have to implement this function. JNI also provides helper functions for converting typical Java types into C types and the other way around.
What does that mean for you:
Assuming the library is not prepared for JNI and you have the source code of the used library you can add the necessary JNI functions yourself.
Otherwise you don't have the source code of the used .so library and the library does not provide the required JNI functions you have to develop your own .so library that acts as a bridge from Java-JNI to the exported functions of the .so file you want to call.
I am trying to reference native libraries from within an NDK project using CMAKE, but I am not having much luck. My first attempt was to simply add the include for the native library I was interested in. (for example)
#include <multinetwork.h>
However, that didn't work it simply said file not found. So, would it be something I put in the CMakeList.txt? Just to be clear I want to reference native library methods/classes DIRECTLY from my C++ code in my NDK project.
P.S: (READING PAST HERE ISN'T NESSASARY IT JUST ADDS MORE COLOR ON WHAT I AM GOING AFTER) I feel like I should share my true objective is to gain access to things not allowed through the application framework. I want to override/use classes in the native libraries that I am not given access to. I am hoping I don't have to recompile kernal to do this.
Try to use:
#include <android/multinetwork.h>
I am using a number of static pre-built static libraries in my native android application and everything works fine. Now I want to switch one of my static libraries to be .so. I was successfully able to build .so library by replacing BUILD_STATIC_LIBRARY with BUILD_SHARED_LIBRARY in its android.mk and adding required dependencies.
I was also able to build my application by replacing corresponding PREBUILT_STATIC_LIBRARY with PREBUILT_SHARED_LIBRARY in its android.mk. The resulting application now fails to start. I cannot even get to point where debugger attaches to the application.
Besides that what I do not understand is how the build system knows that the function should be imported from the library. My so library should export one function, but I did not declare it as dllexport/import or something. Still there are no unresolved symbols in my application (when I remove my prebuilt library from the list, the unresolved symbol appears as expected).
The other question is that I see there are two .so files generated. One big file in obj/local/$(TARGET_ARCH_ABI) folder and another small one in libs/$(TARGET_ARCH_ABI). When declaring my prebuilt library I reference the second one in libs folder.
I did try to search stackoverflow for answers and found quite a few related posts:
loading library (.so file) in android
NDK - How to use a generated .so library in another project
How to use .so file in Android code to use the native methods
How to use libffmpeg.so in Android project?
but I do not see how these posts related to my problem since I can successfully build and even link my application.
You need to load the libraries in reverse dependency order in the java code. You previously probably have something like this:
System.loadLibrary("mylib");
Now if your prebuilt library (that was previously a static library, now a shared library) is named dependencylib, you need to change the code for loading the libraries into this:
System.loadLibrary("dependencylib");
System.loadLibrary("mylib");
As for your question how the linker can figure it out; when linking libmylib.so, it looks for all undefined symbols in all the other libraries you specified (i.e. in libdependencylib.so, and in libc.so and other system libraries). As long as all undefined symbols are found somewhere, the linker is ok. Then at runtime, when libmylib.so is loaded, it does the same routine again; all undefined symbols are looked up in the list of symbols loaded in the current process. On linux, you normally don't need to manually mark symbols as dllexport as you do on windows - all non-static symbols are exported by default.
There may be two reasons why the app fails to start after the change of STATIC -> SHARED.
The prebuilt library is not installed. With your device connected, run adb ls -l /data/your.package.name/lib/. Do you see the library there?
The prebuilt library is not loaded. In your main Java class, try
static {
System.loadLibrary("prebuiltname");
System.loadLibrary("yourlib");
}
This is a static constructor, the safest place to load JNI library.
If you are on linux you will see exported symbols using nm -D. example nm -D libzip.so:
...
0000000000009dc0 T zip_unchange
0000000000009dd0 T zip_unchange_all
0000000000009e30 T zip_unchange_archive
0000000000009e60 T _zip_unchange_data
If you want to control visibility of your functions use __attribute__ ((visibility ("default"))) and command line -fvisibility=hidden. More information here.
Now I want to switch one of my static libraries to be .so. I was successfully able to build .so library by replacing BUILD_STATIC_LIBRARY with BUILD_SHARED_LIBRARY in its android.mk and adding required dependencies.
I don't think you can do it if its a C++ library. From <doc>/CPLUSPLUS-SUPPORT.html:
Please keep in mind that the static library variant of a given C++
runtime SHALL ONLY BE LINKED INTO A SINGLE BINARY for optimal
conditions.
What this means is that if your project consists of a
single shared library, you can link against, e.g., stlport_static, and
everything will work correctly.
On the other hand, if you have two
shared libraries in your project (e.g. libfoo.so and libbar.so) which
both link against the same static runtime, each one of them will
include a copy of the runtime's code in its final binary image. This
is problematic because certain global variables used/provided
internally by the runtime are duplicated.
This is likely to result in code that doesn't work correctly, for example:
* memory allocated in one library, and freed in the other would leak
or even corrupt the heap.
* exceptions raised in libfoo.so cannot be caught in libbar.so (and may
simply crash the program).
* the buffering of cout not working properly
This problem also happens if you want to link an executable and a shared
library to the same static library.
In other words, if your project requires several shared library modules,
then use the shared library variant of your C++ runtime.
From above, it means everything needs to link against the same C++ standard runtime shared object.
We have developed an iPad application where the core logic is written in CPP code, so that we can use the same code files/libraries to other platforms.
Now I want to use those files and develop similar Android application, but unable to create .so files and integrate paths in Android.mk files and all. I am basically an iOS developer, this is first time I am looking into Android NDK.
Can anyone help and guide if there is any straight forward steps to it.
I have already gone through android developers site and few other tutorial sites. But none of those worked for me.
Require easy-clear steps to call cpp method in java, if I do have few cpp files and .a libraries with me already.
You aren't very specific at the step you are stuck at.
Here's a very quick explanation on how to call native code from java (android) :
first create a method to be exported by the native and called by java (this uses JNI, so google JNI , JNIEXPORT)
once you have this method defined in your native code, it's time to create a shared library (.so) file , using the compiler that comes in the NDK (because you are compiling for android ). You will need to compile for the correct architecture of the device (armeabiv7s is the most common now days).
you need to add the library file in your app.apk inside the armeabi folder (more details in NDK tutorials).
inside your java code you will need to load the shared library via the System.loadLibrary(LIBRARY_NAME);
inside your java code you will need to have defined static native methods that are in pair with the methods you exported from your CPP code
Quick tips :
use C functions,not CPP , since CPP will be mangled in the resulting shared library. In other words, you will need to create a C wrapper that will call your cpp code.
look over a hello world tutorial for NDK , and work yourself from there . Here's a link to such tutorial http://trivedihardik.wordpress.com/2011/06/16/hello-world-example-using-ndk-in-android/
You will bump later on into compilation issues with the makefiles, but by then you will probably be able to be more specific with your question.
Easiest way is to use the hello-jni Android studio sample project.
There are a lot of settings and configurations, you get them from the sample that is a working unit, always easiest when starting from something working.
First run (and modify) the hello-jni and learn how the interactivity between the Java and C parts works. About everything works except environmental ANSI C/C++ stuff. You have to get things like language, country etc from Java and transfer it to the C-code. You are in US in English with "inches and gallons" in JNI.
Then to an own project you create with android studio, copy and modify from it bit by bit from hello-jni. When you have our own branded hello-JNI you can add bit by bit your own code. Often using C-dummies for testing the interactivity with the Java part is easier, then change it to the real C/C++ code of yours.
Read the Android/Android studio documentation and learn and understand. Use the Android emulators, much easier and they are good.
The project configuration stuff is by far the hardest to handle at the start. If I would make a new project today, I would start from the Hello-JNI once again.
How I can do this without compile again the native code and all stuff?
It seems like you are probably doing jni to this library. If that is the case, the java package and class name needs to be encoded in the names of the jni functions, so you would have to do something such as:
Always keep the same name for the java package and class file which actually interfaces to the native library. This does not need to match the main package name for the APK. You may want to make this a sort of wrapper for the native library.
Or you could create a new native library using the new package name which is just a wrapper calling the original library by it's original function names. This will be a bit more complicated to set up than the previous idea.
Create libs/<arch> folder, and put the so file there. Eclipse or ant build system will automatically pick it up and package in apk file.
In place of <arch> use one of armeabi, armeabi-v7a, x86 or mips - depending on how .so file has been built.