Android Studio 配置 javah 生成 C/C++ 头文件,完成 JNI 调用

在自己阅读 <> 一书时,在其中章节有相关 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 工具栏,点击 +,具体配置信息如下图:
Android Studio 配置 javah 生成 C/C++ 头文件,完成 JNI 调用_第1张图片

其中具体配置内容分别如下:

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 
/* 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 

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 调用全部完成。

疑惑

按照 <> 一书在 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 )
    以上亲测有效。

原来之前的错误是自己的书写问题。

参考资料

Android 框架揭秘
Android JNI 環境建置

你可能感兴趣的:(JNI,C++)