FFI相关
javascript
const core = require('cheese-js');
const ffi = core.ffi;lua
local core = require('cheese-lua')
local ffi = core.fficreateCDef() ✅
说明:
- 创建函数声明构建器,用于描述 native 方法签名。
链式方法:
add(name, returnType, ...args):添加一个 native 方法定义build():生成最终 specs
类型来源:
ffi.NativeTypes.INT()ffi.NativeTypes.POINTER()ffi.NativeTypes.VOID()ffi.NativeTypes.FLOAT()ffi.NativeTypes.DOUBLE()ffi.NativeTypes.CHAR()ffi.NativeTypes.SHORT()ffi.NativeTypes.LONG()ffi.NativeTypes.BOOLEAN()ffi.NativeTypes.BYTE()ffi.NativeTypes.STRING()
createStruct() ✅
说明:
- 创建结构体布局构建器。
链式方法:
add(name, fieldType, count?):添加字段,count可用于定长数组build():生成结构体布局
字段类型来源:
ffi.NativeStructs.FieldType.INT8ffi.NativeStructs.FieldType.INT32ffi.NativeStructs.FieldType.INT64ffi.NativeStructs.FieldType.FLOAT32ffi.NativeStructs.FieldType.FLOAT64ffi.NativeStructs.FieldType.POINTER
cString(str) ✅
参数:
- ⭐
string(str): 要转为 C 字符串的文本
返回值:
- 🟢 C 字符串对象,可通过
pointer()传入结构体指针字段 - 🔴 null
buffer(layout) ✅
参数:
- ⭐
layout: 由createStruct().build()生成的结构体布局
返回值:
- 🟢 Buffer 对象,支持链式读写字段
- 🔴 null
buffer.pointer() ✅
返回值:
- 🟢 当前缓冲区的 native 指针
buffer.setInt/getInt ✅
说明:
- 写入/读取
INT32字段。
buffer.setLong/getLong ✅
说明:
- 写入/读取
INT64字段。
buffer.setFloat/getFloat ✅
说明:
- 写入/读取
FLOAT32字段。
buffer.setDouble/getDouble ✅
说明:
- 写入/读取
FLOAT64字段。
buffer.setPointer/getPointer ✅
说明:
- 写入/读取指针字段(例如字符串指针、子结构体指针)。
buffer.setFixedCString/getFixedCString ✅
说明:
- 写入/读取定长
char[]字段。
createFromAssets(name, specs) ✅
参数:
- ⭐
string(name): assets 中动态库名,例如libmyapplication.so - ⭐
specs:createCDef().build()生成的签名定义
返回值:
- 🟢 Native API 调用对象
createFromLibraryName(name, specs) ✅
参数:
- ⭐
string(name): 系统库名,例如c - ⭐
specs:createCDef().build()生成的签名定义
返回值:
- 🟢 Native API 调用对象
createFromAbsolutePath(path, specs) ✅
参数:
- ⭐
string(path): 动态库绝对路径 - ⭐
specs:createCDef().build()生成的签名定义
返回值:
- 🟢 Native API 调用对象
api.callInt/callLong/callString/callBoolean/callByte/callShort/callFloat/callDouble/callPointer/callVoid ✅
参数:
- ⭐
string(methodName): 已定义的 native 函数名 ...args: 按签名传入参数
返回值:
- 根据调用方法返回对应类型,
callVoid无返回值
示例一:嵌套结构体调用
javascript
const core = require('cheese-js'); // 导入 cheese-js 核心模块
const { ffi } = core; // 从核心模块中解构出 ffi 对象
const cdef = ffi.createCDef() // 创建 C 函数签名构建器
.add("process_complex", ffi.NativeTypes.INT(), ffi.NativeTypes.POINTER()); // 声明 process_complex(ComplexUser*) -> int
const api = ffi.createFromAssets("libmyapplication.so", cdef.build()); // 从 assets 加载 libmyapplication.so 并创建调用实例
const childLayout = ffi.createStruct() // 创建子结构体布局构建器
.add("score", ffi.NativeStructs.FieldType.FLOAT64) // 添加 score 字段,类型为 double
.add("level", ffi.NativeStructs.FieldType.INT32) // 添加 level 字段,类型为 int32
.build(); // 生成子结构体布局
const userLayout = ffi.createStruct() // 创建主结构体布局构建器
.add("id", ffi.NativeStructs.FieldType.INT32) // 添加 id 字段,类型为 int32
.add("name", ffi.NativeStructs.FieldType.POINTER) // 添加 name 字段,类型为指针
.add("child", ffi.NativeStructs.FieldType.POINTER) // 添加 child 字段,类型为指针
.add("weight", ffi.NativeStructs.FieldType.FLOAT64) // 添加 weight 字段,类型为 double
.add("name_len", ffi.NativeStructs.FieldType.INT32) // 添加 name_len 字段,类型为 int32
.add("total", ffi.NativeStructs.FieldType.FLOAT64) // 添加 total 字段,类型为 double
.build(); // 生成主结构体布局
const child = ffi.buffer(childLayout) // 按子结构体布局创建内存缓冲区
.setDouble("score", 88.75) // 设置 score = 88.75
.setInt("level", 6); // 设置 level = 6
const name = ffi.cString("alice-复杂结构"); // 创建 C 字符串并获得其 native 内存对象
const user = ffi.buffer(userLayout) // 按主结构体布局创建内存缓冲区
.setInt("id", 101) // 设置 id = 101
.setPointer("name", name.pointer()) // 设置 name 指针指向 C 字符串
.setPointer("child", child.pointer()) // 设置 child 指针指向子结构体
.setDouble("weight", 12.5); // 设置 weight = 12.5
const result = api.callInt("process_complex", user.pointer()); // 传入主结构体指针并调用 native 方法
console.log("nested struct result:", result); // 输出函数返回值
console.log("nested struct name_len:", user.getInt("name_len")); // 输出 native 回填的 name_len
console.log("nested struct total:", user.getDouble("total")); // 输出 native 回填的 totallua
local core = require('cheese-lua') -- 导入 cheese-lua 核心模块
local ffi = core.ffi -- 从核心模块获取 ffi 对象
local cdef = ffi.createCDef() -- 创建 C 函数签名构建器
:add("process_complex", ffi.NativeTypes.INT(), ffi.NativeTypes.POINTER()) -- 声明 process_complex(ComplexUser*) -> int
local api = ffi.createFromAssets("libmyapplication.so", cdef:build()) -- 从 assets 加载 libmyapplication.so 并创建调用实例
local childLayout = ffi.createStruct() -- 创建子结构体布局构建器
:add("score", ffi.NativeStructs.FieldType.FLOAT64) -- 添加 score 字段,类型为 double
:add("level", ffi.NativeStructs.FieldType.INT32) -- 添加 level 字段,类型为 int32
:build() -- 生成子结构体布局
local userLayout = ffi.createStruct() -- 创建主结构体布局构建器
:add("id", ffi.NativeStructs.FieldType.INT32) -- 添加 id 字段,类型为 int32
:add("name", ffi.NativeStructs.FieldType.POINTER) -- 添加 name 字段,类型为指针
:add("child", ffi.NativeStructs.FieldType.POINTER) -- 添加 child 字段,类型为指针
:add("weight", ffi.NativeStructs.FieldType.FLOAT64) -- 添加 weight 字段,类型为 double
:add("name_len", ffi.NativeStructs.FieldType.INT32) -- 添加 name_len 字段,类型为 int32
:add("total", ffi.NativeStructs.FieldType.FLOAT64) -- 添加 total 字段,类型为 double
:build() -- 生成主结构体布局
local child = ffi.buffer(childLayout) -- 按子结构体布局创建内存缓冲区
:setDouble("score", 88.75) -- 设置 score = 88.75
:setInt("level", 6) -- 设置 level = 6
local name = ffi.cString("alice-复杂结构") -- 创建 C 字符串并获得其 native 内存对象
local user = ffi.buffer(userLayout) -- 按主结构体布局创建内存缓冲区
:setInt("id", 101) -- 设置 id = 101
:setPointer("name", name:pointer()) -- 设置 name 指针指向 C 字符串
:setPointer("child", child:pointer()) -- 设置 child 指针指向子结构体
:setDouble("weight", 12.5) -- 设置 weight = 12.5
local result = api:callInt("process_complex", user:pointer()) -- 传入主结构体指针并调用 native 方法
print("nested struct result:", result) -- 输出函数返回值
print("nested struct name_len:", user:getInt("name_len")) -- 输出 native 回填的 name_len
print("nested struct total:", user:getDouble("total")) -- 输出 native 回填的 total示例二:定长字符数组结构体
javascript
const core = require('cheese-js'); // 导入 cheese-js 核心模块
const { ffi } = core; // 从核心模块中解构出 ffi 对象
const cdef = ffi.createCDef() // 创建 C 函数签名构建器
.add("process_fixed_char", ffi.NativeTypes.INT(), ffi.NativeTypes.POINTER()); // 声明 process_fixed_char(FixedCharData*) -> int
const api = ffi.createFromAssets("libmyapplication.so", cdef.build()); // 从 assets 加载 libmyapplication.so 并创建调用实例
const layout = ffi.createStruct() // 创建结构体布局构建器
.add("tag", ffi.NativeStructs.FieldType.INT8, 16) // 添加 tag 字段,类型为长度16的 char 数组
.add("value", ffi.NativeStructs.FieldType.INT32) // 添加 value 字段,类型为 int32
.add("tag_len", ffi.NativeStructs.FieldType.INT32) // 添加 tag_len 字段,类型为 int32
.build(); // 生成结构体布局
const buffer = ffi.buffer(layout) // 按结构体布局创建内存缓冲区
.setFixedCString("tag", "char-array-demo") // 写入定长字符串 tag
.setInt("value", 20); // 设置 value = 20
const result = api.callInt("process_fixed_char", buffer.pointer()); // 传入结构体指针并调用 native 方法
console.log("fixed char struct result:", result); // 输出函数返回值
console.log("fixed char struct tag:", buffer.getFixedCString("tag")); // 输出结构体中的 tag
console.log("fixed char struct tag_len:", buffer.getInt("tag_len")); // 输出 native 回填的 tag_len
console.log("fixed char struct value:", buffer.getInt("value")); // 输出 native 更新后的 valuelua
local core = require('cheese-lua') -- 导入 cheese-lua 核心模块
local ffi = core.ffi -- 从核心模块获取 ffi 对象
local cdef = ffi.createCDef() -- 创建 C 函数签名构建器
:add("process_fixed_char", ffi.NativeTypes.INT(), ffi.NativeTypes.POINTER()) -- 声明 process_fixed_char(FixedCharData*) -> int
local api = ffi.createFromAssets("libmyapplication.so", cdef:build()) -- 从 assets 加载 libmyapplication.so 并创建调用实例
local layout = ffi.createStruct() -- 创建结构体布局构建器
:add("tag", ffi.NativeStructs.FieldType.INT8, 16) -- 添加 tag 字段,类型为长度16的 char 数组
:add("value", ffi.NativeStructs.FieldType.INT32) -- 添加 value 字段,类型为 int32
:add("tag_len", ffi.NativeStructs.FieldType.INT32) -- 添加 tag_len 字段,类型为 int32
:build() -- 生成结构体布局
local buffer = ffi.buffer(layout) -- 按结构体布局创建内存缓冲区
:setFixedCString("tag", "char-array-demo") -- 写入定长字符串 tag
:setInt("value", 20) -- 设置 value = 20
local result = api:callInt("process_fixed_char", buffer:pointer()) -- 传入结构体指针并调用 native 方法
print("fixed char struct result:", result) -- 输出函数返回值
print("fixed char struct tag:", buffer:getFixedCString("tag")) -- 输出结构体中的 tag
print("fixed char struct tag_len:", buffer:getInt("tag_len")) -- 输出 native 回填的 tag_len
print("fixed char struct value:", buffer:getInt("value")) -- 输出 native 更新后的 value示例三:调用系统库函数
javascript
const core = require('cheese-js'); // 导入 cheese-js 核心模块
const { ffi } = core; // 从核心模块中解构出 ffi 对象
importClass(java.lang.Integer); // 导入 Java Integer 类型用于参数封装
const specs = ffi.createCDef() // 创建 C 函数签名构建器
.add("getpid", ffi.NativeTypes.INT()) // 声明 getpid() -> int
.add("abs", ffi.NativeTypes.INT(), ffi.NativeTypes.INT()) // 声明 abs(int) -> int
.build(); // 生成最终签名定义
const inst = ffi.createFromLibraryName("c", specs); // 从系统 c 库创建调用实例
console.log("当前进程ID:", inst.callInt("getpid")); // 调用 getpid 并输出当前进程ID
console.log("abs(-123) =", inst.callInt("abs", Integer(-123))); // 调用 abs 并输出结果lua
local core = require('cheese-lua') -- 导入 cheese-lua 核心模块
local ffi = core.ffi -- 从核心模块获取 ffi 对象
local Integer = java.import('java.lang.Integer') -- 导入 Java Integer 类型用于参数封装
local specs = ffi.createCDef() -- 创建 C 函数签名构建器
:add("getpid", ffi.NativeTypes.INT()) -- 声明 getpid() -> int
:add("abs", ffi.NativeTypes.INT(), ffi.NativeTypes.INT()) -- 声明 abs(int) -> int
:build() -- 生成最终签名定义
local inst = ffi.createFromLibraryName("c", specs) -- 从系统 c 库创建调用实例
print("当前进程ID:", inst:callInt("getpid")) -- 调用 getpid 并输出当前进程ID
print("abs(-123) =", inst:callInt("abs", Integer(-123))) -- 调用 abs 并输出结果示例四:调用自定义的普通方法
javascript
const core = require('cheese-js'); // 导入 cheese-js 核心模块
const { ffi } = core; // 从核心模块中解构出 ffi 对象
importClass(java.lang.Byte)
const specs = ffi.createCDef() // 创建 C 函数签名构建器
.add("sum", ffi.NativeTypes.INT(), ffi.NativeTypes.BYTE(), ffi.NativeTypes.BYTE()) // 声明 sum(int8_t,int8_t) -> int
.add("getMessage", ffi.NativeTypes.STRING()) // 声明 getMessage() -> const char*
.add("getMessageLength", ffi.NativeTypes.INT(), ffi.NativeTypes.STRING()) // 声明 getMessageLength(const char*) -> int
.build(); // 生成最终签名定义
const api = ffi.createFromAssets("libmyapplication.so", specs); // 从 assets 加载 libmyapplication.so 并创建调用实例
const sumResult = api.callInt("sum", Byte(7), Byte(5)); // 调用 sum 计算 7 + 5
console.log("sum(7,5) =", sumResult); // 输出 sum 的结果
const msg = api.callString("getMessage"); // 调用 getMessage 获取字符串
console.log("getMessage() =", msg); // 输出返回的消息
const msgLen = api.callInt("getMessageLength", msg); // 把字符串传回 native 计算长度
console.log("getMessageLength(msg) =", msgLen); // 输出返回的长度lua
local core = require('cheese-lua') -- 导入 cheese-lua 核心模块
local ffi = core.ffi -- 从核心模块获取 ffi 对象
local Byte = java.import("java.lang.Byte")
local specs = ffi.createCDef() -- 创建 C 函数签名构建器
:add("sum", ffi.NativeTypes.INT(), ffi.NativeTypes.BYTE(), ffi.NativeTypes.BYTE()) -- 声明 sum(int8_t,int8_t) -> int
:add("getMessage", ffi.NativeTypes.STRING()) -- 声明 getMessage() -> const char*
:add("getMessageLength", ffi.NativeTypes.INT(), ffi.NativeTypes.STRING()) -- 声明 getMessageLength(const char*) -> int
:build() -- 生成最终签名定义
local api = ffi.createFromAssets("libmyapplication.so", specs) -- 从 assets 加载 libmyapplication.so 并创建调用实例
local sumResult = api:callInt("sum", Byte(7), Byte(5)) -- 调用 sum 计算 7 + 5
print("sum(7,5) =", sumResult) -- 输出 sum 的结果
local msg = api:callString("getMessage") -- 调用 getMessage 获取字符串
print("getMessage() =", msg) -- 输出返回的消息
local msgLen = api:callInt("getMessageLength", msg) -- 把字符串传回 native 计算长度
print("getMessageLength(msg) =", msgLen) -- 输出返回的长度对应C/C++实现示例
以下方法实现位于 libmyapplication 动态库中,对应上面的 ffi.createFromAssets("libmyapplication.so", ...) 调用。
cpp
#ifdef __cplusplus
extern "C" {
#endif
#include <jni.h>
#include <cstring>
#include <cstdint>
typedef struct ComplexChild {
jdouble score;
jint level;
} ComplexChild;
typedef struct ComplexUser {
jint id;
const char* name;
ComplexChild* child;
jdouble weight;
jint name_len;
jdouble total;
} ComplexUser;
JNIEXPORT jint JNICALL process_complex(ComplexUser* data) {
if (data == nullptr || data->name == nullptr || data->child == nullptr) {
return -1;
}
data->name_len = static_cast<jint>(std::strlen(data->name));
data->total = data->weight
+ data->child->score
+ static_cast<jdouble>(data->child->level)
+ static_cast<jdouble>(data->name_len)
+ static_cast<jdouble>(data->id);
return 0;
}
typedef struct FixedCharData {
char tag[16];
jint value;
jint tag_len;
} FixedCharData;
JNIEXPORT jint JNICALL process_fixed_char(FixedCharData* data) {
if (data == nullptr) {
return -1;
}
jint len = 0;
while (len < 16 && data->tag[len] != '\0') {
len++;
}
data->tag_len = len;
data->value = data->value + len;
return 0;
}
JNIEXPORT int JNICALL sum(int8_t a, int8_t b) {
return a + b;
}
JNIEXPORT const char* JNICALL getMessage() {
static const char* msg = "Hello from C++ JNA";
return msg;
}
JNIEXPORT jint JNICALL getMessageLength(const char* msg) {
if (msg == nullptr) {
return -1;
}
return static_cast<jint>(std::strlen(msg));
}
#ifdef __cplusplus
}
#endif