您的位置:首页 > 移动开发 > Android开发

Android NDK学习之四、 Android.mk实例和NDK实用技巧

2015-11-27 18:11 423 查看


例1:JNI程序使用由单个.c文件生成的动态库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程序使用多个.c文件生成的动态库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)

# 第一阶段,生成静态库: libB.a.

include $(CLEAR_VARS)

LOCAL_MODULE := hello-B

LOCAL_SRC_FILES := libstatic/B1.c libstatic/B2.c

include $(BUILD_STATIC_LIBRARY)

# 第二阶段, 生成动态库: libhello-jni.so

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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: