学习在Android Studio上使用NDK,花的时间比当时用ADT还长。呵呵,顺便吐槽一下,百度出来的资料不多,用微软的必应和雅虎的搜索比较多;另外搜索出来的很多链接是连接失败的,这个做Android的都比较懂。费了这么大劲,一定要记下来的,以备以后查看。
电脑系统Win7,64位,这应该标准配置;
Android Studio版本 2.1.2,从官网上下的,下载地址:
https://developer.android.com/studio/index.html
jdk8,这个是从oracle官网上下:
http://www.oracle.com/technetwork/Java/javase/downloads/jdk8-downloads-2133151.html
安装jdk,配置环境变量,安装Android Studio略过,这个可以百度到。
1、在Android Studio中安装NDK
Android Studio 2.1.2里有NDK支持,可以使sdk下载工具下载。
1)打开AS,在菜单中找到"File"-> "Settings...",打开Settings对话框;
2)在左边选择"Appearance&Behavior"->"System Settings" ->"Android SDK";
3)在右边切换到 "SDK Tools"选项卡, 选中其中的 "LLDB 2.1"和 "NDK",然后点下边的"Apply"按钮,它会弹出一个框让设置NDK的目录,通常来说默认就可以。
2 编译native代码
刚开始先搜索了一下步骤,然后自己建了一个工程,添加NDK支持,后来发现这个方法不好,太浪费时间;google有NDK samples的,可以直接下载来,然后编译,研究,比自己从头开始要好的多;samples下载地址为:
https://github.com/googlesamples/android-ndk
下载zip文件,把所有的sample打包下来,然后将一个工程(如使用 hello-jni)导入AS,编译,通常不会出什么大问题。
如果编译不通过,可以打开AS中的 "Terminal",在命令行下执行 "gradlew.bat"命令编译。
编译完后就可以在模拟器里运行了。
---------------------------------------------------------------
自己建立的工程要加入JNI,修改以下几个地方:
1) 工程目录下的local.properties文件:在文件中加入ndk的路径:
ndk.dir=D\:\\program\\Android\\sdk\\ndk-bundle
sdk.dir=D\:\\program\\Android\\sdk
2)要修改build.gradle,这又分成两种情况:使用原来的"gradle插件"和"gradle-experimental插件",两者二选一。
(1)使用"gradle插件":工程下边的build.gradle不用修改,只修改模块下的build.gradle要修改,在 defaultConfig中加入NDK部分
ndk {
moduleName"MyLib"
}
里边还可以加入好多的参数,具体可以参考:
http://ruikye.com/2014/08/30/androidstudio_ndk/
(2) 使用"gradle-experimental插件"
我用的是这种方式。NDK功能在AS中还处于开发阶段,这个插件就处于开发中。虽然是这样,但是这个功能还是很强的,自动补全什么还都是有的。
先是要修改工程根目录下的build.gradle,将原来的插件换成这个插件,把原来的注释一下:
classpath'com.android.tools.build:gradle-experimental:0.7.0'
//classpath 'com.android.tools.build:gradle:2.1.2'
然后修改模块目录下的build.gradle,我这里就修改app下的gradle文件了,文件的内容如下:
apply plugin: 'com.android.model.application' //这里原来是 com.android.application model { //这一行是新加的,对应上边的修改, /** * 这里要注意一下,如果使用gradle-experimental,参数赋值要使用 = 号,而不是空格, * 所以要把原来文件中的空格都用等号去替换; * 还有一部分参数使用的函数也与原来不一样, * 实在不能确定的就找出ndk samples中的build.gradle文件对比一下。 */ android { compileSdkVersion = 23 buildToolsVersion = "23.0.3" defaultConfig { applicationId = "com.example.zhaoh.demo2" minSdkVersion.apiLevel = 15 //这两个参数和原来有点不一样,多了apiLevel targetSdkVersion.apiLevel = 23 versionCode = 1 versionName = "1.0" } ndk { //NDK设置 moduleName = "MyLib" //动态库的名称 toolchain = 'clang' //编译器,据说这个比gcc要快,没有这个写native代码时没有自动补全的功能 CFlags.addAll(['-Wall', '-DHELLO_2']) //对应gcc中的编译选项 CFLAGS,方括号内是一个数组,可以有多个值 CFlags.add("-DHELLO_3") // CFlags += "-DHELLO" //这种方式在这里行不通,包括以下的ldFlags, ldLibs 也一样. ldFlags.addAll(["-L../lib"]) //库文件路径 ldLibs.addAll(['log']) //库文件名 stl = "stlport_static" //指示使用动态库还是静态库 } buildTypes { release { minifyEnabled = false proguardFiles.add(file('proguard-rules.pro')) //这是把原来的改成这样的 } debug { ndk.debuggable = true //有这个才会支持调试native 代码,这个放到release里一样能用 } } productFlavors { /** copy过来的,根据不同的平台,会有不同的配置 */ // for detailed abiFilter descriptions, refer to "Supported ABIs" @ // https://developer.android.com/ndk/guides/abis.html#sa create("arm") { ndk.abiFilters.add("armeabi") } create("arm7") { ndk.abiFilters.add("armeabi-v7a") } create("arm8") { ndk.abiFilters.add("arm64-v8a") } create("x86") { ndk.abiFilters.add("x86") } create("x86-64") { ndk.abiFilters.add("x86_64") } create("mips") { ndk.abiFilters.add("mips") } create("mips-64") { ndk.abiFilters.add("mips64") } // To include all cpu architectures, leaves abiFilters empty create("all") } } } //这个是对就 model{ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.android.support:design:23.4.0' }
3)写java代码,声明native方法,加载本地库文件。在MainActivity文件里添加以下代码:
/** native 函数声明 */ private native String getStringFromNative(); /** 加载库文件 */ static{ System.loadLibrary("MyLib"); }
4)添加native文件,AS默认的路径为 模块名\src\jni ,也不需要写什么Android.mk文件,直接放上就可以。我直接放了一个main.c文件。内容如下:
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <jni.h> //#include "demo.h" JavaVM *g_jvm = NULL; /** native 方法的实现 */ JNIEXPORT jstring JNICALL Native_getStringFromNative (JNIEnv *env, jobject obj){ return (*env)->NewStringUTF(env, "Hello from JNI!"); } /** 这里一定要用反斜杠,不能用点,不然会崩溃的 */ #define JNI_MAINACTIVITY "com/example/zhaoh/demo2/MainActivity" /** 本地方法列表,要向java类注册的 */ static JNINativeMethod methods[] = { {"getStringFromNative", "()Ljava/lang/String;", (void*)Native_getStringFromNative}, }; /** * JNI库加载时的回调方法,在本函数中分配资源. */ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm , void *reserved){ JNIEnv *env = NULL; jint result = -1; jclass cls = NULL; jint num = (int)(sizeof(methods)/sizeof(methods[0])); /** 从虚拟机中取得一个env对象 */ if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK){ return -1; } // g_jvm = vm; /** 找到要注册的java类*/ cls = (*env)->FindClass(env, JNI_MAINACTIVITY); if(NULL == cls) return -1; /** 在java类中注册native方法的实现 */ if((*env)->RegisterNatives(env, cls, methods, num) < 0) return -1; return JNI_VERSION_1_6; } /** * JNI被卸载时的回调方法,清空所占用的资源. */ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved){ JNIEnv *env = NULL; jclass cls; /** 从java虚拟机取得env对象 */ if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6)!= JNI_OK) return; /** 查找java类 */ cls = (*env)->FindClass(env, JNI_MAINACTIVITY); if (NULL == cls) return; /** 反注册native方法 */ jint tmp = (*env)->UnregisterNatives(env, cls); // g_jvm = NULL; }
5)修改完成后重新编译一下就,通常会成功的,呵呵。
3、调试native代码
在给工程添加Native代码后,会在配置里出现一个app-native的选项,选中这个选项,再点右边的小虫子,就会进入到调试模式。可以在c代码里加几个断点试试,挺方便的。
4、注意:
1)有时候AS编译不成功,先清理一下,执行菜单 "build"->"Clean Project",再重新编译就可能会好。
2)在调试Native代码时,可能会出现"build type isn't JNI debuggable",在build.gradle中打开NDK的调试开关就好: ndk.dbuggable = true,具体参考以上的部分。
3)还有一种方法,打开"Terminal",在命令行下执行: gradlew.bat。有些时候这么做也可以。
4)编译的时候可能会有以下问题"Gradle sync failed: Cause:org.gradle.api.internal.ExtensibleDynamicObject Consult IDE log for more details (Help |Show Log)"。这个问题是因为在使用了gradle-experimental后,没有把app目录下build.gradle中的model结点中的,空格改成等号,"compileSdkVersion 23"要改成"compileSdkVersion = 23"。
总结:
最后还是要说的,AS这个东西太吃内存,Android模拟器也很吃内存,全部开动起来8G内存占了6G。这个软件太复杂了,编译出一问题都不知道怎么去找。折腾了这么长时间,还是学到了好多的东西。源码上传到CSDN下载去了
参考资料:
http://ask.android-studio.org/?/question/518
https://code.google.com/p/android/issues/detail?id=82187
NKD support is an experimental feature andall use cases are not yet supported
https://www.sitepoint.com/using-c-and-c-code-in-an-android-app-with-the-ndk/
http://www.cnblogs.com/tanlon/p/4739171.html
http://www.tuicool.com/articles/VzMB7zR
http://blog.csdn.net/tanlon_0308/article/details/47958675
来自:http://blog.csdn.net/zhaoguangjun33/article/details/51690136