Android Studio下使用NDK

学习在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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值