Android NDK开发入门
2016-01-12 20:16
549 查看
接触Android有一段时间了,NDK算是一道坎,因为对c语言不熟悉,所以也没怎么去接触这一块,但是最近的学习,越发的觉得,如果想让app能有更高的效率,比如在图像的处理方面,游戏开发方面,甚至于要做一些涉及系统层面的操作时,不可避免的要去用到NDK。通过利用c/c++的能力来弥补java的缺点。
NDK的好处:
1. 提高程序的执行效率
2. 代码保护,java层容易被反编译,但是ndk层较难
3. 便于移植,而且也能方便使用c/c++开源库
这个是Android Studio的ndk入门版,于eclipse的不同,使用的是gradle进行构建。
下面就从hello ndk开始进入ndk的世界。这个sample可以在下载了ndk之后在ndk文件夹中的sample文件夹下找到,导入Android Stuido就可以开始学习了。
Android Studio中有两种方法去使用c/c++的代码:
1. 使用现有的.so库
2. 使用项目中当前的c/c++代码
![](http://img.blog.csdn.net/20160110220654346)
接着,在要使用的地方加上加载库的代码,就可以使用当前的.so库了。
库的名字构成是由,lib + moduleName + .so,当去加载库时,只需要使用moduleName不用加前缀或后缀。
如果想改变使用的jnilibs的路径,可以在gradle中定义如下的代码,既可以改变引用的.so的文件路径。
1. 配置ndk的路径
2. 在gradle中配置ndk的module
3. 添加c/c++文件
也可以在工程根目录下的local.properties中增加ndk的配置。
当然可以在增加一些指定的配置,类似于cFlags, stl, ldLibs.
1. Android.mk
2. Application.mk
3. c/c++文件包括头文件
当然和引入so库相似,对于c/c++的代码的位置也是可以改变,通过在gradle中进行配置
可以通过productFlavors去设定对于不同平台时候的不同的变量,比如通过设置全局变量指定不同平台去加载不同的头文件。
又或者编译制定平台的so库而不是所有的平台。通过这个方法来得到我们想要的结果。
在src/main/jni目录下创建hello-jni.c文件,方法名字的定义要注意,否则会引起方法找不到的错误,方法名称为Java_+完整类路径+方法。当然,为了防止手动的错误,可以使用javah的命令生成,具体请继续往下看。
使用javah生成头文件
在gradle指定ndk module
回到MainActivity.java中,引入ndk library,library的名称也就是module name无需加前缀或者后缀。
默认情况下,NDK的编译系统根据 “armeabi” ABI生成机器代码。可以使用APP_ABI 来选择一个不同的ABI。
比如:为了在ARMv7的设备上支持硬件FPU指令。可以使用:
当然,当有多个定义时,以空格分开。
ndk编译系统的系统的变量都有如下的定义:
LOCAL_为前缀的变量,比如LOCAL_MODULE
以PRIVATE_、NDK_、APP_为前缀的变量
全小写字母的变量
所以在Android.mk文件中使用自定义变量的时候,建议使用MY_前缀。
在这个HelloNdk工程中,Android.mk文件定义如下:
CLEAR_VARS变量,主要在构建的时候会清除所有的LOCAL_开头的变量的定义,包括LOCAL_MODULE、LOCAL_SRC_FILES等,但是不包括LOCAL_PATH。由于在构建的时候这些变量都是全局的变量,所以在一个module在构建的时候,应该不受其他module的变量影响。使用方式就是:
my-dir属于一个方法(function)的全局定义,返回当前Android.mk文件的文件路径,值得注意的是,这个方法是包含最后一个构建文件的路径,所以这个定义要在开头,尤其是包含其他的文件的时候。使用的方法是
LOCAL_PATH变量是指明当前文件的位置,必须定义在Android.mk文件的开头,定义方式:
BUILD_SHARED_LIBRARY变量,收集从最近的CLEAR_VARS之后的LOCAL_前缀的变量,并以此生成so库。使用方法:
NDK的好处:
1. 提高程序的执行效率
2. 代码保护,java层容易被反编译,但是ndk层较难
3. 便于移植,而且也能方便使用c/c++开源库
这个是Android Studio的ndk入门版,于eclipse的不同,使用的是gradle进行构建。
下面就从hello ndk开始进入ndk的世界。这个sample可以在下载了ndk之后在ndk文件夹中的sample文件夹下找到,导入Android Stuido就可以开始学习了。
Android Studio中有两种方法去使用c/c++的代码:
1. 使用现有的.so库
2. 使用项目中当前的c/c++代码
使用现有的.so库
这一步是比较简单的,只要把所有的.so文件拷贝到对应的目录文件下就可以了。要对应好不同的平台。接着,在要使用的地方加上加载库的代码,就可以使用当前的.so库了。
String libName = "helloNDK"; // 加载的module的名字,不用加.so System.loadLibrary( libName );
库的名字构成是由,lib + moduleName + .so,当去加载库时,只需要使用moduleName不用加前缀或后缀。
如果想改变使用的jnilibs的路径,可以在gradle中定义如下的代码,既可以改变引用的.so的文件路径。
android { sourceSets.main { jniLibs.srcDir 'src/main/filePath' // 设置有so库的位置 } }
使用当前项目中的c/c++代码
这个是很方便的一个开发方式,而且通过gradle去配置,将原本eclipse的复杂度降低了。主要要做以下的事情1. 配置ndk的路径
2. 在gradle中配置ndk的module
3. 添加c/c++文件
配置ndk的路径
可以通过在项目的ModuleSetting中设置,右键项目,选择ModuleSetting,在sdk location中可以选择下载或者是配置路径,下载的话去官网就有的下载了。也可以在工程根目录下的local.properties中增加ndk的配置。
ndk.dir=/Users/ndkPath
在gradle中配置ndk module
指明将要编译的c/c++代码android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { ... ndk { moduleName "modulename" } } }
当然可以在增加一些指定的配置,类似于cFlags, stl, ldLibs.
ndk { moduleName "myEpicGameCode" cFlags "-DANDROID_NDK -D_DEBUG DNULL=0" // 定义宏变量 ldLibs "EGL", "GLESv3", "dl", "log" // 链接这些库 stl "stlport_shared" // Use shared stlport library }
添加c/c++文件
配置好上述步骤之后,我们就可以开始开发我门的c/c++代码了。c/c++代码的位置是在src/main/jni目录下,文件包括1. Android.mk
2. Application.mk
3. c/c++文件包括头文件
当然和引入so库相似,对于c/c++的代码的位置也是可以改变,通过在gradle中进行配置
android { sourceSets.main { jni.srcDirs 'src/main/path' // your path } }
可以通过productFlavors去设定对于不同平台时候的不同的变量,比如通过设置全局变量指定不同平台去加载不同的头文件。
又或者编译制定平台的so库而不是所有的平台。通过这个方法来得到我们想要的结果。
android { productFlavors { x86 { ndk { abiFilter "x86" } } arm { ndk { abiFilter "armeabi-v7a" } } } }
c/c++ 文件
这里开始真正的关于Hello NDK的代码编写。首先在MainActivity.java中先定义好我们的native的方法,使用的native的关键字。private native String sayHello(); // 在btn的setListener中设定toast Toast.makeText(MainActivity.this, sayHello(), Toast.LENGTH_SHORT).show();
在src/main/jni目录下创建hello-jni.c文件,方法名字的定义要注意,否则会引起方法找不到的错误,方法名称为Java_+完整类路径+方法。当然,为了防止手动的错误,可以使用javah的命令生成,具体请继续往下看。
#include <string.h> #include <jni.h> jstring Java_com_yxp_hellondk_MainActivity_sayHello( JNIEnv* env, jobject thiz ) { return (*env)->NewStringUTF(env, "Hello NDK!"); }
使用javah生成头文件
# 进入src/main/java文件夹,-d指明输出文件路径 javah -d ../jni com.example.MainActivity
在gradle指定ndk module
defaultConfig { ... ndk { moduleName "hello-jni" } }
回到MainActivity.java中,引入ndk library,library的名称也就是module name无需加前缀或者后缀。
static { System.loadLibrary("hello-jni"); }
Application.mk(官网)
Application.mk文件的路径是在src/main/jni目录下,用来描述工程中需要用到的native库,包括静态库,共享库和可执行文件。在HelloNdk项目中的Application.mk内容如下:APP_ABI := all
默认情况下,NDK的编译系统根据 “armeabi” ABI生成机器代码。可以使用APP_ABI 来选择一个不同的ABI。
比如:为了在ARMv7的设备上支持硬件FPU指令。可以使用:
APP_ABI := armeabi-v7a
当然,当有多个定义时,以空格分开。
Android.mk(官网)
Application.mk文件的路径是在src/main/jni目录下,Android.mk文件是用来描述ndk相关源文件,便于构建工程。在构建的过程中它会被解析多次,以完成构建。在Android.mk文件中,可以定义一个到多个模块,模块之间可以共享文件。在文件当中,不用列出具体的头文件或者依赖,这些ndk编译器都会自动完成。ndk编译系统的系统的变量都有如下的定义:
LOCAL_为前缀的变量,比如LOCAL_MODULE
以PRIVATE_、NDK_、APP_为前缀的变量
全小写字母的变量
所以在Android.mk文件中使用自定义变量的时候,建议使用MY_前缀。
在这个HelloNdk工程中,Android.mk文件定义如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY)
CLEAR_VARS变量,主要在构建的时候会清除所有的LOCAL_开头的变量的定义,包括LOCAL_MODULE、LOCAL_SRC_FILES等,但是不包括LOCAL_PATH。由于在构建的时候这些变量都是全局的变量,所以在一个module在构建的时候,应该不受其他module的变量影响。使用方式就是:
inclued $(CLEAR_VARS)
my-dir属于一个方法(function)的全局定义,返回当前Android.mk文件的文件路径,值得注意的是,这个方法是包含最后一个构建文件的路径,所以这个定义要在开头,尤其是包含其他的文件的时候。使用的方法是
$(call my-dir)
LOCAL_PATH变量是指明当前文件的位置,必须定义在Android.mk文件的开头,定义方式:
LOCAL_PATH := $(call my-dir)
BUILD_SHARED_LIBRARY变量,收集从最近的CLEAR_VARS之后的LOCAL_前缀的变量,并以此生成so库。使用方法:
include $(BUILD_SHARED_LIBRARY)
参考文档:
NDK with Android Studio相关文章推荐
- Android Studio——synchronized
- Android Studio Gradle编译禁用Lint报错
- Android程序的调试-输出日志信息的几种方法
- Android安装卸载探究
- android的自定义控件简单(二)
- 关于android的Notification的若干问题
- Android----PinnedSectionListView
- Android开发 - 下拉刷新和分段头悬停列表
- Android系统设置【android.provider.settings】
- 关于动态布局的时候调整布局的错误(一)
- android的adb命令
- Android studio 上 EventBus的初步使用
- 30.Android之百度地图简单学习
- Android将程序崩溃信息保存本地文件以及上传到服务器
- Android 5.x Theme 与 ToolBar 实战
- android (获取验证码)倒计时按钮或者TextView
- Android WebView JS不能调用Java对象的问题
- android.view.InflateException 异常处理
- Android组件:Fragment切换后保存状态
- Android加载图片内存溢出问题解决方法