使用实验性的 Gradle 工具构建 Android NDK 环境

本文阐述如何在 Android Studio 中使用实验性的 Gradle 工具构建 Android NDK 的开发环境,其中 native 部分使用 cpp 文件。
新版本的工具已不再需要 Android.mk 和 Application.mk 文件,也不需要手动 javah 生成头文件,更不需要 cygwin 等的辅助。

版本号:

操作步骤:

  1. 新建一个Project

  2. 打开 Project 视图

  3. 修改 ./build.gradle(最外层的build.gradle),内容为

    classpath 'com.android.tools.build:gradle-experimental:0.4.0'
  4. 确认 ./gradle/wrapper/gradle-wrapper.properties 中版本号为 2.8

    distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
  5. 修改 ./app/build.gradle(module的build.gradle),这个比较麻烦。分几步吧:

    1. 第一行加上model

      • 如果是应用,则改成

        apply plugin: 'com.android.model.application'
      • 如果是库,要改成

        apply plugin: 'com.android.model.library'
    2. 后面部分的android外层用model{}包起来,buildTypes等部分移到android的外面

    3. 里面的属性都加上等号,minSdkVersiontargetSdkVersion要加上.apiLevel,以及其它一些细微而繁琐的变动,参考下述代码段。注意,如果你使用更新版本的 plugin (目前最新是0.6.0-alpha5),该文件可以修改起来会略微简单一些

      apply plugin: 'com.android.model.application'

      model{
      android {
      compileSdkVersion = 23
      buildToolsVersion = "23.0.1"

      defaultConfig.with {
      applicationId = "andy.studentingeo"
      minSdkVersion.apiLevel = 21
      targetSdkVersion.apiLevel = 23
      versionCode = 1
      versionName = "1.0"

      buildConfigFields.with {
      create() {
      type = "int"
      name = "VALUE"
      value = "1"
      }
      }
      }
      }
      android.buildTypes {
      release {
      minifyEnabled = false
      proguardFiles.add(file('proguard-rules.pro'))
      }
      }
      }

      dependencies {
      compile fileTree(dir: 'libs', include: ['*.jar'])
      testCompile 'junit:junit:4.12'
      compile 'com.android.support:appcompat-v7:23.1.1'
      compile 'com.android.support:support-v4:23.1.1'
      compile 'com.android.support:design:23.1.1'
      }
  6. 打开 Project Structure

  7. 选择 SDK Location,关注 Android NDK location 部分。尚未下载时,此处会出现可点击的链接与提示,直接点击下载即可

  8. 回到./app/build.gradle(module的build.gradle),在 model{} 中加入下述内容,并 Sync 一下 Gradle

    android.ndk {
    moduleName = "Face" // 本行为自定义的native代码module名
    cppFlags.add("-std=c++11") // c++11支持
    cppFlags.add("-fexceptions")
    ldLibs.addAll(["android", "EGL", "GLESv2", "dl", "log", "z"])
    stl = "gnustl_shared"
    }
  9. app module 下新建一个 JNI 文件夹(默认从 src/main/jni 中找 c/c++ 文件)

  10. java 下新建一个 Java 类,我命名为 FaceHelper

  11. FaceHelper.java 中静态加载 native 库,库名为前述 android.ndk{} 中自定义的 moduleName,并添加一个临时方法 ndkTmp

    public class FaceHelper {
    static {
    System.loadLibrary("Face");
    }
    public static native void ndkTmp();
    }
  12. 点击标红的方法名,Alt+Enter 选第一项

  13. 检查 JNI 文件夹下,应该自动创建文件并生成了下述代码

  14. 重命名(Shift + F6Face.c 文件为 cpp 后缀,然后 Sync 一下 Gradle

  15. 回到 FaceHelper.java 文件,声明另一个方法

    public static native void logDebug(String logMsg);
  16. 同样 Alt+Enter ,应该会在 Face.cpp 文件中自动添加该方法的相关代码

  17. 此时,可以删除之前的临时方法 ndkTmp 的相关代码,包括在 FaceHelper.java 中的声明,和在 Face.cpp 中的实现

  18. 添加相关头文件、完成 logDebug 方法,将 JNIEXPORT 的方法都加入到 extern "C"

    #include <jni.h>
    #include <string>
    #include <android/log.h>
    #define LOG_TAG "Andy/FaceCPP"
    #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))

    using namespace std;

    extern "C" {

    JNIEXPORT void JNICALL
    Java_andy_studentingeo_FaceHelper_logDebug(JNIEnv *env, jclass type, jstring logMsg_) {
    const char *logMsg = env->GetStringUTFChars(logMsg_, 0);
    string stdLogMsg(logMsg); //转换成 std 的类型,方便操作

    LOGD(stdLogMsg.c_str()); //在 logcat 中打 log

    env->ReleaseStringUTFChars(logMsg_, logMsg);
    }

    }

现在可以在 MainActivity 的 onCreate 方法中调用该方法来测试了:

FaceHelper.logDebug("Hello NDK");

运行之后,在 logcat 中捕捉到我们设定的 log:

至此,环境搭建完成。往后需要添加方法的时候,可以在 FaceHelper.java 中声明,然后 Alt+Enter 自动完成,接着去 Face.cpp 中把生成的方法代码移动到 extern "C" 中,最后具体实现即可。