Android NDK學習 Android.mk實例和NDK實用技巧

例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文件即可。
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/..//*.c)
注意:此處NDK版本爲NDK R7C.(不同NDK版本,ndk-build所產生的Makefile並不完全相同)

原文出處:http://blog.sina.com.cn/s/blog_602f877001014kgj.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章