Android NDK使用分析:
在Android应用程序开发中,对于一些对性能安全要求较高的模块开发中,我们一般会使用C/C++代码来实现,或者引用一些已经实现好的C/C++库时,都需要使用JNI机制。正如上面的介绍是比较常用的,同样可以实现编写基于JNI机制访问C/C++库文件。
Android NDK是谷歌公司提供的开发工具集,我们可以使用它快捷得开发基于JNI机制的程序。它的几个重要的功能如下:
A、提供将C/C++源代码编译成本地库文件(编译器、连接器等)
B、提供将编译好的本地库文件插入到Android的包文件(.apk)
C、提供NDK开发相关的文档、实例及规范等
注意:
谷歌是不建议开发者使用NDK开发Android本地应用程序的,当然,这样做是可以实现的,因为Android系统的主要应用是运行在dalvik虚拟机上的Java程序,推荐多开发运行在dalvik上的程序。
NDK的功能:
如上图所示,Android ndk先编译了C/C++本地代码,并生成本地库文件,然后将其插入到Android应用程序的包当中,但调用JNI的编译本地代码的工作还需要我们来做的。
NDK安装:
在谷歌官网上下载最新的NDK包,并解压到指定的目录下,那么这个目录就作为环境变量的名字,即NDK_HOME,然后在环境变量的path中添加该NDK_HOME环境变量即可。
Cygwin安装:
在上面,我们已经安装并安装了Cygwin了,在这里简单介绍下他的作用。Cygwin主要是用来处理不同平台间的转化工作,例如Android是基于Linux的框架集,而我们常使用的系统却是windows系统,所以使用它来将库文件从windows转为基于Linux系统的。当然,如果使用的系统是Linux的话 则无需这个步骤了。
既然在这里介绍了它,无可置疑会用到它的。回到上面NDK环境的配置,怎么样验证环境变量是否配置成功那?答案就是使用这个Cygwin工具。我们打开它,在里面输入ndk-build,如果没有报错而是出现这个结果的话,则说明环境变量配置成功:
哦,这里的错误是因为没设定项目的路径,这里不用理会即可。
NDK的使用步骤:
实际上,NDK的使用流程与之前介绍的《Android本地接口JNI使用分析》使用流程及内容很多都很相似,在这里,我只介绍与之前不同的一些东西,下面即为NDK的开发流程:
1、创建Java本地方法类
2、编译Java本地方法生成C/C++头文件
3、编写实现上面的C/C++实现文件
4、编译生成动态运行库(.so)
5、运行应用程序
下面,根据上面的流程来介绍下NDK的使用及注意事项。我的项目目录:
运行的效果如:
每点击按钮打印LOG日志。
创建Java本地方法:
创建Java本地方法与之前文章是相同的,只不过这里的LOG内容为”Hello Ndk!”而已,代码如下:
public class VerifyNdkJniMethod {
static {
System.loadLibrary("hello_ndk");
}
public static native String helloNdk();
}
编译Java本地方法:
和之前的文章内容相同,都是使用Javah命令来编译生成的class文件,从而生成对应的C/C++头文件,我的头文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_demo_verifyndk_jni_ndk_VerifyNdkJniMethod */
#ifndef _Included_com_demo_verifyndk_jni_ndk_VerifyNdkJniMethod
#define _Included_com_demo_verifyndk_jni_ndk_VerifyNdkJniMethod
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_demo_verifyndk_jni_ndk_VerifyNdkJniMethod
* Method: helloNdk
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_demo_verifyndk_1jni_ndk_VerifyNdkJniMethod_helloNdk
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
同样的,与之前的头文件编写方式相同,我的实现文件如下:
#include <stdio.h>
#include "VerifyNdkJniMethod.h"
JNIEXPORT jstring JNICALL Java_com_demo_verifyndk_1jni_ndk_VerifyNdkJniMethod_helloNdk
(JNIEnv *env, jclass obj)
{
return (*env)->NewStringUTF(env,"Hello Ndk!");
}
编译生成动态库文件(.so):
在这里,与我们之前介绍的略有不同,我们任然需要编写Android.mk文件,我的mk文件如下(在文章的最下面会介绍下mk文件):
#指定源文件的位置
LOCAL_PATH := $(call my-dir)
#初始化与Make相关的环境变量
include $(CLEAR_VARS)
#库编译相关信息 包括库名称、源码等
LOCAL_MODULE := hello_ndk
LOCAL_SRC_FILES := VerifyNdkJniMethod.c
#生成共享库
include $(BUILD_SHARED_LIBRARY)
同样需要将头文件和实现文件及mk文件放在一起,只不过这里我们必须在项目的根目录下新建一个jni目录,cd到这个目录下,使用ndk-build或是$NDK/ndk-build来编译生成动态库文件。这与之前有些不同,之前我们使用的是$NDK/ndk-build来编译,是因为之前我们没有通过NDK的方式来实现,而是借助工具Cygwin来实现生成共享库文件的(这个是成立的);而现在,我们配置了NDK的环境变量可以使用$NDK/ndk-build或是ndk-build都可以进行编译生成,不同的是使用NDK及进行编译的话,我们必须cd到项目根目录下的jni下,上面的编译命令才会生效。原因是这样的,使用NDK编译的时候,NDK自身带有编译器系统,在编译的时候,编译系统会在当前的目录下查找AndroidManifest.xml文件,若文件存在,则将当前的目录看做为Android工程目录。然后再转到jni目录下,根据Android.mk进行NDK编译。
运行应用程序:
这个过程与之前的文章《Android本地接口JNI使用分析》基本相同,我的代码如下:
public class VerifyNdkJniActivity extends Activity {
private static String TAG = "VerifyNdkJniActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_verify_ndk_jni);
}
public void helloNdk(View v) {
String result = VerifyNdkJniMethod.helloNdk();
log(result);
}
private void log(String log) {
Log.d(TAG, log);
}
}
Android.mk文件说明:
实际上,Android.mk文件的帮助文件在<NDK_HOME>/docs/ANDROID_MK.html中(不过是英文的),下面就介绍下吧!
LOCAL_PATH := $(param...):
这个变量是用来在开发树中查找源文件,必须在mk文件的最开始处定义,一般情况下,使用下面的这种的编写格式:
LOCAL_PATH := $(call my-dir)
其中的my-dir为一个宏函数,$(call my-dir)就是用来返回一个宏函数的值,一般Android.mk文件与源文件在同一个目录下。
Include $(CLEAR_VARS):
用来初始化mk文件中LOCAL_XXX的变量,但是上面的LOCAL_PATH除外,因为Android系统会将LOCAL_XXX的变量当作全局变量,所以会先对其进行初始化。
LOCAL_MODULE := XXX:
用来标记在mk文件中描述的每一个模块,也就是要生成的库的名称。这个名称必须唯一并且不能含有空格,编译系统会自动产生适当的前缀和后缀,比如:libhello_jni.so文件。
LOCAL_SRC_FILES := XXX.c/.cpp:
用来将包含将要被编译进打包模块的各个资源文件。同时,这些源文件所在的位置为LOCAL_PATH所指定的目录。
Include $(BUILD_SHARED_LIBRARY):
用来使用LOCAL_PATH、LOCAL_SRC_FILES等的变量值,创建名称为lib$(LOCAL_MODULE).so的库文件。
到这里,关于NDK的一些基本的东西介绍完了,如果想要更灵活的使用NDK,那么可以参看NDK中自带的simples,仔细研究学习,其位置在: <NDK_HOME>\samples\
/**
* 欢迎加入技术群:179914858
* 如果有任何问题请在评论中进行讨论学习
*/