在自己阅读 <<Android 系统框架>> 一书时,在其中章节有相关 JNI 的描述中我得知可以通过 Java 代码中的本地方法的声明可以生成相应 C/C++ 的头文件,进而可以在 C/C++ 源文件中进一步实现,真是知识让我渺小 。
1、编辑 Java 源代码
- HelloJNI.java
public class HelloJNI {
// 本地方法声明
native int getNum();
// 加载本地库文件
static {
System.loadLibrary("hellojni");
}
}
2. Android Studio 配置 javah 工具 ( Linux 环境)
打开 setting > Tools > External Tools 工具栏,点击 +,具体配置信息如下图:
其中具体配置内容分别如下:
usr/bin/javah
-v -jni -d $ModuleFileDir$/src/main/jni $FileClass$
$SourcepathEntry$
其中第二行为自定义生成的头文件的位置,可以按照自己意愿定义。
3. 生成 C/C++ 文件
选中相应的 Java 源代码,右键点击 External Tools ,点击刚才配置成功的 javah
工具,这样就会在 java 目录下自动生成 jni
目录(自定义),目录下生成相应的 C/C++ 头文件。
- HelloJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lee_cmaketest_cmaketestdemo_HelloJNI */
#ifndef _Included_com_lee_cmaketest_cmaketestdemo_HelloJNI
#define _Included_com_lee_cmaketest_cmaketestdemo_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_lee_cmaketest_cmaketestdemo_HelloJNI
* Method: getNum
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_lee_cmaketest_cmaketestdemo_HelloJNI_getNum
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
这里在头文件中没有实现的函数成为 函数原型。
如何把运行库中的 C/C++ 函数与 Java 代码的本地函数方法映射到一起呢?答案就是 函数原型。当生成了函数原型, Java 虚拟机即可把本地库中相应的函数与 Java 本地方法映射在一起。
函数原型各部分意义:
JNIEXPORT jint JNICALL Java_com_lee_cmaketest_cmaketestdemo_HelloJNI_getNum
(JNIEnv *, jobject);
JNIEXPORT、JNICALL : JNI 关键字,表示该函数要被 JNI 调用
jint : 返回值类型
com_lee_cmaketest_cmaketestdemo_HelloJNI: 类名
getNum: 方法名
( … ): 参数
4. 编写 C/C++ 源码文件
- HelloJNI.cpp
#include "HelloJNI.h"
#include <stdio.h>
JNIEXPORT jint JNICALL Java_com_lee_cmaketest_cmaketestdemo_HelloJNI_getNum
(JNIEnv *env, jobject object){
int num;
num = 200;
return num;
}
5. 编写 CMakeList.text
cmake_minimum_required(VERSION 3.4.1)
add_library(
hellojni
SHARED
src/main/cpp/hellojni.cpp)
指定库的名称、属性、源码位置
6. Java 调用 C/C++ 代码
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
HelloJNI mHelloJNI = new HelloJNI();
tv.setText(mHelloJNI.getNum());
}
}
则屏幕显示信息为 200 。
至此,本地方法的声明、C/C++ 头文件的声明、C/C++ 源码的编写、本地运行库的配置、 JNI 调用全部完成。
疑惑
按照 <<Android 系统框架>> 一书在 Terminal 终端按照如下步骤生成 C/C++ 头文件失败,具体操作如下:
javac HelloJNI.java // 成功生成 .class 文件
// 以下操作生成C/C++ 头文件均失败
javah HelloJNI
或
javah -classpath app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/lee/cmaketest/cmaketestdemo com.lee.cmaketest.cmaketestdemo.HelloJNI
或
javah -classpath ~/AndroidSDK/Sdk/platforms/android-28/android.jar;. -jni com.lee.cmaketest.cmaketestdemo.HelloJNI
如有知道原因的朋友,欢迎留言。
0x00 11.15 更新
今天偶尔看到一篇博客,通过 javah 来生成 java 的头文件的方式如下:
javah -d -jni -classpath /Users/liguoying/Git/CMakeTest/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes com.lgy.cmaketest.HelloWorld
参数解释:
- -d:指明生成的文件的目录
- -classpath: 指明 class 文件所在的目录 (注意:此处的指定的目录为绝对路径,并且目录为 .class 所在目录的上一级目录)
以上亲测有效。
原来之前的错误是自己的书写问题。