Android Studio下使用NDK

来源:互联网 发布:根据域名查询ip地址 编辑:程序博客网 时间:2024/06/05 20:19

学习在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.applicationmodel { //这一行是新加的,对应上边的修改,    /**     * 这里要注意一下,如果使用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 = NULLjint result = -1jclass cls = NULLjint 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 -1return JNI_VERSION_1_6;}/** * JNI被卸载时的回调方法,清空所占用的资源. */JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved){  JNIEnv *env = NULLjclass 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

1 0
原创粉丝点击