一、JNIEnv与JavaVM
1. JNIEnv 概念 : 是一个线程相关的结构体, 该结构体代表了 Java 在本线程的运行环境 ;
2. JNIEnv 与 JavaVM :
JavaVM : JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局只有一个;
JNIEnv : JavaVM 在线程中的代表, 每个线程都有一个, JNI 中可能有很多个 JNIEnv;
3. JNIEnv 作用 :
调用 Java 函数 : JNIEnv 代表 Java 运行环境, 可以使用 JNIEnv 调用 Java 中的代码;
操作 Java 对象 : Java 对象传入 JNI 层就是 Jobject 对象, 需要使用 JNIEnv 来操作这个 Java 对象;
4. JNIEnv 体系结构
线程相关 : JNIEnv 是线程相关的, 即 在 每个线程中 都有一个 JNIEnv 指针, 每个JNIEnv 都是线程专有的, 其它线程不
能使用本线程中的 JNIEnv, 线程 A 不能调用 线程 B 的 JNIEnv;
JNIEnv 不能跨线程 : 当前线程有效 : JNIEnv 只在当前线程有效, JNIEnv 不能在 线程之间进行传递, 在同一个线程中,
多次调用 JNI层方法, 传入的 JNIEnv 是相同的;
本地方法匹配多JNIEnv : 在 Java 层定义的本地方法, 可以在不同的线程调用, 因此 可以接受不同的 JNIEnv;
JNIEnv 结构 : 由上面的代码可以得出, JNIEnv 是一个指针, 指向一个线程相关的结构, 线程相关结构指向 JNI 函数指针
数组, 这个数组中存放了大量的 JNI 函数指针, 这些指针指向了具体的 JNI 函数;
注意:JNIEnv只在当前线程中有效。本地方法不能将JNIEnv从一个线程传递到另一个线程中。相同的 Java 线程中对本地
方法多次调用时,传递给该本地方法的JNIEnv是相同的。但是,一个本地方法可被不同的 Java 线程所调用,因此可以接受
不同的 JNIEnv。
二、Application.mk:
1. Application.mk目的是描述在你的应用程序中所需要的模块(即静态库或动态库)。Application.mk文件通常被放置在 $PROJECT/jni/Application.mk
下,$PROJECT指的是您的项目。
2. 该文件内变量的作用:
2.1 APP_PROJECT_PATH:这个变量是强制性的,并且会给出应用程序工程的根目录的一个绝对路径。这是用来复制或者安装一个没有任何版本限制的JNI库,从而给APK生成工具一个详细的路径。
2.2 APP_MODULES:这个变量是可选的,如果没有定义,这个模块名字被定义在Android.mk文件中的LOCAL_MODULE 中。NDK将由在Android.mk中声明的默认的模块编译,并且包含所有的子文件(makefile文件), NDK会自动计算模块的依赖。如果APP_MODULES定义了,它必须是一个空格分隔的模块列表( 注意:NDK在R4开始改变了这个变量的行为,在此之前: 在Application.mk中,该变量是强制的必须明确列出所有需要的模块)。
2.3 APP_OPTIM:这个变量是可选的,用来定义“release”或"debug"。在编译您的应用程序模块的时候,可以用来改变优先级。“release”模式是默认的,并且会生成高度优化的二进制代码。"debug"模式生成的是未优化的二进制代码,但可以检测出很多的BUG,可以用于调试。注意:如果你的应用程序是可调试的(即: 如果你的清单文件在它的标签中把android:debuggable属性设为true),默认将是debug而非release。把APP_OPTIM设置为release可以覆写它。注意:可以调试release和debug版二进制,但release版构建倾向于在调试会话中提供较少信息:一些变量被优化并且不能被检测,代码重新排序可能致使代码步进变得困难,堆栈跟踪可能不可靠,等等。
2.4 APP_CFLAGS : 这个变量是可选的, 一个C编译器开关集合,在编译任意模块的任意C或C++源代码时传递。它可以用于改变一个给定的应用程序需要依赖的模块的构建,而不是修改它自身的Android.mk文件。
2.5 APP_BUILD_SCRIPT : 默认,NDK构建系统将在 $(APP_PROJECT_PATH)/jni下寻找一个名为 Android.mk 的文件。即,对于这个文件 $(APP_PROJECT_PATH)/jni/Android.mk。如果你想重载这个行为,你可以定义APP_BUILD_SCRIPT指向一个不同的构建脚本。一个非绝对路径将总是被解析为相对于NDK顶级目录的路径。
2.6 APP_ABI : 默认情况下,NDK的编译系统根据 "armeabi" ABI生成机器代码。可以使用APP_ABI 来选择一个不同的ABI。比如:为了在ARMv7的设备上支持硬件FPU指令。可以使用 APP_ABI := armeabi-v7a或者为了支持IA-32指令集,可以使用 APP_ABI := x86或者为了同时支持这三种,可以使用 APP_ABI := armeabi armeabi-v7a x86。
2.7 APP_STL :默认,NDK构建系统提供由Android系统给出的最小C++运行时库(/system/lib/libstdc++.so)的C++头文件。然而,NDK带有另一个C++实现,你可以在你自己的应用程序中使用或链接它。定义APP_STL以选择它们其中的一个:
APP_STL := stlport_static --> static STLport library
APP_STL := stlport_shared --> shared STLport library
APP_STL := system --> default C++ runtime library
三、 Android.mk
1. Android.mk的作用:
Android.mk 用来向编译系统描述你的源代码。具体来说:该文件是GNU Makefile的一小部分,会被
编译系统解析一次或多次。你可以在每一个Android.mk 文件中定义一个或多个模块,你也可以在几个模块
中使用同一个源代码文件。每个模块属下列类型之一:
a. APK程序,一般的Android程序,编译打包生成apk文件;
b. JAVA库,java类库,编译打包生成jar文件;
c. C\C++应用程序,可执行的C\C++应用程序;
d. C\C++静态库,编译生成C\C++静态库,并打包成.a文件;
e. C\C++共享库, 编译生成共享库(动态链接库),并打包成.so, 有且只有共享库才能被安装/复制到
您的应用软件(APK)包中。
以下面代码为例:
LOCAL_PATH: = $(call my-dir)
JNI_PATH: =$(LOCAL_PATH)
include $(CLEAR_VARS)
# This is the target being built.
LOCAL_MODULE:= liblike
LOCAL_SRC_FILES := liblike.a
#include $(PREBUILT_STATIC_LIBRARY)
#include $(CLEAR_VARS)
# This is the target being built.
LOCAL_MODULE: = libhellojni
LOCAL_C_INCLUDES : = $(JNI_PATH) /include \
$(JNI_PATH) /test/inc \
$(JNI_PATH) /test/ble/inc \
$(JNI_PATH) /testlib/inc
#LOCAL_C_INCLUDES := $(JNI_PATH)/include
# All of the source files that we will compile.
LOCAL_SRC_FILES: = native_func.cpp \
test/src/test1.cpp \
test/src/test2.cpp \
test/src/test3.c
#LOCAL_C_INCLUDES += system/core/include/cutils
# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES : = liblog libcutils libutils
#LOCAL_STATIC_LIBRARIES := liblike
LOCAL_PROGUARD_ENABLED : = disabled
# Also need the JNI headers.
LOCAL_C_INCLUDES + = \
$(JNI_H_INCLUDE)
LOCAL_PRELINK_MODULE : = false
LOCAL_LDLIBS : = -llog -lz -lm
include $(BUILD_SHARED_LIBRARY)
(1). LOCAL_PATH := $(call my-dir) ,一个Android.mk file首先必须定义好LOCAL_PATH变量。它用于在开发树中查找源文件。在这个例子中,宏函数'my-dir', 由编译系统提供,用于返回当前路径(即包含Android.mk 文件的目录)。
(2). include $(CLEAR_VARS),CLEAR_VARS由编译系统提供((可以在 android 安装目录的
/build/core/config.mk 文件看到其定义,为 CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk)),指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
(3). LOCAL_MODULE:= liblike 、LOCAL_SRC_FILES := liblike.a 和 LOCAL_STATIC_LIBRARIES := libshlib 其中LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块,名称必须是唯一的,而且不包含任何空格。 LOCAL_SRC_FILES := liblike.a 用于引用由C或者C++封装.a的库文件且该库文件的所在目录为jni的根目录。LOCAL_STATIC_LIBRARIES := liblike ,表示该模块需要使用哪些静态库,以便在编译时进行链接 。
(4). LOCAL_MODULE:= libhellojni ,LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'like'的共享库模块,将会生成'liblike.so'文件(也可以直接已libxxx命名好)。
(5). LOCAL_SRC_FILES := test/src/test1.cpp \ ,LOCAL_SRC_FILES变量必须包含需要编译的.c文件或.cpp文件的C或C++源代码文件 。
(6). LOCAL_C_INCLUDES : = $(JNI_PATH) /include \ 表示头文件的搜索路径。默认的头文件的搜索路径是LOCAL_PATH目录。注意"\"后面不能为空格,但可以为换行符。
(7). TARGET_ARCH:目标 CPU平台的名字;TARGET_PLATFORM:Android.mk 解析的时候,目标 Android 平台的名字;TARGET_ARCH_ABI暂时只支持两个值:armeabi 和 armeabi-v7a。
(8). LOCAL_SHARED_LIBRARIES: 表示模块在运行时要依赖的共享库(动态库),在链接时就需要,以便在生成文件时嵌入其相应的信息。
(9). LOCAL_STATIC_LIBRARIES: 表示该模块需要使用哪些静态库,以便在编译时进行链接。
(10). LOCAL_SHARED_LIBRARIES: 表示模块在运行时要依赖的共享库(动态库),在链接时就需要,以便在生成文件时嵌入其相应的信息。注意:它不会附加列出的模块到编译图,也就是仍然需要在Application.mk 中把它们添加到程序要求的模块中。
(11). LOCAL_LDLIBS: 编译模块时要使用的附加的链接器选项。这对于使用‘-l’前缀传递指定库的名字是有用的。例如,LOCAL_LDLIBS := -lz表示告诉链接器生成的模块要在加载时刻链接到/system/lib/libz.so可查看 docs/STABLE-APIS.TXT 获取使用 NDK发行版能链接到的开放的系统库列表。
(12). LOCAL_ARM_MODE: 默认情况下, arm目标二进制会以 thumb 的形式生成(16 位),你可以通过设置这个变量为 arm如果你希望你的 module 是以 32 位指令的形式。
'arm' (32-bit instructions) mode. E.g.:
LOCAL_ARM_MODE :=