例1:JNI程序使用libhello-jni.so的符號。
libhello-jni.so由hello-jni.c組成。
hello-jni.c如下:
#include
#include
#include
#define LOG_TAG "libhello-jni"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
void Java_com_example_hellojni_HelloJni_functionA(JNIEnv* env, jobject thiz)
{
LOGE("SamInfo: Enter Native functionA");
return;
}
Android.mk如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
../../../ndk-build -B V=1
可以正常編譯,再使用Eclipse編譯Android工程,可正常運行。
例2:JNI程序使用libhello-jni.so的符號
libhello-jni.so由hello-jni.c, hell-jniB.c,頭文件hello-jni.h組成。
hello-jni.h如下:
#ifndef _HELLO_JNI_H
#define _HELLO_JNI_H
#include
#include
#include
#define LOG_TAG "libhello-jni"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#endif
hello-jni.c如下:
#include "hello-jni.h"
void Java_com_example_hellojni_HelloJni_functionA(JNIEnv* env, jobject thiz)
{
LOGE("SamInfo: Enter Native functionA");
return;
}
hell-jniB.c如下:
#include "hello-jni.h"
void Java_com_example_hellojni_HelloJni_functionB(JNIEnv* env, jobject thiz)
{
LOGE("SamInfo: Enter Native functionB");
return;
}
Android.mk如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c hell-jniB.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
注意:LOCAL_SRC_FILES := hello-jni.c hell-jniB.c
此模塊hello-jni由兩個C文件組成,因爲hello-jni.h只是依賴文件,所以不必加入。
又因爲hello-jni.h在project/jni目錄中,此目錄本身爲-I,所以也不用再Android.h中指出。
例3:JNI程序使用 libhello-jni.so的符號
libhello-jni.so依賴於libB.a.
libhello-jni.so由hello-jni.c, hell-jniB.c,頭文件hello-jni.h組成。
libB.a由libstatic/B1.c,libstatic/B2.c頭文件libstatic/B.h組成。
B.h 如下:
#ifndef _B_H
#define _B_H
#include
#include
#include
int iFunctionB1();
int iFunctionB2();
#define LOG_TAG "libStatic"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#endif
B1.c:
#include "B.h"
int iFunctionB1()
{
LOGI("SamInfo: Enter static function iFunctionB1()");
return 0;
}
B2.c
#include "B.h"
int iFunctionB2()
{
LOGI("SamInfo: Enter static function iFunctionB2()");
return 0;
}
hello-jni.h:
#ifndef _HELLO_JNI_H
#define _HELLO_JNI_H
#include
#include
#include
#include "libstatic/B.h"
#define LOG_TAG "libhello-jni"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#endif
hello-jni.c:
#include "hello-jni.h"
void Java_com_example_hellojni_HelloJni_functionA(JNIEnv* env, jobject thiz)
{
LOGE("SamInfo: Enter Native functionA");
iFunctionB1();
return;
}
hell-jniB.c:
#include "hello-jni.h"
void Java_com_example_hellojni_HelloJni_functionB(JNIEnv* env, jobject thiz)
{
LOGE("SamInfo: Enter Native functionB");
iFunctionB2();
return;
}
Android.mk如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-B
LOCAL_SRC_FILES := libstatic/B1.c libstatic/B2.c
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c hell-jniB.c
LOCAL_STATIC_LIBRARIES := hello-B
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
這就是典型的一個Android.mk中包含2個Modules的例子。其中一個是產生靜態庫,另一個產生動態庫。
動態庫依賴於靜態庫。(一定要添加 LOCAL_STATIC_LIBRARIES := hello-B ,否則不生成靜態庫)
例4:JNI程序使用libA.so(由A1.c,A2,c,A.h組成)
libA.so依賴於libB.so(由B1.c,B2.c,B.h組成)
情況分析:
因爲libB.so被libA.so使用。所以肯定要加入:
LOCAL_LDLIBS := -L$(LOCAL_PATH)/../libs/armeabi/ -lhello-B
但請注意:此時libA.so中所用到的libB.so 的符號只是一個空穴。併爲將實現加進來。
而如果使用:
LOCAL_SHARED_LIBRARIES := hello-B
也僅僅是將libhello-B.so 添加進編譯選項。並未將符號添加進去。
在Linux下,此類情況可以將動態庫放置於某個目錄下,然後使用export LD_LIBRARY_PATH 來解決。但Android下並無此方法。導致運行時會找不到libhello-B.so中的符號。
針對此類情況,有兩種方法,但都不是特別好用。分別描述如下:
方法1.
將libhello-B.so放置於/system/lib下。
且Android.mk如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-B
LOCAL_SRC_FILES := libstatic/B1.c libstatic/B2.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c hell-jniB.c
#LOCAL_SHARED_LIBRARIES := hello-B
LOCAL_LDLIBS := -llog -L$(LOCAL_PATH)/../libs/armeabi/ -lhello-B
include $(BUILD_SHARED_LIBRARY)
此Android.mk有兩個模塊,分別產生libhello-B.so, libhello-jni.so. 後者依賴於前者。
而因爲Java層程序使用:System.loadLibrary("hello-jni");
則libhello-jni.so的符號加入到Java應用程序中。而它使用到的libhello-B.so,則能夠在/system/lib下找到。
方法2. 將libhello-B.so也添加如Java程序中。且放置於hello-jni之前。
System.loadLibrary("hello-B");
System.loadLibrary("hello-jni");
方法3:
可以使用dlopen()方式調用。
據說使用-rpath可以指定應用程序查找庫的目錄。但Sam覺得理論上並不可能(因爲Java無法指定Wl,-rpath).也沒有嘗試出來。
例5:JNI程序使用libA.so(由A1.c A2.c, A.h組成)
libA.so依賴於libB.so(由B1.c, B2.c, B.h組成)
libB.so依賴於libC.so(由c.c組成)和libD.a(由d.c組成)
libC.so依賴於libD.a (由d.c組成)
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-B
LOCAL_SRC_FILES := libstatic/B1.c libstatic/B2.c
LOCAL_STATIC_LIBRARIES := D
LOCAL_LDLIBS := -llog -lC -L$(LOCAL_PATH)/../libs/armeabi/
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c hell-jniB.c
#LOCAL_SHARED_LIBRARIES := hello-B
LOCAL_LDLIBS := -llog -L$(LOCAL_PATH)/../libs/armeabi/ -lhello-B
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := C
LOCAL_SRC_FILES := c.c
LOCAL_LDLIBS := -llog
LOCAL_STATIC_LIBRARIES := D
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := D
LOCAL_SRC_FILES := d.c
LOCAL_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)
請注意:如此寫法:則libD.a中的符號分別被:libC.so, libB.so兩次引用。
例6: JNI程序使用libA.so(由A1.c A2.c, A.h組成)
並引用外部生成的:libE.so, libF.a.
Sam:經常發生這樣的情況,某NDK工程A生成一個動態庫,供另一個NDK工程B使用。我們常把此動態庫放到B工程的lib/armeabi下。 但ndk-build時,它會首先刪除lib/armeabi下所有.so文件。
所以可以使用 PREBUILT_SHARED_LIBRARY
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-B
LOCAL_SRC_FILES := libstatic/B1.c libstatic/B2.c
LOCAL_SHARED_LIBRARIES := prelib
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c hell-jniB.c
#LOCAL_SHARED_LIBRARIES := hello-B
LOCAL_LDLIBS := -llog -L$(LOCAL_PATH)/../libs/armeabi/ -lhello-B
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := prelib
LOCAL_SRC_FILES := libE.so
include $(PREBUILT_SHARED_LIBRARY)
注意:當 PREBUILT_SHARED_LIBRARY 時,LOCAL_SRC_FILES不再是.c文件,而是動態庫。它可以放置在jni下。編譯時,它會自動被copy到lib/armaebi下。
請注意模塊名。它將被用作LOCAL_SHARED_LIBRARIES
注1:
NDK實用技巧:
1. 顯示NDK Build過程中所有編譯選項和動作:
../../ndk-build V=1
這樣就可以看到編譯時所用編譯選項是否我們期望使用的。
2.重新編譯:
../../ndk-build -B
或者:
../../ndk-build clean
../../ndk-build
第三方庫中編譯方法:
Sam常需要NDK編譯第三方庫,但又不想破壞其原有目錄結構.所以通常在src目錄中,添加一個目錄叫:jni.
在其中添加Android.mk, Application.mk文件。 Source Code 還是繼續使用原有src目錄下的源碼文件。
在Android.mk中,只需要添加上一級目錄的所有.c或者.cpp文件即可。