Android JNI开发工具篇(1)-开发环境搭建

原文出处:http://www.ccbu.cc/index.php/android/android-jni-dev-env.html

工欲善其事,必先利其器

1. 开发环境准备

搭建开发环境是我们进行开发前首先要完成的任务,进行Android jni开发,依赖的基本开发环境包括:

  1. Android sdk
  2. android ndk
  3. cmake
  4. android studio

Android studio的sdk manager已经包括了上面所说的sdk,ndk,cmake等工具的安装,所以一般只用下载android studio,然后再使用sdk manager工具下载这些工具就可以了。

默认情况下,Android studio使用的编译工具是cmake,但很多沿用的项目都是使用NDK的ndk-build工具来编译的,所以android studio也支持ndk-build。

2. 使用android studio创建本地C++工程

1.新建工程, 在向导的 Choose your project 部分中,选择Natvie C++ 项目类型 。

2.在设置工程名,包名,保存路径和语言,此处我们选择Java语言。

3.在向导的 Customize C++ Support 部分中,可以选则C++ Toolchain,一般情况下,选择默认就可以,如果开发中需要用到C++11,或者c++14等一些较高级的C++标准的特性时,可以选择对应的Toolchain

4.点击finish,开始构建工程,工程构建完成以后,整个项目及其gradle配置文件如下:

默认情况下,Android studio使用cmake编译链工具,通过gradle脚本进行配置,默认cmake配置如下:

externalNativeBuild {
    cmake {
        path "src/main/cpp/CMakeLists.txt"
        version "3.10.2"
    }
}

cmake文件和c++源代码都在src/main/cpp/目录下。

Android studio也支持ndk-buid,根据实际需求,我们也可以配置为ndk-build,当然,这需要我们先写好对应的Android.mkAppplication.mk(可选)配置文件,然后通过修改gradle配置中的externalNativeBuild配置项来进行更改。配置为nkd-build编译工具,则其配置文件如下:

externalNativeBuild {
    ndkBuild {
        path file('src/main/cpp/Android.mk')
    }
}

3. 使用现有android studio工程链接C++工程

当一个普通的不带C++本地库支持的项目需要引入一个现有的c++本地库时,可以使用android studio的Link C++ Psroject with Gradle功能来导入一个本地C++库,导入的库需要提供可用的cmake配置文件或Android.mk配置文件,导入工作是通过加载这些本地库编译配置文件来完成的。

4.在android studio配置javah工具

在Settings->Tools->External Tools下创建NDK group,在NDK group下创建javah工具。

详细的配置参数如下:

配置项 参数
Programe $JDKPath$\bin\javah.exe
Arguments -classpath $ModuleFileDir$\src\main\java -jni -d $ModuleFileDir$\src\main\cpp $FileClass$
Working directory $FileDir$

使用时,只需要在定义了native方法的java类上右键选择NDK->javah即可生成对应的c++本地函数定义的头文件。

截图中例子中,TestJni.java定义了本地函数add

package com.android.jnitest;

public class TestJni {
    public native int add(int a, int b);
}

使用javah生成的对应本地c++头文件com_android_jnitest_TestJni.h内容如下:

#include <jni.h>
/* Header for class com_android_jnitest_TestJni */

#ifndef _Included_com_android_jnitest_TestJni
#define _Included_com_android_jnitest_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_android_jnitest_TestJni
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_android_jnitest_TestJni_add
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

5. 工程配置

5.1 可选参数配置

可以在模块级 build.gradle 文件的 defaultConfig 块中配置另一个 externalNativeBuild 块,为 CMake 或 ndk-build 指定可选参数和标记 。

android {
    ...
    defaultConfig {
        ...
        // This block is different from the one you use to link Gradle
        // to your CMake or ndk-build script.
        externalNativeBuild {
          // For ndk-build, instead use the ndkBuild block.
          cmake {
            // Passes optional arguments to CMake.
            arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
            // Sets a flag to enable format macro constants for the C compiler.
            cFlags "-D__STDC_FORMAT_MACROS"
            // Sets optional flags for the C++ compiler.
            cppFlags "-fexceptions", "-frtti"
          }
        }
    }
    ...
}

通过上面的配置,可用对cmake的编译选项及C和C++编译选项做一些配置。

5.2 指定 ABI

默认情况下,Gradle 会针对 NDK 支持的应用二进制接口 (ABI) 将您的原生库编译到单独的 .so 文件中,并将这些文件全部打包到您的 APK 中。如果您希望 Gradle 仅编译和打包原生库的部分 ABI 配置,您可以在模块级文件 build.gradle 中使用 ndk.abiFilters 标记指定这些配置,如下所示:

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
        cmake {...}
        // or ndkBuild {...}
        }

        // Similar to other properties in the defaultConfig block,
        // you can configure the ndk block for each product flavor
        // in your build configuration.
        ndk {
        // Specifies the ABI configurations of your native
        // libraries Gradle should build and package with your APK.
        abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
        'arm64-v8a'
        }
    }
    buildTypes {...}
    externalNativeBuild {...}
}

6. 原生库支持

Android NDK 会提供一组随着新的 Android API 级别的后续发布而逐渐添加的原生标头和共享库文件。 请执行以下两个基本步骤的操作,以便让您的应用使用 NDK 提供的库:

  1. 在您的代码中添加与您想使用的库关联的标头。

  2. 通知编译系统您的原生模块需要在加载时链接库。

    • 如果您使用的是 ndk-build:将原生库添加到您 Android.mk 文件中的 LOCAL_LDLIBS 变量中。例如,要链接 /system/lib/libfoo.so,请添加以下这行代码:
    LOCAL_LDLIBS := -lfoo  
    

    要列出多个库,请使用空格作为分隔符。

    • 如果您使用的是 CMake,向 CMake 编译脚本添加 find_library() 命令以找到 NDK 库并将其路径存储为一个变量。

      find_library( # Defines the name of the path variable that stores the
                        # location of the NDK library.
                        log-lib
                        # Specifies the name of the NDK library that
                        # CMake needs to locate.
                        log )
      

      然后再 CMake 脚本中的 target_link_libraries() 命令来关联库:

      target_link_libraries( # Specifies the target library.
                                 native-lib
                                 # Links the log library to the target library.
                                 ${log-lib} )
      

常用的android原生库包括以下一些:

API级别 库名 链接代码 说明
3 C 库 系统自动添加,无需配置
3 动态链接器库 LOCAL_LDLIBS := -ldl 动态链接器的 dlopen(3) 和 dlsym(3) 功能
3 Android日志库 LOCAL_LDLIBS := -llog 原生代码向 logcat 发送日志消息
3 ZLib 压缩库 LOCAL_LDLIBS := -lz
4 OpenGL ES 1.x LOCAL_LDLIBS := -lGLESv1_CM
5 OpenGL ES 2.0 LOCAL_LDLIBS := -lGLESv2
8 jnigraphics LOCAL_LDLIBS += -ljnigraphics
9 EGL LOCAL_LDLIBS += -lEGL 分配和管理 OpenGLES 表面的原生平台接口
9 OpenSL ES LOCAL_LDLIBS += -lOpenSLES 原生音频处理库
9 Android 原生应用api LOCAL_LDLIBS += -landroid 使用原生代码编写整个 Android 应用
14 OpenMAX AL LOCAL_LDLIBS += -lOpenMAXAL 原生多媒体处理库
14 OpenSL ES LOCAL_LDLIBS += -lOpenSLES 增加了 PCM 支持
18 OpenGL ES 3.0 LOCAL_LDLIBS := -lGLESv3
21 OpenGL ES 3.1 LOCAL_LDLIBS := -lGLESv3
24 OpenGL ES 3.2 LOCAL_LDLIBS := -lGLESv3

7. C++ 库支持

7.1 C++ 运行时库

NDK 支持多种 C++ 运行时库。

名称 库文件 功能
libc++ 共享库为 libc++_shared.so
静态库为 libc++_static.a
C++17 支持。
system /system/lib/libstdc++.so newdelete。(在 r18 中已弃用。)
none 无头文件,有限 C++。

libc++

libc++ 同时提供静态库和共享库 。LLVM 的 libc++ 是 C++ 标准库,自 Lollipop 以来 Android 操作系统便一直使用该库,并且从 NDK r18 开始成为 NDK 中唯一可用的 STL。libc++ 的共享库为 libc++_shared.so,静态库为 libc++_static.a。

system

系统运行时指的是 /system/lib/libstdc++.so。请勿将该库与 GNU 的全功能 libstdc++ 混淆。在 Android 系统中,libstdc++ 只是 newdelete。对于全功能 C++ 标准库,请使用 libc++。

none

不包括STL。在这种情况下,没有关联或授权要求。不提供 C++ 标准头文件。

7.2 配置C++ 运行时

如果您要使用 CMake,则可使用模块级 build.gradle 文件中的 ANDROID_STL 变量,指定表表格中的一个运行时 。如果您要使用 ndk-build,则可使用 Application.mk 文件中的 APP_STL 变量指定表 1 中的一个运行时。

APP_STL := c++_shared

只能为应用选择一个运行时,并且只能在 Application.mk 中进行选择。

7.3 共享运行时

如果应用包括多个共享库,则应使用 libc++_shared.so

在 Android 系统中,NDK 使用的 libc++ 不是操作系统的一部分。这使得 NDK 用户能够获得最新的 libc++ 功能和问题修复程序,即使应用以旧版 Android 为目标。需要权衡的是,如果使用 libc++_shared.so,则必须将其纳入 APK 中。如果使用 Gradle 编译应用,则此步骤会自动完成。

7.4 C++ 异常

C++ 异常受 libc++ 支持,但其在 ndk-build 中默认为停用状态。这是因为之前 NDK 并不支持 C++ 异常。CMake 和独立工具链默认启用 C++ 异常。

要在 ndk-build 中针对整个应用启用异常,请将下面这一行代码添加至 Application.mk 文件:

APP_CPPFLAGS := -fexceptions

要针对单一 ndk-build 模块启用异常,请将下面这一行代码添加至相应模块的Android.mk中:

LOCAL_CPP_FEATURES := exceptions

或者,您可以使用:

LOCAL_CPPFLAGS := -fexceptions

7.4 RTTI

与异常一样,RTTI 也受 libc++ 支持,但在 ndk-build 中默认为停用状态。CMake 和独立工具链默认启用 RTTI。

要在 ndk-build 中针对整个应用启用 RTTI,请将下面这一行代码添加至 Application.mk文件:

APP_CPPFLAGS := -frtti    

要针对单一 ndk-build 模块启用 RTTI,请将下面这行代码添加至相应模块的 Android.mk中:

LOCAL_CPP_FEATURES := rtti    

或者,您可以使用:

LOCAL_CPPFLAGS := -frtti

参考:

将 Gradle 关联到您的原生库

向您的项目添加 C 和 C++ 代码

Android NDK 原生 API

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容