【问题标题】:Compile standalone binaries with NDK 13使用 NDK 13 编译独立二进制文件
【发布时间】:2017-04-25 19:10:33
【问题描述】:

在 NDK 10 版本中,我曾经使用 ndk-build 为许多不同的 ABI 和多个 API 级别编译独立的二进制文件。这些二进制文件将包含在应用程序中。然而,我在一台新的开发机器上安装了 NDK 为described in this article。这导致我的 Android SDK 目录中有一个文件夹 ndk-bundle。我曾经从命令行编译代码,然后将二进制文件复制到我的 Android Studio 项目的资源中,但我不知道如何使用 NDK 13 执行此操作,因此我尝试关注 the tutorial to include my native code in the Android Studio project。然而,几乎所有最近的指令都假设一个人想要构建一个库,而不是一个独立的二进制文件,所以我没有走多远。

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

  • 原生

    • 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)
      
    • 应用程序.mk

      APP_ABI := all
      APP_PLATFORM := android-21
      
    • 我的工具

        • main.c

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

编辑:

我在 build.gradle 中使用它:

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

Gradle 创建了一个目录.externalNativeBuild,其中包含构建配置,但我不知道如何实际构建本机代码。运行 gradle 时不会创建任何二进制文件。

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

【问题讨论】:

标签: android android-studio android-ndk native ndk-build


【解决方案1】:

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

这是 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" />

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

// 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 文件夹中显示我的 ma​​in.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 升级时中断,恕不另行通知。

【讨论】:

  • 太棒了!生成的二进制文件会自动包含在 APK 中吗?
  • 我认为它们不会自动添加到 APK 中。实际上,二进制文件不会自动从 APK 中提取。我曾经为此提出过hack,但我从未在 Android 5 或 6 上测试过。
  • 提取没问题,我们已经得到了它的代码。这只是意味着我们需要手动添加一个步骤来将二进制文件添加到 APK,但之前是一样的,所以没有改变。
  • 感谢您的回答,我接受了并授予您赏金。剥离工具是 NDK 捆绑包的一部分吗?您能否提供一个剥离和打包所有架构的可执行文件的示例,以便我以后提取它们?
  • 我已更新答案以强制执行 strip。要强制打包,old hack 似乎是不可避免的。
【解决方案2】:

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

根据我的经验,我无法让 Cmake for Android 生成可执行文件(独立二进制文件)或静态库作为最终输出。因为我希望最终输出是可执行文件,所以我不得不切换回使用 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)
      
    • 应用程序.mk

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

      • 我的工具
          • 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 文件。

【讨论】:

  • 我们无法访问旧工具链,我们只能获得新设置的代码。这就是为什么我正在寻找有关如何编译代码的具体说明,最好是通过 Android Studio,但命令行解决方案也是可以接受的。我们不仅更改了 NDK 版本,还安装了全新的机器。
  • 我真的觉得我能帮上忙。几天前,我使用 NDK 13 完成了此操作。当我说切换回使用 NDK 时,我的意思是您可以使用任何版本,甚至 13。您收到什么样的错误消息? Android Studio Run 按钮只是一个包装器,它最终会从您的 jni 文件夹中调用 ndk-build。你有app/src/main/jni 文件夹吗?
  • 不,我没有这样的文件夹,因为我没有使用 JNI。 JNI 只支持原生库代码,我不编译库。还是jni 文件夹也应该包含其他本机代码?我将编辑我的问题以回答另一部分。
  • 对。我认为我所做的与您尝试做的类似。即使我没有尝试使用 JNI 和本机代码构建 Android 应用程序,我仍然有一个 jni 文件夹,以便我可以让 Android Studio 为我构建我的项目。但我仍然构建了一个纯 C++ 项目,以便可以在 Android 模拟器上运行它。我将添加另一个包含更多详细信息的答案。
  • 我更新了我的答案,而不是添加另一个答案。让我知道这是否有帮助。
猜你喜欢
  • 2017-02-20
  • 1970-01-01
  • 2015-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-16
  • 1970-01-01
相关资源
最近更新 更多