Android NDK 编程指南(一)

文章测试案例提交到Github:learnNdk

前言

一般的我们在开发Android应用程序时都是用Java语言开发, 但是随着业务要求的提高,纯Java平台可能会达到瓶颈,不同的功能模块如果考虑用其他语言实现可能会有更好的表现,甚至可以做到Java语言达不到的效果.

本篇我们最开始部分来探讨Android平台下如何使用C/C++语言来开发Android应用。本篇的一系列文章都是自己在开发,学习过程中一点一滴的积累,也希望和广大技术爱好者一起交流,共同促进技术的提升,如有不正确的地方,或者有欠缺的地方欢迎大家不吝赐教。

总体分两部分:

  • Android NDK 基础编程
  • Android 常用库的NDK实现

什么是JNI?

JNIJava编程语言中的一个很强大有用的特性,它的存在使得Java类中的某个方法能够用native code实现,同时也能像普通的Java方法一样在Java的类中被调用。Native中的函数也能像Java中一样使用Java中的对象,同时也能创建一个新的Java对象,并且使用这些对象去调用,修改,查询

使用JNI开发的流程

  1. 在Java类中声明一个native方法
  2. 运行javah命令获取包含该方法的C声明的头文件.
  3. C/C++实现native方法.
  4. 将C/C++代码编译成共享库
  5. 在Java程序中加载该类库.
  6. java里面调用这个native方法

下面我们写一个案例:我们计算两个数的和,我们想使用C语言来实现这个函数的功能。
当然这个功能我们可以使用Java代码实现。为了学习JNI我们首先应该从最简单的案例入手。

1.1声明本地方法

/**
 * 将参数传从Java层传递给JNI层,在jni层处理完之后,在返回给Java层
 * @param a,b
 * @return 处理之后的结果
 */
 public native int add(int a, int b);

1.2获取头文件

使用JDK中的javah命令,注意Javah的目标是字节码文件,因此我们要提前将Java文件编译成Class文件,然后进入到字节码文件的工程目录使用命令生成头文件.

// 直接在Android studio 命令窗口下进行如下操作
cd \app\build\intermediates\classes\debug.
// Javah 类的完整类名
javah com.sivin.ndkdemo.NormalJni

如果没有什么错误就可以发现在debug目录下生成了一个NormalJni.h的头文件,下面我可以就可以使用该头文件了。

那么为什么要这个头文件呢?事实上也可以不要这个头文件,我们知道在C/C++中,头文件就是声明函数用的,其实,如果想让Java可以调用native层代码,函数需要特定的命名才行(Java_包名_类名_方法名)和一些特定的参数而已,这些如果让开发这手写,未免有点繁琐,同时也容易写错,因此Jdk直接提供了javah的命令帮助我们生成了这个头文件,这样就不用我们自己写了。

如果你使用的是Android studio 2.2以上版本,可以新建项目的时候勾选上support c++,这样就可以在书写native方法的时候,直接自动生成对应的对应的函数规范,不需要在做上面的工作了,这也是工具的方便。但是原理我觉得还是有必要搞清楚。

1.3编写Native代码

通过上一步我们得到了Javanative方法对应的C层的方法,下面我们就来具体实现以下这个方法

JNIEXPORT jint JNICALL
Java_com_sivin_ndkdemo_NormalJni_add(JNIEnv *env, jobject instance, jint a ,jint b) {
    return a +b;
}

下面我们具体的任务就是实现native里面的代码,实现部分就是C/C++里面的知识了,这里我并不想过多的讲解如何使用C/C++来去处整数的功能。如果你对C/C++不是太了解,建议你先去学习一下。

1.4 Java代码中加载共享库

static{
    System.loadLibrary("共享库名称");
}

为什么使用静态代码块加载?

当类加载的时候就应该去加载共享库.

1.5 Java代码中使用这个native 方法

经过上面的几部,我们已经将C/C++代码打包成动态库,并且已经加载进虚拟机,此时我们可以向使用普通的java方法一样使用这个native函数。

2.知识点梳理

上面的基本过程,其实就是很基本的JNI的基本知识,相信了解过JNI的人都清楚。相信大家实践一下,就可以实现一个demo了。下面我们来讲解一下这个案例所设计的基本知识点。

几个很重要的参数JNIEnv *env,jobject jobject,jclass jcls

Java代码如何与C/C++代码进行通信,这正是我们的主角--->JNI要解决的问题,我们发现每一个对应Java层方法的C 函数第一个参数都是JNIEnv *env这个参数,那么这个参数到底是什么呢?打开jni.h这个头文件,有人想问,这个头文件在哪?这个问题。。。好吧刚接触JNI的小白确实不知道,首先Java想要与C/C++通信,肯定是Java的工作,难不成还是C/C++的工作吗?知道了这个,我们很自然的想到JDK开发包,我们打开我们的JDK安装目录,里面有一个include目录,这个目录下就可以找到。
我们打开可以看到这样的代码:

struct JNINativeInterface_;
struct JNIEnv_;

#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif

文件首先声明了两个结构体类型:然后使用宏定义来重新定义JNIEnv,我们可以看到CC++JNIEnv是不同的。有什么不同呢?这里我们先不讨论,我们目前是以C语法学习,后面我们会讲解和C++不同。
我们看到JNIEnvC里面定义的原型是一个JNINativeInterface_ *是一个结构体指针,下面我们来简单的看看这个结构体到底是什么。

struct JNINativeInterface_ {
    void *reserved0;
    void *reserved1;
    void *reserved2;

    void *reserved3;
    jint (JNICALL *GetVersion)(JNIEnv *env);
    
    jclass (JNICALL *DefineClass)
    (JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
       jsize len);
        
        ...
}

这个结构体定义的东西很多,对C不熟悉的人,咋一看感觉,有点复杂啊,其实,仔细想想,C的结构体里能翻什么大浪,无非就是定义一些变量嘛。前三个是指针变量,后面的呢?认真一瞅,唉。。。也是指针变量,是函数指针变量
这么一来我们就似乎懂了,通过这个结构体,我们可以得到里面的函数指针,通过这个函数指针我们可以调用相关的函数。那么到底是什么函数呢?我就不卖关子了,这些函数正是我们JavaC相互调用的桥梁。是开发java虚拟机这帮大牛们帮我们实现的。

我们发现,这里面提供很很多方法接口,它给我们提供了很多从CJava或者从javaC的转换,通俗的来讲这就是一个纽带

这样我们就搞定了第一个参数:

`JNIEnv *env`:通过这个对象我们可以获取到很多从`C`到`Java`或者从`java`到`C`的转换

第二个参数:jobjectjclass: 我们注意到这两个参数一般并不是同时出现的,那么这两个参数代表什么呢?我们仔细观察发现,当我们的java native方法是对象方法时,我们生成的对应的native code第二个参数就是jobject 如果我们的方法是static时我们生成的native code就是jclass,想必大家其实已经猜到了,jobject就是我们调用这个方法时所属的对象。jclass其实就是这个静态方法所属的Class类。
这两个参数会十分有用,我们在后面会更加具体的讨论这个两个参数的作用和意义。

本篇内容就到此为止了。接下来我们会具体的讨论,JNIEnv *里具体有哪些转换函数,以及JNInative codejava code之间数据传递是如何转换的,学习这些的前提是先把本篇内容写一个demo出来,弄懂这demo所表达出来的知识内容。建议初学者使用android studio 2.2以上的实现这个demo,至于如何使用Cmake我们会有后续文章讲解。

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

推荐阅读更多精彩内容