j4rs

j4rs

Rust与Java互操作的高效解决方案

j4rs是一个实现Rust和Java双向调用的开源库。它支持从Rust调用Java代码以及从Java调用Rust代码,提供异步支持、类型转换、数组和泛型处理等功能。j4rs无需复杂配置即可实现两种语言的互操作,适用于Linux、Windows和Android平台,大大简化了跨语言开发流程。

j4rsRustJava跨语言调用JVMGithub开源项目

j4rs

crates.io Maven Central Build

j4rs 代表 "Java for Rust",它允许从 Rust 轻松调用 Java 代码,反之亦然。

特性

用法

基础用法

use j4rs::{Instance, InvocationArg, Jvm, JvmBuilder}; // 创建 JVM let jvm = JvmBuilder::new().build()?; // 创建 java.lang.String 实例 let string_instance = jvm.create_instance( "java.lang.String", // 要创建实例的 Java 类 InvocationArg::empty(), // 用于构造函数调用的 `InvocationArg` 数组 - 本例中为空 )?; // 从调用和实例化返回的实例可以视为指向 Java 对象的指针。 // 它们可以用于进一步的 Java 调用。 // 例如,以下代码调用创建的 java.lang.String 实例的 `isEmpty` 方法 let boolean_instance = jvm.invoke( &string_instance, // 上面创建的 String 实例 "isEmpty", // 要调用的 String 实例的方法 InvocationArg::empty(), // 用于调用的 `InvocationArg` - 本例中为空 )?; // 如果我们需要将 `Instance` 转换为 Rust 值,应该调用 `to_rust` let rust_boolean: bool = jvm.to_rust(boolean_instance)?; println!("java.lang.String 实例的 isEmpty() 方法返回 {}", rust_boolean); // 上面会打印: // java.lang.String 实例的 isEmpty() 方法返回 true // 静态调用 let _static_invocation_result = jvm.invoke_static( "java.lang.System", // 要调用的 Java 类 "currentTimeMillis", // 要调用的 Java 类的静态方法 InvocationArg::empty(), // 用于调用的 `InvocationArg` - 本例中为空 )?; // 访问类的字段 let system_class = jvm.static_class("java.lang.System")?; let system_out_field = jvm.field(&system_class, "out"); // 使用字段获取枚举常量 let access_mode_enum = jvm.static_class("java.nio.file.AccessMode")?; let access_mode_write = jvm.field(&access_mode_enum, "WRITE")?; // 获取嵌套类(注意使用 `$` 而不是 `.`) let state = jvm.static_class("java.lang.Thread$State")?;

可以使用 java_listjava_map 函数创建 Java ListMapInstance

let rust_vec = vec!["arg1", "arg2", "arg3", "arg33"]; // 生成 Java List。Java List 实现是由 java.util.Arrays#asList 返回的实现 let java_list_instance = jvm.java_list( JavaClass::String, rust_vec)?; let rust_map = HashMap::from([ ("Potatoes", 3), ("Tomatoes", 33), ("Carrotoes", 333), ]); // 生成 java.util.HashMap。 let java_map_instance = jvm.java_map( JavaClass::String, JavaClass::Integer, rust_map)?;

从 Rust 向 Java 传递参数

j4rs 使用 InvocationArg 枚举将参数传递给 Java 世界。

用户可以利用现有的几种基本类型的 TryFrom 实现:

let i1 = InvocationArg::try_from("a str")?; // 创建 java.lang.String 类型的参数 let my_string = "a string".to_owned(); let i2 = InvocationArg::try_from(my_string)?; // 创建 java.lang.String 类型的参数 let i3 = InvocationArg::try_from(true)?; // 创建 java.lang.Boolean 类型的参数 let i4 = InvocationArg::try_from(1_i8)?; // 创建 java.lang.Byte 类型的参数 let i5 = InvocationArg::try_from('c')?; // 创建 java.lang.Character 类型的参数 let i6 = InvocationArg::try_from(1_i16)?; // 创建 java.lang.Short 类型的参数 let i7 = InvocationArg::try_from(1_i64)?; // 创建 java.lang.Long 类型的参数 let i8 = InvocationArg::try_from(0.1_f32)?; // 创建 java.lang.Float 类型的参数 let i9 = InvocationArg::try_from(0.1_f64)?; // 创建 java.lang.Double 类型的参数

以及 Vec 的实现:

let my_vec: Vec<String> = vec![ "abc".to_owned(), "def".to_owned(), "ghi".to_owned()]; let i10 = InvocationArg::try_from(my_vec.as_slice())?;

j4rs API 接受 InvocationArg 作为引用或值:

let inv_args = InvocationArg::try_from("arg from Rust")?; let _ = jvm.create_instance("java.lang.String", &[&inv_args])?; // 传递引用 let _ = jvm.create_instance("java.lang.String", &[inv_args])?; // 移动所有权

j4rs 返回的 Instance 可以转换为 InvocationArg,并进一步用于调用方法:

let one_more_string_instance = jvm.create_instance( "java.lang.String", // 要创建实例的 Java 类 InvocationArg::empty(), // 用于构造函数调用的 `InvocationArg` - 本例中为空 )?; let i11 = InvocationArg::try_from(one_more_string_instance)?;

要创建表示 Java null 值的 InvocationArg,请使用 Null 结构的 From 实现:

let null_string = InvocationArg::from(Null::String); // 一个 null String let null_integer = InvocationArg::from(Null::Integer); // 一个 null Integer let null_obj = InvocationArg::from(Null::Of("java.util.List")); // 任何其他类的 null 对象。例如 List

从 Rust 向 Java 传递自定义参数

对于没有 TryFrom 实现的自定义类型,也可以通过序列化支持。

要将自定义结构体 MyBean 作为 InvocationArg 使用,它需要是可序列化的:

#[derive(Serialize, Deserialize, Debug)] #[allow(non_snake_case)] struct MyBean { someString: String, someInteger: isize, }

然后,可以这样创建 InvocationArg

let my_bean = MyBean { someString: "My String In A Bean".to_string(), someInteger: 33, }; let ia = InvocationArg::new(&my_bean, "org.astonbitecode.j4rs.tests.MyBean");

它可以用作接受 org.astonbitecode.j4rs.tests.MyBean 实例的 Java 方法的参数。

当然,类路径中应该存在相应的 Java 类,以便反序列化工作并创建自定义 Java 对象:

package org.astonbitecode.j4rs.tests; public class MyBean { private String someString; private Integer someInteger; public MyBean() { } public String getSomeString() { return someString; } public void setSomeString(String someString) { this.someString = someString; } public Integer getSomeInteger() { return someInteger; } public void setSomeInteger(Integer someInteger) { this.someInteger = someInteger; } }

异步支持

(v0.16.0 及以后版本)

j4rs 通过 Jvm::invoke_async 函数支持 .async/.await。 该函数返回一个 Future,它通过 oneshot 通道Receiver 完成。 在Java端,可以被invoke_async调用的方法__必须__返回一个Java Future。 当Java Future完成时,j4rs的Java端会调用原生Rust代码来完成待处理的Rust Future,无论是成功还是失败,都会使用在调用invoke_async时创建的oneshot通道的Sender

例如,假设我们有一个返回Future的Java方法:

package org.astonbitecode.j4rs.tests; public class MyTest { private static ExecutorService executor = Executors.newSingleThreadExecutor(); // 只是在Future中返回传入的字符串 public Future<String> getStringWithFuture(String string) { CompletableFuture<String> completableFuture = new CompletableFuture<>(); executor.submit(() -> { completableFuture.complete(string); return null; }); return completableFuture; } }

我们可以像这样调用它:

let s_test = "j4rs_rust"; let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?; let instance = jvm.invoke_async(&my_test, "getStringWithFuture", &[InvocationArg::try_from(s_test)?]).await?; let string: String = jvm.to_rust(instance)?; assert_eq!(s_test, string);

请注意,对于被invoke_async函数调用的Java方法,最好返回一个CompletableFuture,因为这样可以提高性能。

对于不是CompletableFuture的简单Java Future,j4rs通过轮询来处理,使用内部的单线程ScheduledExecutorService

这显然会有性能问题。

invoke_asyncSend

InstanceSend的,可以安全地发送到其他线程。然而,由于Send近似invoke_async返回的Future不是Send的,即使它只包含一个Instance。这是因为Jvmasync调用捕获,而Jvm不是Send的。

为了获得一个__是__SendFuture<Instance>,可以使用Jvm::invoke_into_sendable_async。这个函数不需要Jvm作为参数;它在需要时内部创建一个,并应用一些作用域变通方法,以实现返回一个也是SendFuture<Instance>

讨论在此

类型转换

可以将Instance转换为其他类:

let instantiation_args = vec![InvocationArg::try_from("Hi")?]; let instance = jvm.create_instance("java.lang.String", instantiation_args.as_ref())?; jvm.cast(&instance, "java.lang.Object")?;

Java数组和可变参数

// 创建一个Java字符串数组 let s1 = InvocationArg::try_from("string1")?; let s2 = InvocationArg::try_from("string2")?; let s3 = InvocationArg::try_from("string3")?; let arr_instance = jvm.create_java_array("java.lang.String", &[s1, s2, s3])?; // 调用Arrays.asList(...)并获取一个java.util.List<String> let list_instance = jvm.invoke_static("java.util.Arrays", "asList", &[InvocationArg::from(arr_instance)])?;

Java泛型

// 假设map_instance是一个Map<String, Integer> // 我们可以调用它的put方法 jvm.invoke(&map_instance, "put", &[InvocationArg::try_from("one")?, InvocationArg::try_from(1)?])?;

Java基本类型

尽管存在自动装箱和拆箱,但j4rs无法使用_Integer_实例调用带有_基本_int参数的方法。

例如,以下代码无法工作:

let ia = InvocationArg::try_from(1_i32)?; jvm.create_instance("java.lang.Integer", &[ia])?;

它会抛出一个_InstantiationException_,因为Integer的构造函数接受一个基本类型int作为参数:

Exception in thread "main" org.astonbitecode.j4rs.errors.InstantiationException: Cannot create instance of java.lang.Integer at org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.instantiate(NativeInstantiationImpl.java:37) Caused by: java.lang.NoSuchMethodException: java.lang.Integer.<init>(java.lang.Integer) at java.base/java.lang.Class.getConstructor0(Class.java:3349) at java.base/java.lang.Class.getConstructor(Class.java:2151) at org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.createInstance(NativeInstantiationImpl.java:69) at org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.instantiate(NativeInstantiationImpl.java:34)

在这种情况下,应该先将java.lang.Integer实例转换为基本类型int

let ia = InvocationArg::try_from(1_i32)?.into_primitive()?; jvm.create_instance("java.lang.Integer", &[ia]);

Java实例链式调用

use j4rs::{Instance, InvocationArg, Jvm, JvmBuilder}; // 创建一个JVM let jvm = JvmBuilder::new().build()?; // 创建一个实例 let string_instance = jvm.create_instance( "java.lang.String", &[InvocationArg::try_from(" a string ")?], )?; // 对实例执行链式操作 let string_size: isize = jvm.chain(string_instance) .invoke("trim", InvocationArg::empty())? .invoke("length", InvocationArg::empty())? .to_rust()?; // 断言字符串已被修剪 assert!(string_size == 8);

回调支持

j4rs提供_Java到Rust回调_的支持。

这些回调通过Rust 通道进入Rust世界。

要初始化一个提供Java回调值的通道,应该调用Jvm::invoke_to_channel。它返回一个InstanceReceiver结构的结果,其中包含一个通道Receiver

// 调用Java实例的方法并在Rust通道中获取返回值。 // 创建一个支持原生回调的类的实例 // (该类只需要扩展 // `org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport`) let i = jvm.create_instance( "org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?; // 调用方法 let instance_receiver_res = jvm.invoke_to_channel( &i, // 异步调用的实例 "performCallback", // 异步调用的方法 InvocationArg::empty() // 用于调用的`InvocationArg`s - 本例中为空 ); // 等待响应 let instance_receiver = instance_receiver_res?; let _ = instance_receiver.rx().recv();

在Java世界中,可以进行__原生回调__的类必须扩展 org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport

例如,考虑以下Java类。

performCallback方法创建一个新线程并在该线程中调用doCallback方法。doCallback方法继承自NativeCallbackToRustChannelSupport类。

package org.astonbitecode.j4rs.tests; import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport; public class MyTest extends NativeCallbackToRustChannelSupport { public void performCallback() { new Thread(() -> { doCallback("THIS IS FROM CALLBACK!"); }).start(); } }

使用Maven制品

从0.6.0版本开始,可以从Maven仓库下载Java制品。 虽然可以定义更多仓库,但maven central是默认且始终可用的。

例如,以下是如何下载dropbox依赖并部署以供Rust代码使用:

let dbx_artifact = MavenArtifact::from("com.dropbox.core:dropbox-core-sdk:3.0.11"); jvm.deploy_artifact(dbx_artifact)?;

也可以使用其他制品库:

let jvm: Jvm = JvmBuilder::new() .with_maven_settings(MavenSettings::new(vec![ MavenArtifactRepo::from("myrepo1::https://my.repo.io/artifacts"), MavenArtifactRepo::from("myrepo2::https://my.other.repo.io/artifacts")]) ) .build() ?; jvm.deploy_artifact(&MavenArtifact::from("io.my:library:1.2.3"))?;

Maven制品会自动添加到类路径中,不需要显式添加。

一个好的做法是在crate编译期间通过构建脚本部署Maven制品。这确保了在实际执行Rust代码时类路径已正确填充。

注意:部署还不处理传递依赖。

将jar添加到类路径

如果我们需要使用 j4rs 访问一个 jar 文件,我们需要在创建 JVM 时将其添加到类路径中:

let entry = ClasspathEntry::new("/home/myuser/dev/myjar-1.0.0.jar"); let jvm: Jvm = JvmBuilder::new() .classpath_entry(entry) .build()?;

j4rs Java 库

j4rs 的 jar 文件可在 Maven Central 上获取。可以通过在 pom 中添加以下依赖项来使用它:

<dependency> <groupId>io.github.astonbitecode</groupId> <artifactId>j4rs</artifactId> <version>0.20.0</version> <scope>provided</scope> </dependency>

注意 scopeprovided。这是因为 j4rs Java 资源始终与 j4rs crate 一起提供。

这样使用可以避免可能出现的类加载错误。

在 Android 中使用 j4rs

Rust 端

  1. Cargo.toml 中将你的 crate 定义为 cdylib:
[lib] name = "myandroidapp" crate-type = ["cdylib"]
  1. 实现 jni_onload 函数并将提供的 JavaVM 应用于 j4rs:
const JNI_VERSION_1_6: jint = 0x00010006; #[allow(non_snake_case)] #[no_mangle] pub extern fn jni_onload(env: *mut JavaVM, _reserved: jobject) -> jint { j4rs::set_java_vm(env); jni_version_1_6 }

Java 端

创建一个 Activity 并按照此处所述正常定义你的本地方法。

注意: 如果在较旧的 Android 版本中使用 j4rs 时遇到任何问题,这可能是由 Java 8 兼容性问题引起的。 这就是为什么有一个 Java 7 版本的 j4rs:

<dependency> <groupId>io.github.astonbitecode</groupId> <artifactId>j4rs</artifactId> <version>0.13.1-java7</version> </dependency>

更新:Java 7 不再支持。j4rs 0.13.1 是最后一个版本。

JavaFX 支持

(v0.13.0 及以后版本)

构建 JavaFX UI 的步骤

1. 安装 Rust、cargo 和 JDK 11(或更高版本)

2. 获取 j4rs 的 JavaFX 依赖项:

最好在构建时进行这一操作,以便在实际的 Rust 应用程序启动和 JVM 初始化时依赖项可用。 可以通过在构建脚本中添加以下内容来实现:

use j4rs::JvmBuilder; use j4rs::jfx::JavaFxSupport; fn main() { let jvm = JvmBuilder::new().build().unwrap(); jvm.deploy_javafx_dependencies().unwrap(); }

3. 实现 UI:

这里有两种选择;要么使用 FXML 构建 UI,要么传统地使用 Java 代码构建。 在下面的代码片段中,你可以找到带有每行简短描述的注释。

3.a 使用 Java 调用 JavaFX API 实现 UI
// 创建支持 JavaFX 的 Jvm let jvm = JvmBuilder::new().with_javafx_support().build()?; // 启动 JavaFX 应用程序。 // 当 JavaFX 应用程序启动时,从 `start_javafx_app` 调用返回的 `InstanceReceiver` 通道 // 将接收一个 `javafx.stage.Stage` 的实例。 // 可以使用提供的 `Stage` 开始构建 UI。 let stage = jvm.start_javafx_app()?.rx().recv()?; // 创建 StackPane。Java 代码: StackPane root = new StackPane(); let root = jvm.create_instance("javafx.scene.layout.StackPane", InvocationArg::empty())?; // 创建按钮。Java 代码: Button btn = new Button(); let btn = jvm.create_instance("javafx.scene.control.Button", InvocationArg::empty())?; // 获取此按钮的动作通道 let btn_action_channel = jvm.get_javafx_event_receiver(&btn, FxEventType::ActionEvent_Action)?; // 设置按钮文本。Java 代码: btn.setText("Say Hello World to Rust"); jvm.invoke(&btn, "setText", &["A button that sends events to Rust".try_into()?])?; // 将按钮添加到 GUI。Java 代码: root.getChildren().add(btn); jvm.chain(&root)? .invoke("getChildren", InvocationArg::empty())? .invoke("add", &[btn.try_into()?])? .collect(); // 创建新场景。Java 代码: Scene scene = new Scene(root, 300, 250); let scene = jvm.create_instance("javafx.scene.Scene", &[ root.try_into()?, InvocationArg::try_from(300_f64)?.into_primitive()?, InvocationArg::try_from(250_f64)?.into_primitive()?])?; // 为场景设置标题。Java 代码: stage.setTitle("Hello Rust world!"); jvm.invoke(&stage, "setTitle", &["Hello Rust world!".try_into()?])?; // 将场景设置到舞台。Java 代码: stage.setScene(scene); jvm.invoke(&stage, "setScene", &[scene.try_into()?])?; // 显示舞台。Java 代码: stage.show(); jvm.invoke(&stage, "show", InvocationArg::empty())?;
3.b 使用 FXML 实现 UI

我个人更喜欢使用 FXML 构建 UI,例如使用 Scene Builder

需要记住的是,控制器类应该在根 FXML 元素中定义,并且应该是 fx:controller="org.astonbitecode.j4rs.api.jfx.controllers.FxController"

这里有一个 FXML 示例;它创建了一个带有标签和按钮的窗口:

<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.text.Font?> <VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="725.0" spacing="33.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.astonbitecode.j4rs.api.jfx.controllers.FxController"> <children> <Label text="JavaFX in Rust"> <font> <Font size="65.0" /> </font> </Label> <Label text="This UI is loaded with a FXML file" /> <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="10.0"> <children> <Button id="helloButton" mnemonicParsing="false" text="Say Hello" /> </children> </HBox> </children> </VBox>

元素的 id 可用于在 Rust 中检索相应的 Nodes 并对其进行操作(例如添加事件监听器、更改文本或效果等)。

// 创建支持 JavaFX 的 Jvm let jvm = JvmBuilder::new().with_javafx_support().build()?; // 启动 JavaFX 应用程序。 // 当 JavaFX 应用程序启动时,从 `start_javafx_app` 调用返回的 `InstanceReceiver` 通道 // 将接收一个 `javafx.stage.Stage` 的实例。 // 可以使用提供的 `Stage` 开始构建 UI。 let stage = jvm.start_javafx_app()?.rx().recv()?; // 为场景设置标题。Java 代码: stage.setTitle("Hello Rust world!"); jvm.invoke(&stage, "setTitle", &["Hello JavaFX from Rust!".try_into()?])?; // 显示舞台。Java 代码: stage.show(); jvm.invoke(&stage, "show", InvocationArg::empty())?; // 加载 fxml。这会返回一个 `FxController`,可用于通过 id 查找节点、 // 添加事件监听器等。 let controller = jvm.load_fxml(&PathBuf::from("./fxml/jfx_in_rust.fxml"), &stage)?; // 等待控制器初始化。这不是强制性的,它在这里只是为了展示这个功能的存在。 let _ = controller.on_initialized_callback(&jvm)?.rx().recv()?; println!("The controller is initialized!"); // 获取 InstanceReceiver 以接收 id 为 helloButton 的 JavaFX 按钮的回调 let hello_button_action_channel = controller.get_event_receiver_for_node("helloButton", FxEventType::ActionEvent_Action, &jvm)?;

完整示例请查看这里

Java 到 Rust 支持

(v0.12.0 及以后版本)

  • Cargo.toml 中添加两个必需的依赖项(j4rsj4rs_derive) 并将项目标记为 cdylib,以便输出共享库。 Java 代码将加载并使用此库来实现 JNI 调用。

  • 使用 call_from_java 属性注解从 Java 代码可访问的函数:

#[call_from_java("io.github.astonbitecode.j4rs.example.RustSimpleFunctionCall.fnnoargs")] fn my_function_with_no_args() { println!("来自Rust世界的问候!"); // 如果您需要在这里使用Jvm,需要附加线程 let jvm = Jvm::attach_thread().unwrap(); // 现在您可以像往常一样进一步调用Java类和方法! }

完整示例请参考这里

注意:后台使用了JNI,因此JNI的任何命名约定也适用于j4rs。 例如,下划线(_)应该转义为_1,用在call_from_java定义中。

Rust构建后的可移植性假设(发布j4rs应用程序)

在构建过程中,j4rs会创建一个jassets目录,其中包含crate工作所需的"Java世界"。 它总是会自动填充Java库,可以被视为一个默认的classpath容器,应该始终可用。

默认情况下,jassets与crate生成的产物位于同一目录下(在CARGO_TARGET_DIR下),因此在开发过程中不应该有任何问题。

但是实现完成后,如何发布应用程序呢?

可以在Jvm初始化期间为j4rs指定不同的base_path,如:

let jvm_res = j4rs::JvmBuilder::new() .with_base_path("/opt/myapp") .build();

base_path定义了j4rs工作所需的两个目录的位置; 即jassetsdeps

  1. jassets 包含j4rs jar和其他可能使用Maven部署的jar。
  2. deps 应包含j4rs动态库。这是实现Java到Rust的回调所必需的。 如果应用程序不执行Java->Rust回调,则不需要deps目录。

因此,可以将应用程序二进制文件放在例如/usr/bin下,而jassetsdeps目录放在/opt/myapp/$HOME/.myapp或其他任何地方。

目录树示例可能如下:

/ 
+ --- usr
|      + --- bin
|             + --- myapp
| 
+ --- opt
       + --- myapp 
              + --- jassets
              + --- deps

此外,还有一个实用函数可以自动将这两个目录复制到特定路径下。 Jvm::copy_j4rs_libs_under函数可以在正在发布的crate的构建脚本中调用:

Jvm::copy_j4rs_libs_under("/opt/myapp")?;

之后,/opt/myapp将包含j4rs工作所需的一切, 只要使用with_base_path方法创建Jvm:

let jvm_res = j4rs::JvmBuilder::new() .with_base_path("/opt/myapp") .build();

常见问题

我遇到java.lang.NoSuchMethodError: java.net.URLClassLoader.<init>(Ljava/lang/String;[Ljava/net/URL;Ljava/lang/ClassLoader;)V

j4rs使用自定义ClassLoader,需要至少Java 9版本。为了使用支持较旧Java版本的默认类加载器,在构建Jvm时调用JvmBuilder::with_default_classloader

如何启用调试日志?

j4rs使用log crate,因此可以根据所选实现相应地配置日志记录。

然而,它也支持控制台日志记录,可以通过设置环境变量J4RS_CONSOLE_LOG_LEVEL来配置。

接受的值有debuginfowarnerrordisabled

许可证

根据您的选择,可以采用:

编辑推荐精选

Trae

Trae

字节跳动发布的AI编程神器IDE

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
问小白

问小白

全能AI智能助手,随时解答生活与工作的多样问题

问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。

热门AI助手AI对话AI工具聊天机器人
Transly

Transly

实时语音翻译/同声传译工具

Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

AI办公办公工具AI工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图热门
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

热门AI开发模型训练AI工具讯飞星火大模型智能问答内容创作多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

咔片PPT

咔片PPT

AI助力,做PPT更简单!

咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

热门AI辅助写作AI工具讯飞绘文内容运营AI创作个性化文章多平台分发AI助手
材料星

材料星

专业的AI公文写作平台,公文写作神器

AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。

openai-agents-python

openai-agents-python

OpenAI Agents SDK,助力开发者便捷使用 OpenAI 相关功能。

openai-agents-python 是 OpenAI 推出的一款强大 Python SDK,它为开发者提供了与 OpenAI 模型交互的高效工具,支持工具调用、结果处理、追踪等功能,涵盖多种应用场景,如研究助手、财务研究等,能显著提升开发效率,让开发者更轻松地利用 OpenAI 的技术优势。

下拉加载更多