使用 AndroidStudio 进行 NDK 开发

AndroidStudio 中进行NDK 开发比起以往的Eclipse 要方便的多,下面来介绍下如何使用AndroidStudio 这个 IDE 工具实现NDK 相关开发工作。

1. 准备工作


在实际写代码之前,首先我们还是需要做一些准备工作:

  1. 下载NDK 开发包:Android 官方下载页面
  2. 配置系统环境变量

下载好NDK 开发包之后,直接解压到任意目录,然后需要配置一下系统环境变量,之所以要配置环境变量,是为了方便使用命令ndk-build 脚本进行NDK 编译。配置参考如下:

# 在.bash_profile中配置如下代码export ANDROID_NDK=/Users/you/android-ndkexport 
PATH=$ANDROID_NDK:$PATH
# 然后执行如下代码,更新配置文件
source .bash_profile

其实编译C/C++代码不一定在AndroidStudio 中,如果配置好环境变量,直接使用进入项目中的jni 目录执行ndk-build 命令即可在当前目录下生成一个libs 的目录,里面存放了不同 平台的.so 包,当然运行这个命令的前提是,这个目录下至少得有一个Android.mk 文件,如果需要指定具体的编译平台,那么还需要添加一个Application.mk 文件,当然,如果命令行让你头疼,那么你可以采用gradle 的方式来解决这些问题,接下来我们将分别介绍这些使用方式。

2. 项目配置

使用AndroidStudio 开发前我们也要做点额外工作,我们需要在项目根目录下local.properties 中添加编译NDK 的路径:

ndk.dir=/Users/you/android-ndk

如果这个文件不存在,你可以手动生成一个,然后再添加上述内容即可。完成这个步骤之后,我们就可以正式开始着手NDK 相关的开发工作了。之所以要配置这个目录,目的是让我们开发的项目在使用 gradle 编译时能够找到NDK 相关编译路径

那么,接下来的工作也分为两种情况:

没有(C/C++)源码,别人已经提供好相应的.so 文件,不需要编译代码
拥有(C/C++)源码,需要自己编译.so 文件

2.1 已有.SO文件,不需要编译源码

这类情况是最简单的,.so 文件以及被其他人员编译好,或者是第三方库来提供的,那么我们只需要把相应.so 文件放到AndroidStudio 目录src/main/jniLibs/ 下即可,当然,肯定需要按 CPU 架构分不同的子目录。
jniLibsAndroidStudio 默认提供的ndk 目录,用来存放已经编译好的.so 文件,当然你也可以放在任意自定义目录下,例如src/main/libs,然后在build.gradle 中指定相应的资源目录位置即可:

android { 
    sourceSets.main { 
        // 你的.so库的实际路径 
        jniLibs.srcDir 'src/main/libs' 
    }
}

在导入.so 文件完成之后,那么你可以在相应的java 类文件中,加载这个静态库,一般来说,.so 文件如果由第三方提供,他在提供.so 文件的同时也会提供相应的java 调用类文件,或者按之前双方定好的规则自己创建相应类文件,并生成相应的方法,之所以要约定好只因为,NDK 下的C/C++
函数和Java 桥接的函数命名是有约束的,规则如下:
Java_PackageName_ClassName_MethodName

双方必须按这个规则来实现或者调用此函数,否则不会成功,例如,我们现在有一个函数:String stringFromJNI()java函数,它在com.example.hellojni.HelloJni 这个文件下,这个函数用来返回一个字符串,功能由底层C 来实现,那么相应的C 语言jni 开发文件中就必须按上述规则命名一个Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz) 的函数,并返回一个字符串结果:

#include 
// 函数名格式必须按规矩来
jstring Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz)
{ 
    return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI ".");
}

同样对应的java 文件也必须:

  1. 文件必须在com.example.hellojni 包名下
  2. 类文件名必须是HelloJni
  3. 方法名必须是stringFromJNI
package com.example.hellojni;

class HelloJni { 
    public static native String stringFromJNI(); 

    static { 
        // 加载 hellojni.so静态块 
        System.loadLibrary("hellojni"); 
    }
}

2.2 有源码,需要编译.so

文件
如果有 C/C++ 源码,没有.so 文件,那么我们就得手动把源码文件编译成.so 文件,编译的方式也分为两种:
手工执行命令经行编译
使用gradle 脚本自动实现编译

AndroidStudio 默认的源码存放目录是:

src/main/jni

如果你没发现此目录,那么你可以手动创建一个,把所有的 C/C++ 源码放在此文件下,当然并非必须要放在此目录下,你可以自定义目录,然后在build.gradle 中做一个资源路径指定即可:

// build.gradle
android { 
    sourceSets.main { 
        // 你的源码目录 
        jni.srcDir 'src/main/otherDir' 
    }
}

2.2.1 手工执行命令经行编译

在使用手工编译(C/C++)文件之前,我们要回到文章开头部分,我们需要配置好系统环境变量,这样我们才能在系统环境下执行ndk相关编译命令,如果您的环境变量还没有配置,那么可以参考下文章开头部分,如果已经做好这部分工作,那么咱们继续。

接下来,我们还要创建如下两个文件:

  1. Android.mk
  2. Applicatoin.mk (非必要)

2.2.1.1 创建 Android.mk

Android.mk 文件用来指定源码编译的配置信息,例如工作目录,编译模块的名称,参与编译的文件等,大致内容如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello_jni
LOCAL_SRC_FILES := hello_jni.c
include $(BUILD_SHARED_LIBRARY)

1. LOCAL_PATH:设置工作目录,而 my-dir 则会返回 Android.mk 文件所在的目录。
2. CLEAR——VARS:清除几乎所有以 LOCAL——PATH 开头的变量(不包括 LOCAL_PATH)。
3. LOCAL_MODULE:用来设置模块的名称。
4. LOCAL_SRC_FILES:用来指定参与模块编译的 C/C++ 源文件名。
5. BUILD_SHARED_LIBRARY:作用是指定生成的静态库或者共享库在运行时依赖的共享库模块列表。

2.2.1.2 创建 Application.mk

这个文件用来配置编译平台相关内容,我们最常用的估计只是APP_ABI
字段,它用来指定我们需要基于哪些 CPU 架构的.so 文件,当然你可以配置多个平台:

APP_ABI := armeabi armeabi-v7a x86 mips

如果不创建Application.mk 文件,那么手动编译的.so 文件只有armeabi 平台一个版本,其他平台的不会被编译。
假设我们配置好了Android.mk 文件,那么接下来我们就可以执行如下命令来生成.so 文件了,我们假设开发NDK
的目录为默认目录:

cd src/main/jni/
ndk-build

如果顺利,那么你将会看到,在src/main/ 目录下会多了一个libs 目录,这是NDK 使用命令编译.so 文件的生成的默认目录,而AndroidSutdio 默认加载NDK 的目录是jniLibs,那么你有两种解决方式:

  1. 配置build.gradle 资源目录,参见文章 2.1 小节
  2. 使用 ndk-build NDK_LIBS_OUT=../jniLibs指定具体的输出目录

生成.so后,为了避免C/C++文件被Java编译,可能需要使用如下gradle 配置:

    sourceSets.main {
        // default .so path
        jniLibs.srcDir 'src/main/libs'
        // disable automatic ndk-build
        jni.srcDirs = []
    }

当你得到了.so 文件,那么接下来就是在java 文件中调用执行即可,如果想了解更多ndk-build 命令内容,可参见:Android ndk-build 使用文档

2.2.2 使用 gradle 脚本

当然该机器做的事我们还是尽量让机器来做,因此,接下来我打算使用build.gradle 来添加一些配置,让Gradle 自动帮我完成编译工作,这简直就是爽歪歪啦!
使用gradle, 你再也不用手动添加Android.mkApplication.mk 文件,一切在build.gradle 文件中就都能搞定,在这里我们直接贴出build.gradle 中ndk 相关的配置:

android.ndk { 
    // 模块名称 
    moduleName = "hello-jni" 

    // 指定编译平台,更多平台信息 参见https://developer.android.com/ndk/guides/abis.html#sa 
    abiFilters "armeabi", "armeabi-v7a" 
    /* * Other ndk flags configurable here are 
    * cppFlags.add("-fno-rtti") 
    * cppFlags.add("-fno-exceptions") 
    * ldLibs.addAll(["android", "log"]) 
    * stl = "system" 
    */ 
}

使用gradle 的好处是,自动编译生成apk 文件,并且把相关的.so 文件打包到apk 安装包中,一劳永逸。

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

推荐阅读更多精彩内容

  • Android游戏开发实践(1)之NDK与JNI开发02 承接上篇Android游戏开发实践(1)之NDK与JNI...
    AlphaGL阅读 3,739评论 0 24
  • 前段时间由于做比赛的事,一直都没时间写博客,现在终于可以补上一篇了,一直想学习一点NDK开发的知识,但是迟迟没有动...
    冰鉴IT阅读 1,739评论 7 18
  • 一、NDK产生的背景 Android平台从诞生起,就已经支持C、C++开发。众所周知,Android的SDK基于J...
    Ten_Minutes阅读 3,473评论 1 27
  • 动态注册JNI函数 其实NDK开发具体操作在NDK官网Guides中已经有了很详细的介绍。。这里主要记录一下我自己...
    Gabo阅读 1,159评论 1 48
  • 2017年11月7日 今天是换食第一天早餐过后没有什么异常的感觉,午餐后午休时盖着热护毯做摆动的时候就迷迷糊糊地睡...
    CiCi爱美丽阅读 292评论 0 0