NDK开发学习笔记之 javah 及 ndk-build

上一篇写到 NDK 的基本使用及环境搭建。
并写了一个简单的 cpp 文件,但是有没有发现在编写 cpp 文件中的函数时,名字是不是很长,很容易写错!
那么,哟没有方法,自动生成函数名呢?没错,你猜对了,还真有这样的工具—— javah。下面就来介绍 javah 的使用以及以下小技巧,此外还有 ndk-build 生成 so 的方法。

概要

  • javah 的使用
  • ndk-build 的使用

1. javah

上一篇讲过,jni 中函数名的书写方式,再来回顾一下。

(1)函数名:JNIEXPORT + 返回类型 + JNICALL Java_+包名 +类型 + 函数名(java 中声明的),以下划线连接

(2)返回值类型,是 jni 中的数据类型,若没有返回类型,则使用void

(3)默认传入两个参数 JNIEnv* env(jvm运行环境), jobject obj(调用这个函数的Java对象)

#include <jni.h>
extern "C" {

/*
 * Class:     com_ralf_www_jnitest_JniUtils
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_ralf_www_jnitest_JniUtils_getString
   (JNIEnv *env, jclass jc){

     const char* ch = "String From JNI";
     return env->NewStringUTF(ch);
   }

 }

但是这么长的名字很容易写错,特别在包名中出现一些特殊符号时,如,下划线(com.example.my_jni),这时候可以使用 javah 来自动生成头文件,里面会有完整的函数名。下面就来看一下实现的步骤。
主要是 4 个步骤:

  • 利用 javah 命令来编译出对应的头文件,
  • 建立 cpp 文件,如 MyJni.cpp,然后复制头文件中函数名,粘贴进去,然后再编写函数的方法体
  • 在 android.mk 中添加自己新建立的 cpp 文件名
  • 编译 so 库

详细说明:

(1) 在 main 文件夹下建立 jni 文件夹
切换到 java 文件夹下,shift +右键,打开命令行窗口,执行下面命令

javah -classpath ./java -d ../jni com.ralf.www.jnitest.JniUtils

注意:

  • -classpath 表示类文件路径

  • ./java 表示java当前的文件夹

  • -d表示生成头文件所在路径

  • ../jni 表示 jni 文件夹下,这样写是因为当前文件夹时 java 文件夹下,..表示上一级文件夹,即 main,再加上 /jni 表示 jni 文件夹

  • com.ralf.www.jnitest.JniUtils,表示需要编译的类文件,也就是包名+类名(也就是包含 native 方法的类文件)

GBK编译

忽略提示错误,“错误,编码 GBK 的不可映射字符”,此时,对应的头文件已经生成。可以打开看到里面的内容,如下。


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ralf_www_jnitest_JniUtils */

#ifndef _Included_com_ralf_www_jnitest_JniUtils
#define _Included_com_ralf_www_jnitest_JniUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ralf_www_jnitest_JniUtils
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_ralf_www_jnitest_JniUtils_getString
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

(2) 在 jni 文件及下新建一个源文件 .cpp 文件,拷贝头文件中的内容到 cpp 文件中,只保留 extern "C"{。。。} 中的内容

注意,函数的参数(JNIEnv , jclass)中,需要更改下,(JNIEnvenv, jclass jc)

(3) 在 android.mk 中添加需要编译的文件,即 MyJni.cpp

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := libjnitest
LOCAL_SRC_FILES := MyJni.cpp

include $(BUILD_SHARED_LIBRARY)

2.编译 so 库

方式一:ndk-build

方式二:利用gradle

(1)ndk-build:

ndk-build 文件是 Android NDK r4 中引入的一个 shell 脚本。其用途是调用正确的 NDK 构建脚本。上一篇文章中也已经给出 NDK 的相关介绍,脑图中列举了基本的NDK先关知识点。
要想使用 ndk-build 命令,首先需要将该命令添加到环境变量,要不然使用会有点麻烦,添加环境变量的操作这里就不赘述了,请自行百度、谷歌。

主要说下怎么操作:

切换到工程的 main 文件夹下--->shift + 右键 开启命令行窗口--->输入 ndk-build 命令执行编译,生成 so 库。So 库放置的位置是在 main 文件夹下的 libs 文件夹,里面有不同机器的 so 库,生成不同版本的 so 的设置在 Application.mk 文件中设置,本例中设置 APP_ABI :=all

lib路径

(2)利用 gradle 配置

a. 指定 NDK 路径

在 local.properties 文件下设置

ndk.dir=D\:\\Android\\android-ndk-r10e
sdk.dir=D\:\\Android\\sdk

b. 指定编译的 Android.mk 文件

android {
…
  externalNativeBuild{


      //指定 Android.mk 文件
      ndkBuild{
          path 'src/main/jni/Android.mk'
      }
  }
}

c. 指定property.gradle文件设置

android.useDeprecatedNdk=true

这些操作在上一篇文章中都讲过了,多熟悉几次对你来说应该很简单。

生成的 so 库所在路径,稍微有点隐蔽,看下图

so路径

(3)利用 gradle 配置还有一个快捷方式,一键完成。
在 jni 文件夹上右键选择 “Link C++ Project with Gradle” 一项,然后选择你工程中 android.mk 文件,这样就完成了配置。

3.小技巧

个人认为这个小技巧真的很好用。

(1)命令快捷窗口

利用 javah 命令生成头文件的命令可以在 terminal 窗口输入

javah快捷工具

同样,ndk-build 命令也可以在 terminal 窗口执行

ndk-build快捷工具

(2)将命令设置成快捷工具

在 Settings----External Tools 中设置快捷工具
在设置界面,找到 External Tools,如下图,然后点击右边方框的 “+”,添加 javah 和 ndk 命令快捷工具

  • javah 快捷工具
external工具

参数设置:

1.Program: $JDKPath$\bin\javah.exe 这里配置的是 javah.exe 的路径

2.Parametes: -classpath . -jni -d $ModuleFileDir$/src/main/jni $FileClass$ 这里 $FileClass$ 指的是要执行操作的类名(包名.类名),$ModuleFileDir$/src/main/jni表示生成的文件保存在这个module目录的src/main/jni目录下。

3.Working: $ModuleFileDir$\src\main\java

使用方式:
选中 java 文件--->右键---> External Tools--->javah-jni,将生成 jni 文件夹以及文件夹下的包名.类名的.h头文件 (名字过长,我们可以自己重命名)。

  • ndk-build 命令快捷方式
ndk编译工具

设置参数:

1.Program: F:\apk\sdk\ndk-bundle\ndk-build.cmd 这里配置的是 ndk 下的 ndk-build.cmd 的路径(根据实际情况填写)

2.Working: $ModuleFileDir$\src\main\

使用方式:
选中 C/C++ 文件--->右键--->ExternalTools--->ndk-build,将在 main 文件夹下生成 libs 文件夹以及多个 so 文件,我们可以移动至 jniLibs 目录下去。

当然,还有有一种使用 Cmakelist 方式使用 jni 的方法,这种方法时 AS 新增的功能,后面的文章会介绍该方式的使用方法,敬请期待!

代码地址

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

推荐阅读更多精彩内容