一、Java调C
- 编写Native方法。
- 使用javah命令生成.h头文件。
- 复制.h头文件到CPP工程中。
- 复制jni_md.h和jni.h到CPP工程中。
- 实现.h头文件中生成的。
- 生成dll文件。
C的函数名称:Java_包名_方法名称。
1、java:Test
public class Test {
public static void main(String[] args) {
System.out.println(Test.class.getName());
}
public static native void getStringFormC();
}
2、javah:Test.h
命令:javah Test
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Test */
#ifndef _Included_Test
#define _Included_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Test
* Method: getStringFormC
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Test_getStringFormC
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
3、c实现:Test.cpp
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class Test */
#ifndef _Included_Test
#define _Included_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Test
* Method: getStringFormC
* Signature: ()V
*/
JNIEXPORT jstring JNICALL Java_Test_getStringFormC(JNIEnv* env, jclass) {
//将C的字符串转为JAVA的字符串
return env -> NewStringUTF("C string");
}
#ifdef __cplusplus
}
#endif
#endif
二、Android JNI
1、Android Studio创建C++项目
2、app/src/main/cpp/CMakeLists.txt:
# 版本号
cmake_minimum_required(VERSION 3.10.2)
# 项目名称,没有实际意义,可以选填
project("testc1")
# 添加写的cpp项目注意需要在追加在最后面,有先后顺序的
add_library(
# 参数1:依赖库的名称,仅此一个,在System.loadLibrary("testc1")使用
testc1
# 参数2:
SHARED
# 参数3:生成的或者自己写的cpp需要加在后面,不能插入到前两个参数中
native-lib.cpp)
find_library(
log-lib
log)
target_link_libraries(
testc1
)
关于CMake 使用:https://mp.weixin.qq.com/s/XFwHkg89qifyUqlSOSJUdw
3、app/src/main/cpp/native-lib.cpp
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_simple_testc1_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {
// 将C的字符串转为JAVA的字符串
char* hello = "Hello from C++666";
return env->NewStringUTF(hello);
}
4、app/src/main/java/com/simple/testc1/MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// Example of a call to a native method
binding.sampleText.text = stringFromJNI()
}
/**
* A native method that is implemented by the 'testc1' native library,
* which is packaged with this application.
*/
private external fun stringFromJNI(): String
companion object {
// Used to load the 'testc1' library on application startup.
init {
System.loadLibrary("testc1")
}
}
}
效果图:
三、JNIEnv调用分析
- 在C++中JNIEnv是一个结构体_JNIEnv
- 在C语言中JNIEnv是一个结构体指针JNINativeInterface*
struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;
#if defined(__cplusplus)
//在C++中JNIEnv是一个结构体_JNIEnv
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
//在C语言中JNIEnv是一个结构体指针JNINativeInterface*
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
env->NewStringUTF(hello):
C++:
struct _JNIEnv {
const struct JNINativeInterface* functions;
#if defined(__cplusplus)
jstring NewStringUTF(const char* bytes){
//参数this,表示上下文环境即:env
return functions->NewStringUTF(this, bytes);
}
#endif
调用functions的NewStringUTF,即调用C语言中的NINativeInterface*
C语言:
struct JNINativeInterface {
//参数1:JNIEnv指针,参数2:字符串
jstring (*NewStringUTF)(JNIEnv*, const char*);
}
关于JNIEnv详细解析参考: Android JNI 之 JNIEnv 解析
四、JNI引用变量
引用类型:局部引用和全局引用。
1、局部引用
- 访问一个很大的JAVA对象,使用完成之后进行复杂的耗时操作。
- 创建了大量的局部引用,占用的内存太多,而这些局部操作和后面的引用没有关联性。
通过DeleteLocalRef手动释放。
env -> DeleteLocalRef(obj)
2、全局引用
全局共享:创建,获得,释放。三步走,共享可以跨线程,在任意地方调用。
env -> DeleteGlobalRef(obj)
源码:
void DeleteGlobalRef(jobject gref) {
functions->DeleteGlobalRef(this,gref);
}
void DeleteLocalRef(jobject obj) {
functions->DeleteLocalRef(this, obj);
}
3、弱全局引用
- 节省内存,在内存不足的时候释放所引用的对象。
- 可以引用一个不常用的对象,如果内NULL,临时创建。
源码:
jweak NewWeakGlobalRef(jobject obj) {
return functions->NewWeakGlobalRef(this,obj);
}
void DeleteWeakGlobalRef(jweak ref) {
functions->DeleteWeakGlobalRef(this,ref);
}
五、JNI异常处理
- 保证java可以运行。
- 保证后面的C代码可以继续运行。
示例:
jthrowable exception = env->ExceptionOccurred();
if (exception != NULL) {
//可以让java代码继续运行,请空异常信息
env->ExceptionClear();
}
源码:
jthrowable ExceptionOccurred() {
return functions->ExceptionOccurred(this);
}
void ExceptionDescribe() {
functions->ExceptionDescribe(this);
}
void ExceptionClear() {
functions->ExceptionClear(this);
}