使用 NDK 13 编译独立的二进制文件

Compile standalone binaries with NDK 13

对于 NDK 10 版本,我曾经使用 ndk-build 为许多不同的 ABI 和多个 API 级别编译独立的二进制文件。这些二进制文件将包含在应用程序中。但是,我在一台新的开发机器上安装了 NDK described in this article. This resulted in a folder ndk-bundle within my Android SDK directory. I used to compile the code from a command line and then copy the binaries to the resources of my Android Studio project, but I could not figure out how to do this with NDK 13 so I tried to follow the tutorial to include my native code in the Android Studio project。然而,几乎所有最近的说明都假定人们想要构建一个库,而不是一个独立的二进制文件,所以我没有走得太远。

如果我知道如何让它工作,我会切换到 CMake。我的原生项目具有以下(简化)结构:

如何在我们的 Windows 10 台开发机器上使用 Android Studio 或命令行中的 NDK 编译它?

编辑:

我在build.gradle中使用这个:

externalNativeBuild {
    ndkBuild {
        path "../native/Android.mk"
    }
}

Gradle 创建一个包含构建配置的目录 .externalNativeBuild,但我无法找到如何实际构建本机代码。当我 运行 gradle.

时没有创建二进制文件

我找不到 关于 ndk-build 的 gradle 配置的任何 信息。

如果您所做的只是更改 NDK 版本,那么我认为这非常简单,我们遗漏了一些简单的东西。我认为您需要更新 PATH 环境变量以指向新的 ndk-bundle 路径并再次尝试 运行ning ndk-build

根据我的经验,我无法让 Android 的 Cmake 生成可执行文件(独立二进制文件)或静态库作为最终输出。因为我希望最终输出是可执行文件,所以我不得不改回使用 NDK 而忘记了 Cmake。

更新

这是使用 Android Studio 和 NDK 构建 C++ 可执行文件的方法。这没有使用 CMake,即使您没有 JNI 代码也是如此。

app/build.gradle

sourceSets.main {
    // Do not run default ndkbuild
    jni.srcDirs = []
    // Place where .so libs are available
    jniLibs.srcDir 'src/main/libs'
}

task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
    def ndkDir = android.ndkDirectory
    commandLine "$ndkDir/ndk-build.cmd", 'V=1',
            '-C', file('src/main/jni').absolutePath,
            '-j', Runtime.runtime.availableProcessors(),
            'all',
            'NDK_DEBUG=1'
}

task cleanNative(type: Exec, description: 'Clean JNI object files') {
    def ndkDir = android.ndkDirectory
    commandLine "$ndkDir/ndk-build.cmd",
            '-C', file('src/main/jni').absolutePath,
            'clean'
}

目录结构

您需要一个名为 app/src/main/jni 的文件夹。如您所见,上面的 gradle 代码只是针对该目录调用 ndk-build。以下是我建议您设置目录的方法:

  • jni

    • Android.mk

      LOCAL_PATH := $(call my-dir)
      include $(call all-subdir-makefiles)
      
    • Application.mk

      APP_ABI := all
      APP_PLATFORM := android-21
      
    • 原生

      • my_tool
        • 来源
          • main.c
      • Android.mk

        include $(CLEAR_VARS)
        LOCAL_C_INCLUDES := my_tool/src
        LOCAL_PATH := $(call my-dir)
        LOCAL_MODULE := my_tool
        LOCAL_SRC_FILES := my_tool/src/main.c
        include $(BUILD_EXECUTABLE)
        

建筑

要构建,您可以从 app/src/main/jni 目录中 运行 ndk-build,或者从项目的根目录中 运行 gradle buildNative。您可以看到在我上面显示的 gradle 代码中,有一个名为 buildNative 的任务,因此这就是将要执行的 gradle 代码。

顶级 Android.mk 文件在您添加更多要在 app/src/main/jni 下构建的模块时提供帮助,它遍历该目录中的所有子目录并调用任何其他 Android.mk 它找到的文件。

我尽量遵循你的简化结构。

这是文件 app/build.gradle:

apply plugin: 'com.android.library'

android {
    compileSdkVersion 24
    buildToolsVersion "25.0.1"
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 24
        externalNativeBuild {
            ndkBuild {
                targets "my_tool"
                abiFilters "armeabi-v7a"
            }
        }
    }
    externalNativeBuild {
        ndkBuild {
            path "../native/Android.mk"
        }
    }
}

文件 native/Android.mk 与您的相同:

LOCAL_PATH := $(call my-dir)/my_tool/src
include $(CLEAR_VARS)
LOCAL_MODULE    := my_tool
LOCAL_SRC_FILES := main.c
include $(BUILD_EXECUTABLE)

我还有文件 native/main.c 和最小的 app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="my.tool" />

我没有接触 build.gradle 由 Android Studio 向导生成的根脚本:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0-alpha3'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

现在我可以构建项目了,这是我得到的结果:

$> file ./app/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/my_tool
ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

Android Studio 在默认视图的 cpp 文件夹中显示我的 main.c

更新:要将可执行文件剥离并打包到 APK 中,必须更改 native/Android.mk

LOCAL_PATH := $(call my-dir)/my_tool/src

install: LIB_PATH := $(call my-dir)/libs

include $(CLEAR_VARS)
LOCAL_MODULE    := my_tool
LOCAL_SRC_FILES := main.c
include $(BUILD_EXECUTABLE)

install: $(LOCAL_INSTALLED)
    -mkdir $(LIB_PATH)
    -rm -r $(LIB_PATH)
    mv $< $(<:my_tool=lib-my_tool-.so)
    mv $(realpath $(dir $<)..) $(LIB_PATH)

.PHONY: install

另外,app/build.gradle 需要一些调整:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "25.0.1"
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 24
        externalNativeBuild {
            ndkBuild {
                targets "my_tool"
                abiFilters "armeabi-v7a"
                arguments 'V=1', 'install'
            }
        }
    }
    externalNativeBuild {
        ndkBuild {
            path "../native/Android.mk"
        }
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['../native/libs']
        }
    }
}

这依赖于 old hack,它依赖于 NDK 的未记录行为,并且可能会在未来的 NDK 升级时中断,恕不另行通知。