JNI在命令行的使用

开发环境为windows下wsl ubuntu22.04, java版本(原本打算用powershell纯windows环境弄,但不知道为什么-Djava.library.path一直指定不了报Linkerror,算了):

java -version
openjdk version "1.8.0_412"
OpenJDK Runtime Environment (build 1.8.0_412-8u412-ga-1~22.04.1-b08)
OpenJDK 64-Bit Server VM (build 25.412-b08, mixed mode)

首先建立一个教程项目做一个示例,用普通的文本编辑器就行(我是用vscode),不需要IDE高级功能:

一个简单的案例1

最后生成的项目目录应该如下:

 ll
\total 16
drwxrwxrwx 1 root root   512 Jul  9 10:13 ./
drwxrwxrwx 1 root root   512 Jul  9 09:01 ../
-rwxrwxrwx 1 root root   243 Jul  9 09:39 MyLog.c*
-rwxrwxrwx 1 root root   467 Jul  9 10:13 MyLog.class*
-rwxrwxrwx 1 root root   387 Jul  9 10:13 MyLog.h*
-rwxrwxrwx 1 root root   285 Jul  9 09:01 MyLog.java*
-rwxrwxrwx 1 root root 15584 Jul  9 10:12 libclog.so*

编写MyLog.java和MyLog.c:

MyLog.java

 
public class MyLog {
	//获取c代码的日志
	native void outputLog(String str);
     
    static { System.loadLibrary("clog");}
     
    public static void main(String[] args) {
         MyLog a = new MyLog();
         a.outputLog("I from Java!!!");
         
    }
}

一个指令生成MyLog.java的class并生成MyLog.h:

javac MyLog.java -h .

MyLog.h

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

#ifndef _Included_MyLog
#define _Included_MyLog
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     MyLog
 * Method:    outputLog
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_MyLog_outputLog
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

为MyLog.h编写MyLog.c:

MyLog.c

```c
#include "MyLog.h"
#include <stdio.h>
 
JNIEXPORT void JNICALL Java_MyLog_outputLog
  (JNIEnv *env, jobject obj,jstring str)
  {
    const char *cStr = (*env)->GetStringUTFChars(env, str, 0);
    printf("%s!\n", cStr);
    return;
  }
```


编译so库:

gcc -shared -o libclog.so MyLog.c -I/usr/lib/jvm/java-8-openjdk-amd64/include/ -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux/

终于可以运行java了,但并不能直接运行,因为java找不到so库的路径:

java -cp ./java HelloWorld
  Exception in thread "main" java.lang.UnsatisfiedLinkError: no hello in java.library.path:

可以用-Djava.library.path指定库路径:

java -Djava.library.path=. MyLog

如果成功应打印I from Java!!!!

一个简单的案例2

尝试将文件组织化,分c和java,再试一次:

root@WINDOWS-1FNAO0N:/mnt/d/CodeDraft/android_ndk_test/case1_hello# tree
.
├── c
│   ├── HelloWorld.c
│   ├── HelloWorld.h
│   ├── android.mk
│   └── application.mk
└── java
    ├── HelloWorld.class
    └── HelloWorld.java

  • java目录建立HelloWorld.java如下:
class HelloWorld
{
	public static native void displayHelloWorld();
	{
		System.loadLibrary("hello");
	}
	
	public static void main(String[] args)
	{
		new HelloWorld().displayHelloWorld();
	}
}

  • 用javac生成HelloWorld.classHelloWorld.h
javac java/HelloWorld.java -h ./c 

note: 也可以用javah生成HelloWorld.java

javah HelloWorld

但是该工具在jdk10时被弃用,原因是javac能够达到同样的功能,因此这里兼容起见用javac

  • 编译HelloWorld.c的生成动态链接库,上个参考例子是用gcc的,这次用安卓ndk使用的mk文件,类似于makefile,ndk本质是对clang的封装。

    记得include jni.h 和 jni_md.h的路径,android是用LOCAL_C_INCLUDES这个宏,而gcc则用-I指定。

    我这里jni.h和jni_md.h的路径如下,读者可根据自己jdk的安装路径自行修改:

    /usr/lib/jvm/java-8-openjdk-amd64/include/jni.h
    /usr/lib/jvm/java-8-openjdk-amd64/include/linux/jni_md.h
    

    编写android.mk如下:

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := libhello
    LOCAL_SRC_FILES := HelloWorld.c 
    LOCAL_C_FLAGS := -fPIC -g -Wall 
    
    include $(BUILD_SHARED_LIBRARY)
    
    

    各种参数可参考:ndk-build官方使用说明

    在application.mk中可指定安卓平台api版本,abi架构,android.mk位置:

    application.mk

    APP_PLATFORM=android-33
    APP_ABI=armeabi-v7a x86
    APP_BUILD_SCRIPT=./c/android.mk
    

编译:

root@WINDOWS-1FNAO0N:/mnt/d/CodeDraft/android_ndk_test/case1_hello# 
ndk-build NDK_PROJECT_PATH=./ NDK_APPLICATION_MK=./c/application.mk      

附带一个gcc示例:

 gcc -shared ./c/HelloWorld.c -o libhello.so -I/usr/lib/jvm/java-8-openjdk-amd64/include/ -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux/

android.mk生成的libhello.so库可在c/libs看到

tree
.
├── c
│   ├── HelloWorld.c
│   ├── HelloWorld.h
│   ├── android.mk
│   ├── application.mk
│   ├── libs
│   │   ├── armeabi-v7a
│   │   │   └── libhello.so
│   │   ├── x86
│   │   │   └── libhello.so

在命令行用-D参数指定

root@WINDOWS-1FNAO0N:/mnt/d/CodeDraft/android_ndk_test/case1_hello# 
java -Djava.library.path=./c/libs/armeabi-v7a -cp ./java HelloWorld

如果正常输出,结果应该如下:

Hello world!

参考:

NDK相关:

csdn:使用NDK编译C/C++文件生成在安卓中的可执行文件

腾讯云:(NDK编译)详解使用Android.mk编译的C/C++程序过程

https://developer.android.com/ndk/guides/android_mk?hl=zh-cn

ndk-build官方使用说明

JNI相关:

JNI编写详细步骤实践,javac编译,gcc编译

编译JNI的动态库, 遇到的坑

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中使用JNI(Java Native Interface)调用Qt方法,您需要进行以下步骤: 1. 编写Qt代码:首先,您需要编写您想要在Qt中调用的功能代码。确保将该功能封装在一个类中,并在该类的头文件中声明该函数。 2. 生成动态链接库:使用Qt提供的工具(例如qmake或CMake),将您的Qt代码编译为动态链接库(DLL或SO文件)。确保导出您想要从Java中调用的函数。 3. 生成JNI头文件:使用Java提供的工具(例如javah),生成JNI头文件。该头文件将包含用于在Java中调用C/C++代码的函数声明。 4. 实现JNI方法:在您的C/C++代码中,实现JNI方法,以便在Java中调用Qt方法。这些方法将是JNI头文件中声明的函数。 5. 将动态链接库与Java绑定:在Java代码中,使用System.loadLibrary()方法加载生成的动态链接库。然后,使用JNI提供的功能(例如JNIEnv和jobject)在Java中调用C/C++方法。 以下是一个简单的示例: Qt代码(myclass.h): ```cpp #ifndef MYCLASS_H #define MYCLASS_H #include <QObject> class MyClass : public QObject { Q_OBJECT public: explicit MyClass(QObject *parent = nullptr); public slots: void myFunction(); }; #endif // MYCLASS_H ``` Qt代码(myclass.cpp): ```cpp #include "myclass.h" #include <QDebug> MyClass::MyClass(QObject *parent) : QObject(parent) { } void MyClass::myFunction() { qDebug() << "Qt function called from JNI"; } ``` 生成动态链接库:使用Qt提供的工具(例如qmake或CMake)将Qt代码编译为动态链接库。 生成JNI头文件:在命令行中,导航到包含Java类文件的目录,并运行以下命令: ``` javah -jni com.example.MyClass ``` 这将在该目录中生成一个名为"com_example_MyClass.h"的JNI头文件。 实现JNI方法:在C/C++代码中,实现JNI方法。例如,您可以创建一个名为"com_example_MyClass.cpp"的文件,并编写以下内容: ```cpp #include "com_example_MyClass.h" #include "myclass.h" JNIEXPORT void JNICALL Java_com_example_MyClass_myFunction(JNIEnv *, jobject) { MyClass myClass; myClass.myFunction(); } ``` 将动态链接库与Java绑定:在Java代码中,使用System.loadLibrary()方法加载生成的动态链接库,并调用JNI方法。例如: ```java package com.example; public class MyClass { static { System.loadLibrary("mylibrary"); } private native void myFunction(); public static void main(String[] args) { MyClass myClass = new MyClass(); myClass.myFunction(); } } ``` 请注意,这只是一个简单的示例,以帮助您入门。实际上,JNI使用可能会更加复杂,并涉及到处理数据类型转换、异常处理等方面的内容。确保阅读JNI文档以获取更多详细信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值