Android:NDK
lastmod 2023-04-03created 2014-03-1216 edits
The NDK(Native development kit) is a toolset that allows you to implement parts of your app using native-code languages such as C and C++. For certain types of apps, this can be helpful so that you may reuse existing code libraries written in these languages and possibly increased performance.
Category
NDK Example
- NDK Samples
- Iconv:Android:NDK: Android NDK with iconv.
- FreeType:Android:NDK: How to use FreeType in the Android NDK.
JNI
ETC
- Android:Debugging:Wireless - how to debug Android wirelessly
How to compile
Compile with the NDK as follows. (Note that the working directory is the directory containing .project, based on the Eclipse project.)
The important point (with the default settings) is that the directory containing the Makefile is, by default, the ${workspace_loc:/projectname}/jni/ directory, and the default file name is Android.mk.
With the default settings, the output files (Library files, Object files) are generated in the ${workspace_loc:/projectname}/libs/ and ${workspace_loc:/projectname}/obj/ directories, respectively. (${workspace_loc:/projectname} denotes the Root directory of the project corresponding to projectname in Eclipse.)
Using STL in the Android NDK
- http://www.41post.com/3527/programming/import-stl-libraries-to-android-ndk-code
- http://blog.naver.com/PostView.nhn?blogId=websearch&logNo=70144232656
After creating the Application.mk, add this line of code:
Debugging the Android NDK
To keep debugging information on the C/C++ side of the Android NDK, add the following to the APPLICATION.mk file and set debuggable="true" in the AndroidManifest.xml file.
Linking libraries in multiple folders
During a project you sometimes need to link libraries split across multiple folders, but the NDK samples and other code all link only libraries in a single folder. The way to do it is to list all the folders you want in LOCAL_LDLIBS, as below.
# libadd.so 는 LibTest1/libs/armeabi 에
# libsubtract.so 는 LibTest2/libs/armeabi 에 있다고 가정합니다.
LOCAL_LDLIBS := -L$(call host-path, $(LOCAL_PATH)/../../LibTest1/libs/armeabi) \
-L$(call host-path, $(LOCAL_PATH)/../../LibTest2/libs/armeabi) \
-ladd -lsubtract
One caveat when doing this is that static library linking works fine, but dynamic library linking produces link errors at runtime. This is because dynamic library linking only checks simple information at build time, so the actual module has to be loaded separately (using the System.loadLibrary() method).
The $(~ProjectDir)/libs/armeabi folder of the project that actually uses the libraries is entirely deleted when ndk-build runs, so after the build finishes, copy all the Dynamic libraries into the $(~ProjectDir)/libs/armeabi folder. It would be convenient to have a shell script copy them automatically.
Building a static library with the NDK
Most examples that use the NDK build dynamic libraries. Occasionally, though, you need a static library. So this time, let's look at how to build a static library and a few points to watch out for. For the basic way to build a static library, refer to the $(NDK)/samples/two-libs example. The two-libs makefile looks like this.
LOCAL_PATH:= $(call my-dir)
# first lib, which will be built statically
include $(CLEAR_VARS)
LOCAL_MODULE := libtwolib-first # static library 이름
LOCAL_SRC_FILES := first.c # static library 에 빌드될 소스 파일
include $(BUILD_STATIC_LIBRARY) # static library 빌드
# second lib, which will depend on and include the first one
include $(CLEAR_VARS)
LOCAL_MODULE := libtwolib-second # dynamic library 이름
LOCAL_SRC_FILES := second.c # dynamic library 에 빌드될 소스 파일. 빌드할 소스가 없다면 생략될 수 있다.
LOCAL_STATIC_LIBRARIES := libtwolib-first # 같이 빌드할 static library 이름
include $(BUILD_SHARED_LIBRARY) # dynamic library 빌드
As you can see above, it is split into a part that first builds the static library and a second part that builds the dynamic library. The caveats are as follows.
- Even if you only build a static library, a part that builds a dynamic library must be present.
- In this case, the dynamic library part does not need a LOCAL_SRC_FILES entry.
- The code in the static library part must not contain JNI-related code.
- To use JNI-related code, the dynamic library has to be loaded and used from the Java layer, so if it goes into the static library, link errors occur at runtime.
- Therefore, common general-purpose modules should be built as static libraries and JNI-related parts as dynamic libraries.
- After the build, the dynamic library file (.so) is copied to the
$(ProjectDir)/libs/armeabifolder, while the static library file (.a) remains in the$(ProjectDir)/obj/local/armeabifolder.
If a previously built Dynamic (Shared) library exists, specify the *.so file as the LOCAL_SRC_FILES value.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := libffmpeg.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/ffmpeg
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg-bypasser
LOCAL_SRC_FILES := ffmpeg-bypasser.c ffmpeg-command.c cmdutils.c util.c
LOCAL_SHARED_LIBRARIES += ffmpeg
LOCAL_LDLIBS := -llog
LOCAL_CFLAGS := -Wno-deprecated-declarations
include $(BUILD_SHARED_LIBRARY)
How to use --sysroot
Gives a hint on how to use --sysroot.
Getting the Android Context
...
// Passes cache directory path from native code to GDScript
godot_variant android_gdnative_test(godot_object *p_instance, void *p_method_data
, void *p_user_data, int p_num_args, godot_variant **p_args)
{
// Get JNIEnv* from my function that extends godot_gdnative_core_api_struct
JNIEnv* env = api->godot_android_get_env();
// Get context - see https://stackoverflow.com/questions/46869901/how-to-get-the-android-context-instance-when-calling-jni-method
jclass activityThread = env->FindClass("android/app/ActivityThread");
jmethodID currentActivityThread = env->GetStaticMethodID(activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;");
jobject at = env->CallStaticObjectMethod(activityThread, currentActivityThread);
jmethodID getApplication = env->GetMethodID(activityThread, "getApplication", "()Landroid/app/Application;");
jobject context = env->CallObjectMethod(at, getApplication);
// Get path to cache directory
jclass contextClass = env->FindClass("android/content/Context");
jclass fileClass = env->FindClass("java/io/File");
jmethodID getCacheDir = env->GetMethodID(contextClass, "getCacheDir", "()Ljava/io/File;");
jmethodID getAbsolutePath = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;");
jobject file = env->CallObjectMethod(context, getCacheDir);
jstring str = (jstring)env->CallObjectMethod(file, getAbsolutePath);
const char *cacheDir = env->GetStringUTFChars(str, 0);
// Pass cacheDir to GDScript
...
}
...
Documentation
- Application.mk translation
-
Application-mk_ko.txt
See also
Favorite site
- NDK Overview
- Android NDK download page
- Sample: hello-jni | Android NDK | Android Developers
- Eclipse CDT JNI Project
- How to develop with the Android NDK and C++
- Hello Android NDK example
- Fixing errors when using Threads in a JNI environment
References
- Android
- Android:Example
- Android:Example:NdkOpenGLESGeometry
- Android:NDK:Debugging
- Android:NDK:OpenGLES
- Android:NDK:Sample
- Android:NDK:Toolchains
- Android:NDK:Troubleshooting
- FFmpeg:Android
- FreeType
- FreeType:Android:NDK
- Godot-mobile-midi
- Iconv
- Iconv:Android:NDK
- Java
- Java Native Interface
- Java:Example
- JNI:DataByPass
- JNIEnv
- Sequoyah
- Simple and Fast Multimedia Library