现在使用Ndk开发的场景还蛮多,游戏引擎、音视频开发等都能涉及到,以前的工程大部分都是Eclipse的工程目录,但是App开发现在大部分都是在AndroidStudio开发工具中进行的,那就有个问题了?怎么在As中搭建Ndk的环境呢。这就是本篇文章所要解答的,并且会创建一个小例子,编译成.so文件,且在项目中使用。Come on….

在没具体动手之前我们想一想怎么实现比较好吧,假如我们新建一个As2.0的工程,然后按照他的目录结构把jni层文件放到指定的目录下,然后进行开发,固然可行,可行是可行,但是代价就是Sdk开发和Ndk开发就分离了,不在一个工程目录下,不符合我们的预期,那就换种思路吧。庆幸的是,As的gradle脚本功能比较强大,可以指定源码文件,操作任务等。如果只是修改一下配置文件,就把事情给做好了,那岂不是皆大欢喜。好吧 开始做了。

首先在工程Module的src目录下新建一个jni目录(存放我们的c、c++源码及mk文件)


然后最关键的是要在build.gradle配置一下说明jni里面的代码是咱们的ndk编译代码位置,及做一些编译任务的处理 下面配置代码由 成都 小妖 提供,特别感谢他

?


<code          class         =         "hljs mel"         >task buildNative(type: Exec, description:          'Compile JNI source via NDK'         ) {        
                  def ndkDir = android.ndkDirectory        
                  
                  if          (Os.isFamily(Os.FAMILY_WINDOWS)) {        
                  commandLine          "$ndkDir/ndk-build.cmd"         ,        
                  '-C'         , file(         'src/jni'         ).absolutePath,          // Change src/main/jni the relative path to your jni source        
                  '-j'         , Runtime.runtime.availableProcessors(),        
                  'all'         ,        
                  'NDK_DEBUG=1'        
                  }          else          {        
                  commandLine          "$ndkDir/ndk-build"         ,        
                  '-C'         , file(         'src/jni'         ).absolutePath,          // Change src/main/jni the relative path to your jni source        
                  '-j'         , Runtime.runtime.availableProcessors(),        
                  'all'         ,        
                  'NDK_DEBUG=1'        
                  }        
         }        
         //在 mac 中 commandLine "$ndkDir/ndk-build", 但是在windows中 commandLine "$ndkDir/ndk-build.cmd",额,坑        
         //        
         task cleanNative(type: Exec, description:          'Clean JNI object files'         ) {        
                  def ndkDir = android.ndkDirectory        
                  if          (Os.isFamily(Os.FAMILY_WINDOWS)) {        
                  commandLine          "$ndkDir/ndk-build.cmd"         ,        
                  '-C'         , file(         'src/jni'         ).absolutePath,          // Change src/main/jni the relative path to your jni source        
                  'clean'        
                  }          else          {        
                  commandLine          "$ndkDir/ndk-build.cmd"         ,        
                  '-C'         , file(         'src/jni'         ).absolutePath,          // Change src/main/jni the relative path to your jni source        
                  'clean'        
                  }        
         }        
         //        
         clean.dependsOn          'cleanNative'        
                  
         tasks.withType(JavaCompile) {        
                  compileTask -> compileTask.dependsOn buildNative        
         }</code>


注意上面的有一个变化的地方就是jni的路径在哪,src/jni 如果跟我不一样的,应该写你的具体位置

然后在我们的代码中添加一个Native方法,就让他打印一个helloworld吧


public native void printHelloworld(); 

然后使用javah命令 生成对应的.h头文件


这样会在下面目录生成2个文件,当然下面的那个文件不用管它了


然后把生成的这俩文件放到刚才创建的jni里面

下面就是编写cpp文件Android.mk Application.mk文件了


.h文件代码

?


<code          class         =         "hljs vala"         >         /* DO NOT EDIT THIS FILE - it is machine generated */        
         #include <jni.h>        
         /* Header for class com_qbao_ticket_MainActivity */        
                  
         #ifndef _Included_com_qbao_ticket_MainActivity        
         #define _Included_com_qbao_ticket_MainActivity        
         #ifdef __cplusplus        
         extern          "C"          {        
         #endif        
         /*        
                  * Class:     com_qbao_ticket_MainActivity        
                  * Method:    add        
                  * Signature: ()V        
                  */        
         JNIEXPORT          void          JNICALL Java_com_qbao_ticket_MainActivity_printHelloworld        
                  (JNIEnv *, jobject);        
                  
         #ifdef __cplusplus        
         }        
         #endif        
         #endif</jni.h></code>


.cpp文件代码


?


<code          class         =         "hljs vala"         >#include <jni.h>        
         #include         
         #include          "com_qbao_ticket_MainActivity.h"        
         #define LOG_TAG          "System.out.c"        
         #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)        
         #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)        
                  
         JNIEXPORT          void          JNICALL Java_com_qbao_ticket_MainActivity_printHelloworld        
                  (JNIEnv *, jobject){        
                  LOGI(         "hello world! come from jni"         );        
         }</android></jni.h></code>


注意新建写的时候 一定要加上


?


<code          class         =         "hljs ruleslanguage"         >#include          "com_qbao_ticket_MainActivity.h"         </code>


然后写咱们的mk文件,先来Android.mk


?


<code          class         =         "hljs ruby"         >LOCAL_PATH := $(call my-dir)        
                  
         include $(CLEAR_VARS)        
                  
         LOCAL_MODULE    := lalala        
                  
         LOCAL_SRC_FILES := com_qbao_ticket_MainActivity.cpp        
                  
         LOCAL_LDLIBS += -llog        
                  
         include $(BUILD_SHARED_LIBRARY)</code>


要改的地方是咱们的module名称源文件列表 这里根据自己的代码设置就好了

接下来Application.mk


?


<code          class         =         "hljs makefile"         >APP_ABI := all        
         APP_PLATFORM := android-         8         </code>


设置了全平台,当然不设置也可以 默认就行了

点击这个按钮 开始编译


编译成功会在src目录下生成一个libs和obj.local目录

然后就生成了咱们命名的liblalala.so文件 格式为lib{模块名}.so

下面是怎么在代码中使用咱们生成的.so文件了

把我们的编译生成好的.so文件拷贝到代码的libs中armeabi目录下


然后是在我们的代码中使用它

?


<code          class         =         "hljs java"         >         static          {        
                  System.loadLibrary(         "lalala"         );        
         }        
         public          native           void          printHelloworld();</code>


注意load的时候写模块名 不是文件名 这里是lalala 然后在MainActivity里面调用这个printHelloworld方法
打印一下log

好了到这里在As2.0中搭建Ndk环境并且编译so文件,并使用so文件的介绍已经讲完了,后面会在这个基础上编译一些音视频模块的代码,然后封装使用。期待吧。。。

build.gradle全部配置查看


?


<code          class         =         "hljs r"         >         import          org.apache.tools.ant.taskdefs.condition.Os        
                  
         apply plugin:          'com.android.application'        
                  
         dependencies {        
                  compile fileTree(dir:          'libs'         , include:          '*.jar'         )        
         }        
                  
         android {        
                  compileSdkVersion          21        
                  buildToolsVersion          "22.0.1"        
                  sourceSets {        
                  main {        
                  manifest.srcFile          'AndroidManifest.xml'        
                  java.srcDirs = [         'src'         ]        
                  resources.srcDirs = [         'src'         ]        
                  aidl.srcDirs = [         'src'         ]        
                  renderscript.srcDirs = [         'src'         ]        
                  res.srcDirs = [         'res'         ]        
                  assets.srcDirs = [         'assets'         ]        
                  jniLibs.srcDirs = [         'libs'         ]        
                  }        
                  
                  // Move the tests to tests/java, tests/res, etc...        
                  instrumentTest.setRoot(         'tests'         )        
                  
                  // Move the build types to build-types/<type>        
                  // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...        
                  // This moves them out of them default location under src/<type>/... which would        
                  // conflict with src/ being used by the main source set.        
                  // Adding new build types or product flavors should be accompanied        
                  // by a similar customization.        
                  debug.setRoot(         'build-types/debug'         )        
                  release.setRoot(         'build-types/release'         )        
                  }        
                  buildTypes {        
                  debug {        
                  //proguardFile '/Users/xuchuang/Desktop/Stifler/WorkSpace/QBaoTicket_2.0/qbaoticket_2.0/proguard-project.txt'        
                  //signingConfig signingConfigs.qbaoticket        
                  }        
                  release {        
                  //signingConfig signingConfigs.qbaoticket        
                  //minifyEnabled true        
                  //proguardFile '/Users/xuchuang/Desktop/Stifler/WorkSpace/QBaoTicket_2.0/qbaoticket_2.0/proguard-project.txt'        
                  }        
                  }        
                  compileOptions {        
                  sourceCompatibility JavaVersion.VERSION_1_7        
                  targetCompatibility JavaVersion.VERSION_1_7        
                  }        
                  dexOptions {        
                  jumboMode          true        
                  }        
         }        
                  
         task buildNative(type: Exec, description:          'Compile JNI source via NDK'         ) {        
                  def ndkDir = android.ndkDirectory        
                  
                  if          (Os.isFamily(Os.FAMILY_WINDOWS)) {        
                  commandLine          "$ndkDir/ndk-build.cmd"         ,        
                  '-C'         , file(         'src/jni'         ).absolutePath,          // Change src/main/jni the relative path to your jni source        
                  '-j'         , Runtime.runtime.availableProcessors(),        
                  'all'         ,        
                  'NDK_DEBUG=1'        
                  }          else          {        
                  commandLine          "$ndkDir/ndk-build"         ,        
                  '-C'         , file(         'src/jni'         ).absolutePath,          // Change src/main/jni the relative path to your jni source        
                  '-j'         , Runtime.runtime.availableProcessors(),        
                  'all'         ,        
                  'NDK_DEBUG=1'        
                  }        
         }        
         //在 mac 中 commandLine "$ndkDir/ndk-build", 但是在windows中 commandLine "$ndkDir/ndk-build.cmd",额,坑        
         //        
         task cleanNative(type: Exec, description:          'Clean JNI object files'         ) {        
                  def ndkDir = android.ndkDirectory        
                  if          (Os.isFamily(Os.FAMILY_WINDOWS)) {        
                  commandLine          "$ndkDir/ndk-build.cmd"         ,        
                  '-C'         , file(         'src/jni'         ).absolutePath,          // Change src/main/jni the relative path to your jni source        
                  'clean'        
                  }          else          {        
                  commandLine          "$ndkDir/ndk-build.cmd"         ,        
                  '-C'         , file(         'src/jni'         ).absolutePath,          // Change src/main/jni the relative path to your jni source        
                  'clean'        
                  }        
         }        
         //        
         clean.dependsOn          'cleanNative'        
                  
         tasks.withType(JavaCompile) {        
                  compileTask -> compileTask.dependsOn buildNative        
         }</type></type></code>