在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何用Rust通過JNI和Java進(jìn)行交互

jf_wN0SrCdH ? 來源:Rust語言中文社區(qū) ? 2023-10-17 11:41 ? 次閱讀

近期工作中有Rust和Java互相調(diào)用需求,這篇文章主要介紹如何用Rust通過JNI和Java進(jìn)行交互,還有記錄一下開發(fā)過程中遇到的一些坑。


JNI簡單來說是一套Java與其他語言互相調(diào)用的標(biāo)準(zhǔn),主要是C語言,官方也提供了基于C的C++接口。 既然是C語言接口,那么理論上支持C ABI的語言都可以和Java語言互相調(diào)用,Rust就是其中之一。


關(guān)于JNI的歷史背景以及更詳細(xì)的介紹可以參考官方文檔


在Rust中和Java互相調(diào)用,可以使用原始的JNI接口,也就是自己聲明JNI的C函數(shù)原型,在Rust里按照C的方式去調(diào)用,但這樣寫起來會很繁瑣,而且都是unsafe的操作; 不過Rust社區(qū)里已經(jīng)有人基于原始的JNI接口,封裝好了一套safe的接口,crate的名字就叫jni,用這個庫來開發(fā)就方便多了


文中涉及的代碼放在了這個github倉庫https://github.com/metaworm/rust-java-demo


Rust JNI 工程配置


如果你熟悉Cargo和Maven,可以跳過這一節(jié),直接看我提供的github源碼即可

Rust工程配置


首先,通過cargo new java-rust-demo創(chuàng)建一個rust工程

然后切換到工程目錄cd java-rust-demo,并編輯Cargo.toml:修改類型為動態(tài)庫、加上對 jni crate 的依賴


	

[package] name = "rust-java-demo" version = "0.1.0" edition = "2021" [lib] crate-type = ['cdylib'] [dependencies] jni = {version = '0.19'}

重命名src目錄下的main.rslib.rs,Rust庫類型的工程編譯入口為 lib.rs,然后添加以下代碼


	

use jni::*; use jni::JNIEnv; #[no_mangle] pub unsafe extern "C" fn Java_pers_metaworm_RustJNI_init(env: JNIEnv, _class: JClass) { println!("rust-java-demo inited"); }



然后執(zhí)行cargo build構(gòu)建,生成的動態(tài)庫默認(rèn)會位于target/debug目錄下,我這里用的linux系統(tǒng),動態(tài)庫文件名為librust_java_demo.so,如果是Windows系統(tǒng),文件名為rust_java_demo.dll


這樣,我們第一個JNI函數(shù)就創(chuàng)建成功了! 通過Java_pers_metaworm_RustJNI_init這個導(dǎo)出函數(shù),給了Java的pers.metaworm.RustJNI這個類提供了一個native的靜態(tài)方法init; 這里只是簡單地打印了一句話,后面會通過這個初始化函數(shù)添加更多的功能


Java工程配置


還是在這個工程目錄里,把Java部分的代碼放在java這個目錄下,在其中創(chuàng)建pers/metaworm/RustJNI.java文件


	

package pers.metaworm; public class RustJNI { static { System.loadLibrary("rust_java_demo"); } public static void main(String[] args) { init(); } static native void init(); }


我們使用流行的 maven 工具來構(gòu)建Java工程,在項目根目錄下創(chuàng)建 maven 的工程文件pom.xml


	

xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0modelVersion> <groupId>pers.metawormgroupId> <artifactId>RustJNIartifactId> <version>1.0-SNAPSHOTversion> <properties> <exec.mainClass>pers.metaworm.RustJNIexec.mainClass> <maven.compiler.source>1.8maven.compiler.source> <maven.compiler.target>1.8maven.compiler.target> <maven.compiler.encoding>UTF-8maven.compiler.encoding> properties> <dependencies> dependencies> <build> <sourceDirectory>javasourceDirectory> <plugins> <plugin> <groupId>org.apache.maven.pluginsgroupId> <artifactId>maven-compiler-pluginartifactId> <version>2.4version> <configuration> <encoding>UTF-8encoding> configuration> plugin> plugins> build> project>


運行 DMEO 工程


上面的工程配置弄好之后,就可以使用cargo build命令構(gòu)建Rust提供的JNI動態(tài)庫,mvn compile命令來編譯Java代碼


Rust和Java代碼都編譯好之后,執(zhí)行java -Djava.library.path=target/debug -classpath target/classes pers.metaworm.RustJNI來運行


其中-Djava.library.path=target/debug指定了我們JNI動態(tài)庫所在的路徑,-classpath target/classes指定了Java代碼的編譯輸出的類路徑,pers.metaworm.RustJNI是Java main方法所在的類


不出意外的話,運行之后會在控制臺輸出init函數(shù)里打印的"rust-java-demo inited"


Java調(diào)用Rust


接口聲明


前面的Java_pers_metaworm_RustJNI_init函數(shù)已經(jīng)展示了如何給Java暴露一個native方法,即導(dǎo)出名稱為Java_<類完整路徑>_<方法名>的函數(shù),然后在Java對應(yīng)的類里聲明對應(yīng)的native方法


拓展:除了通過導(dǎo)出函數(shù)給Java提供native方法,還可以通過 RegisterNatives 函數(shù)動態(tài)注冊native方法,對應(yīng)的jni封裝的函數(shù)為JNIEnv::register_native_methods,一般動態(tài)注冊會在JNI_Onload這個導(dǎo)出函數(shù)里執(zhí)行,jvm加載jni動態(tài)庫時會執(zhí)行這個函數(shù)(如果有的話)


當(dāng)在Java里首次調(diào)用native方法時,JVM就會尋找對應(yīng)名稱的導(dǎo)出的或者動態(tài)注冊的native函數(shù),并將Java的native方法和Rust的函數(shù)關(guān)聯(lián)起來;如果JVM沒找到對應(yīng)的native函數(shù),則會報java.lang.UnsatisfiedLinkError異常


為了演示,我們再添加一些代碼來覆蓋更多的交互場景


lib.rs


	

use jni::*; use jni::{jint, jobject, jstring}; use jni::JNIEnv; #[no_mangle] pub unsafe extern "C" fn Java_pers_metaworm_RustJNI_addInt( env: JNIEnv, _class: JClass, a: jint, b: jint, ) -> jint { a + b } #[no_mangle] pub unsafe extern "C" fn Java_pers_metaworm_RustJNI_getThisField( env: JNIEnv, this: JObject, name: JString, sig: JString, ) -> jobject { let result = env .get_field( this, &env.get_string(name).unwrap().to_string_lossy(), &env.get_string(sig).unwrap().to_string_lossy(), ) .unwrap(); result.l().unwrap().into_inner() }


RustJNI.java


	

package pers.metaworm; public class RustJNI { static { System.loadLibrary("rust_java_demo"); } public static void main(String[] args) { init(); System.out.println("test addInt: " + (addInt(1, 2) == 3)); RustJNI jni = new RustJNI(); System.out.println("test getThisField: " + (jni.getThisField("stringField", "Ljava/lang/String;") == jni.stringField)); System.out.println("test success"); } String stringField = "abc"; static native void init(); static native int addInt(int a, int b); native Object getThisField(String name, String sig); }


其中,addInt方法接收兩個int參數(shù),并返回相加的結(jié)果;getThisField是一個實例native方法,它獲取this對象指定的字段并返回


參數(shù)傳遞


從上一節(jié)的例子里可以看到,jni函數(shù)的第一個參數(shù)總是JNIEnv,很多交互操作都需要通過這個對象來進(jìn)行; 第二個參數(shù)是類對象(靜態(tài)native方法)或this對象(實例native方法); 從第三個參數(shù)開始,每一個參數(shù)對應(yīng)Java的native方法所聲明的參數(shù)


對于基礎(chǔ)的參數(shù)類型,可以直接用use jni::*提供的j開頭的系列類型來聲明,類型對照表:


Java 類型 Native 類型 類型描述
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned 16 bits
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
void void not applicable



對于引用類型(復(fù)合類型/對象類型),可以統(tǒng)一用jni::JObject聲明;JObject是對jobject的rust封裝,帶有生命周期參數(shù);對于String類型,也可以用 JString 來聲明,JString是對JObject的一層簡單封裝


拋異常


前面的Java_pers_metaworm_RustJNI_getThisField函數(shù)里,用了很多unwrap,這在生產(chǎn)環(huán)境中是非常危險的,萬一傳了一個不存在的字段名,就直接crash了;所以我們改進(jìn)一下這個函數(shù),讓他支持拋異常,出錯的時候能讓Java捕獲到


	

#[no_mangle] pub unsafe extern "C" fn Java_pers_metaworm_RustJNI_getThisFieldSafely( env: JNIEnv, this: JObject, name: JString, sig: JString, ) -> jobject { let result = (|| { env.get_field( this, &env.get_string(name)?.to_string_lossy(), &env.get_string(sig)?.to_string_lossy(), )? .l() })(); match result { Ok(res) => res.into_inner(), Err(err) => { env.exception_clear().expect("clear"); env.throw_new("Ljava/lang/Exception;", format!("{err:?}")) .expect("throw"); std::null_mut() } } }


Java層的測試代碼為

        

try { System.out.println("test getThisFieldSafely: " + (jni.getThisFieldSafely("stringField", "Ljava/lang/String;") == jni.stringField)); jni.getThisFieldSafely("fieldNotExists", "Ljava/lang/String;"); } catch (Exception e) { System.out.println("test getThisFieldSafely: catched exception: " + e.toString()); }


通過env.throw_new("Ljava/lang/Exception;", format!("{err:?}"))拋出了一個異常,從JNI函數(shù)返回后,Java就會捕獲到這個異常; 代碼里可以看到在拋異常之前,調(diào)用了env.exception_clear()來清除異常,這是因為前面的get_field已經(jīng)拋出一個異常了,當(dāng)env里已經(jīng)有一個異常的時候,后續(xù)再調(diào)用env的函數(shù)都會失敗,這個異常也會繼續(xù)傳遞到上層的Java調(diào)用者,所以其實這里沒有這兩句,直接返回null的話,Java也可以捕獲到異常;但我們通過throw_new可以自定義異常類型及異常消息

這其實不是一個典型的場景,典型的場景應(yīng)該是Rust里的某個調(diào)用返回了Error,然后通過拋異常的形式傳遞到Java層,比如除0錯誤


	

#[no_mangle] pub unsafe extern "C" fn Java_pers_metaworm_RustJNI_divInt( env: JNIEnv, _class: JClass, a: jint, b: jint, ) -> jint { if b == 0 { env.throw_new("Ljava/lang/Exception;", "divide zero") .expect("throw"); 0 } else { a / b } }

Rust調(diào)用Java


創(chuàng)建對象、調(diào)用方法、訪問字段...


下面用一段代碼展示如何在Rust中創(chuàng)建Java對象、調(diào)用方法、獲取字段、處理異常等常見用法


	

#[allow(non_snake_case)] fn call_java(env: &JNIEnv) { match (|| { let File = env.find_class("java/io/File")?; // 獲取靜態(tài)字段 let separator = env.get_static_field(File, "separator", "Ljava/lang/String;")?; let separator = env .get_string(separator.l()?.into())? .to_string_lossy() .to_string(); println!("File.separator: {}", separator); assert_eq!(separator, format!("{}", std::MAIN_SEPARATOR)); // env.get_static_field_unchecked(class, field, ty) // 創(chuàng)建實例對象 let file = env.new_object( "java/io/File", "(Ljava/lang/String;)V", &[JValue::Object(env.new_string("")?.into())], )?; // 調(diào)用實例方法 let abs = env.call_method(file, "getAbsolutePath", "()Ljava/lang/String;", &[])?; let abs_path = env .get_string(abs.l()?.into())? .to_string_lossy() .to_string(); println!("abs_path: {}", abs_path); jni::Result::Ok(()) })() { Ok(_) => {} // 捕獲異常 Err(jni::JavaException) => { let except = env.exception_occurred().expect("exception_occurred"); let err = env .call_method(except, "toString", "()Ljava/lang/String;", &[]) .and_then(|e| Ok(env.get_string(e.l()?.into())?.to_string_lossy().to_string())) .unwrap_or_default(); env.exception_clear().expect("clear exception"); println!("call java exception occurred: {err}"); } Err(err) => { println!("call java error: {err:?}"); } } } #[no_mangle] pub unsafe extern "C" fn Java_pers_metaworm_RustJNI_callJava(env: JNIEnv) { println!("call java"); call_java(&env) }


總結(jié)一下常用的函數(shù),具體用法可以參考JNIEnv的文檔

  • 創(chuàng)建對象new_object

  • 創(chuàng)建字符串對象new_string

  • 調(diào)用方法call_methodcall_static_method

  • 獲取字段get_fieldget_static_field

  • 修改字段set_fieldset_static_field


要注意的是調(diào)用方法、創(chuàng)建對象等需要傳一個方法類型簽名,這是因為Java支持方法重載,同一個類里一個名稱的函數(shù)可能有多個,所以需要通過類型簽名來區(qū)分,類型簽名的規(guī)則可以參考官方文檔


異常處理


call_java函數(shù)展示了如何在Rust中處理Java的異常: 通過JNIEnv對象動態(tài)獲取字段或者調(diào)用方法,都會返回一個jni::Result類型,對應(yīng)的Error類型為jni::Error;如果Error是jni::JavaException則表明在JVM執(zhí)行過程中,某個地方拋出了異常,這種情況下就可以用exception_occurred函數(shù)來獲取異常對象進(jìn)行處理,然后調(diào)用exception_clear來清除異常,如果再返回到Java便可以繼續(xù)執(zhí)行


在非Java線程中調(diào)用Java


從Java中調(diào)用的Rust代碼,本身就處于一個Java線程中,第一個參數(shù)為JNIEnv對象,Rust代碼用這個對象和Java進(jìn)行交互; 實際應(yīng)用場景中,可能需要從一個非Java線程或者說我們自己的線程中去調(diào)用Java的方法,但我們的線程沒有JNIEnv對象,這時就需要調(diào)用JavaVM::attach_current_thread函數(shù)將當(dāng)前線程附加到JVM上,來獲得一個JNIEnv


	

#[no_mangle] pub unsafe extern "C" fn Java_pers_metaworm_RustJNI_callJavaThread(env: JNIEnv) { let vm = env.get_java_vm().expect("get jvm"); std::spawn(move || { println!("call java in another thread"); let env = vm.attach_current_thread().expect("attach"); call_java(&env); }); }


attach_current_thread函數(shù)返回一個AttachGuard對象,可以解引用為JNIEnv,并且在作用域結(jié)束drop的時候自動調(diào)用detach_current_thread函數(shù);原始的AttachCurrentThreadJNI函數(shù),如果當(dāng)前線程已經(jīng)attach了,則會拋異常,jni crate里的JavaVM::attach_current_thread做了一層封裝,如果當(dāng)前已經(jīng)attach了,則會返回之前attach的對象,保證不會重復(fù)attach


JavaVM對象通過JNIEnv::get_java_vm函數(shù)獲取,可以在初始化的時候?qū)⑦@個變量存起來,給后續(xù)的其他線程使用


局部引用、全局引用與對象緩存


關(guān)于局部引用與全局引用的官方文檔


Rust提供的native函數(shù),傳過來的對象引用都是局部引用,局部引用只在本次調(diào)用JNI調(diào)用范圍內(nèi)有效,而且不能跨線程使用;如果跨線程,必須使用全局引用


可以通過JNIEnv::new_global_ref來獲取JClass、JObject的全局引用,這個函數(shù)返回一個GlobalRef對象,可以通過GlobalRef::as_object轉(zhuǎn)成JObject或者JClass等對象;GlobalRef對象drop的時候,會調(diào)用DeleteGlobalRef將JVM內(nèi)部的引用刪除


前面的代碼,從Rust調(diào)用Java方法都是通過名稱加方法簽名調(diào)用的,這種方式,寫起來很舒服,但運行效率肯定是非常低的,因為每次都要通過名稱去查找對應(yīng)的方法


其實JNI原始的C接口,是通過jobjectID、jclassID、jmethodID、jfieldID來和Java交互的,只不過是jni crate給封裝了一層比較友好的接口


如果我們對性能要求比較高,則可以在初始化的時候獲取一些JClass、JObject的全局引用,緩存起來,后面再轉(zhuǎn)成JClass、JObject來使用,千萬不要對jmethodID、jfieldID獲取全局引用,因為這倆都是通過jclassID生成的,其聲明周期和jclassID對應(yīng)的對象相同,不是需要GC的對象,如果對jmethodID獲取全局引用然后調(diào)用,會導(dǎo)致某些JVM Crash;對于jmethodID、jfieldID,則可以基于JClass、JObject的全局引用獲取,后面直接使用即可


獲取到這些全局的ID之后,就可以通過JNIEnv::call_method_unchecked系列函數(shù),來更高效地調(diào)用Java


我用Rust強(qiáng)大的宏,實現(xiàn)了這個過程,可以讓我們直接在Rust中以聲明的方式緩存的所需類及其方法ID


	

#[allow(non_snake_case)] pub mod cache { use anyhow::Context; use jni::Result as JniResult; use jni::*; use jni::JNIEnv; pub fn method_global_ref<'a>( env: JNIEnv<'a>, class: JClass, name: &str, sig: &str, ) -> JniResult'a>> { let method = env.get_method_id(class, name, sig)?.into_inner(); Ok(JMethodID::from(method.cast())) } pub fn static_method_global_ref<'a>( env: JNIEnv<'a>, class: JClass, name: &str, sig: &str, ) -> ::Result'a>> { let method = env.get_static_method_id(class, name, sig)?.into_inner(); Ok(JStaticMethodID::from(method.cast())) } macro_rules! gen_global_ref { (@method_type) => { JMethodID<'static> }; (@method_type static) => { JStaticMethodID<'static> }; (@method_ref) => { method_global_ref }; (@method_ref static) => { static_method_global_ref }; ( $( #[name = $classname:literal] class $name:ident { $($method:ident : $($modify:ident)* $sig:literal,)* } )* ) => { $( #[allow(non_snake_case)] pub struct $name { pub class: JClass<'static>, $(pub $method: gen_global_ref!(@method_type $($modify)*),)* } impl $name { pub fn from_env(env: JNIEnv<'static>) -> anyhow::Result<Self> { Self::from_class(env, env.find_class($classname)?) } pub fn from_class(env: JNIEnv<'static>, class: JClass) -> anyhow::Result<Self> { let cls = env.new_global_ref(class)?; let class = JClass::from(*cls.as_obj()); core::forget(cls); Ok(Self { class, $( $method: gen_global_ref!(@method_ref $($modify)*)( env, class, stringify!($method), $sig).context(stringify!($method) )?, )* }) } } // TODO: impl Drop )* pub struct CachedClasses { $(pub $name: $name,)* } impl CachedClasses { pub fn from_env(env: JNIEnv<'static>) -> anyhow::Result<Self> { Ok(Self { $($name: $name::from_env(env).context(stringify!($name))?,)* }) } } unsafe impl Sync for CachedClasses {} unsafe impl Send for CachedClasses {} } } gen_global_ref! { #[name = "java/lang/Thread"] class Thread { currentThread: static "()Ljava/lang/Thread;", getStackTrace: "()[Ljava/lang/StackTraceElement;", } #[name = "java/lang/StackTraceElement"] class StackTraceElement { getLineNumber: "()I", toString: "()Ljava/lang/String;", } #[name = "java/io/File"] class File { getAbsolutePath: "()Ljava/lang/String;", } } static mut CLASSES: Option<Box> = None; pub unsafe fn init(env: JNIEnv<'static>) -> anyhow::Result<Option<Box>> { Ok(CLASSES.replace(CachedClasses::from_env(env)?.into())) } pub fn get() -> &'static CachedClasses { unsafe { CLASSES.as_ref().expect("Cached Java Classed not inited") } } }



審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8968

    瀏覽量

    153351
  • JAVA
    +關(guān)注

    關(guān)注

    20

    文章

    2987

    瀏覽量

    107266
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7630

    瀏覽量

    140726
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2117

    瀏覽量

    74866
  • Rust
    +關(guān)注

    關(guān)注

    1

    文章

    233

    瀏覽量

    6988

原文標(biāo)題:【Rust筆記】Rust與Java交互-JNI模塊編寫-實踐總結(jié)

文章出處:【微信號:Rust語言中文社區(qū),微信公眾號:Rust語言中文社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦
    熱點推薦

    聊聊Rust與C語言交互的具體步驟

    rust FFI 是rust與其他語言互調(diào)的橋梁,通過FFI rust 可以有效繼承 C 語言的歷史資產(chǎn)。本期通過幾個例子來聊聊
    發(fā)表于 07-06 11:15 ?2049次閱讀

    【NanoPi Duo開發(fā)板試用申請】智能家居,Java高級語言控制

    服務(wù)器進(jìn)行交互,并通過Android端進(jìn)行控制。 實現(xiàn)輕松的接入,為智能硬件助力,將底層硬件的控制通過j
    發(fā)表于 09-21 15:35

    請問Labview與Java交互是否可以實現(xiàn)?

    各位大佬,小弟目前在做畢業(yè)設(shè)計是關(guān)于人工智能的,本人想利用laview myrio 機(jī)器人的sensor 傳送數(shù)據(jù)到 java, 然后java進(jìn)行信息處理然后輸出一個相應(yīng)的信息,然后傳遞到labview 使得機(jī)器人
    發(fā)表于 08-13 17:09

    芯靈思SinlinxA33開發(fā)板的安卓控制LED-2-JNI基礎(chǔ)

    ,對應(yīng)于在java層使用native關(guān)鍵字聲明的方法的。直白的說,就是在Java層聲明,C/C++語言實現(xiàn)的。當(dāng)然,這個函數(shù)并不一般,它會通過JNI某種機(jī)制與
    發(fā)表于 02-22 16:55

    基于JNI的嵌入式手機(jī)軟件該如何去設(shè)計?

    Java的性能問題及幾種解決方案什么是JNI技術(shù)基于JNI的嵌入式手機(jī)軟件開發(fā)實例
    發(fā)表于 04-23 07:17

    何用 rust 語言開發(fā) stm32

    本文介紹如何用 rust 語言開發(fā) stm32。開發(fā)平臺為 linux(gentoo)。硬件準(zhǔn)備本文使用的芯片為 STM32F103C8T6。該芯片性價比較高,價格低廉,適合入門學(xué)習(xí)。需要
    發(fā)表于 11-26 06:20

    JNI如何實現(xiàn)Android stdio IIC與從機(jī)通信的呢

    RK3288如何實現(xiàn)JNI對接上層Java和下層的C++呢?JNI如何實現(xiàn)Android stdio IIC與從機(jī)通信的呢?
    發(fā)表于 03-04 06:04

    何用java映射創(chuàng)建java對象和調(diào)用java對象呢

    java是一種解析語言,java程序是通過java虛擬機(jī)解析.class的方式運行起來。因此,java中就存在
    發(fā)表于 04-11 14:43

    java與c之間的數(shù)據(jù)交互

    最近作一個tiemsten數(shù)據(jù)庫的項目,用到了jni技術(shù)。在這個項目中,我們用java來寫界面和業(yè)務(wù)邏輯,用c語言寫數(shù)據(jù)庫odbc訪問。單純的odbc其實沒有什么難的,但是在java和c之間進(jìn)
    發(fā)表于 11-27 10:22 ?1858次閱讀

    JNI java調(diào)用so動態(tài)庫方法

    JNI Java調(diào)用so包相關(guān)問題總結(jié),出現(xiàn)了很多問題,按照操作應(yīng)該不會發(fā)生不到so包的錯誤,其實最后出現(xiàn)的也是說加載不到libpython2.7.x.so.1,我就納悶了,怎么和python扯上
    發(fā)表于 11-28 13:13 ?3642次閱讀

    RSA算法的JNI封裝步驟

    要求較高的算法往往是基于C/C++語言(與硬件關(guān)聯(lián)性更強(qiáng))實現(xiàn)的。如果應(yīng)用程序需要基于JAVA編程實現(xiàn)時,這就會有一些矛盾。此時,通過JNI技術(shù),Java開發(fā)者可以在不了解算法內(nèi)容的情
    的頭像 發(fā)表于 06-04 17:45 ?2417次閱讀
    RSA算法的<b class='flag-5'>JNI</b>封裝步驟

    Go/Rust挑戰(zhàn)Java/Python地位

    編程語言方面,Java 和 Python 仍然遙遙領(lǐng)先,并且分別微小增長了 1.7% 和 3.4%;圍繞 Go (增長 20%) 和 Rust (增長 22%) 的興趣則大幅增加。報告稱,如果這種
    的頭像 發(fā)表于 03-06 10:19 ?886次閱讀

    何用Java播放聲音

    在本篇文章中,我們將學(xué)習(xí)如何用Java播放音樂,Java 聲音 API 的設(shè)計是為了流暢和連續(xù)地播放聲音,甚至是很長的聲音。我們將使用 Java 提供的 Clip 和 SourceDa
    的頭像 發(fā)表于 10-09 10:56 ?4158次閱讀

    何用Java代碼調(diào)用

    CloneNotSupportedException ; 你敢說你沒用過這些方法?如果你用過,那你就是一定用過不是Java語言編寫的方法。 答案就是【native】關(guān)鍵詞,用此關(guān)鍵詞修飾的方法,多數(shù)情況就不是用Java實現(xiàn)的。 那么為什么要用 native 來修飾方法,
    的頭像 發(fā)表于 10-11 15:29 ?726次閱讀
    如<b class='flag-5'>何用</b><b class='flag-5'>Java</b>代碼調(diào)用

    何用Rust編寫一個ChatGPT桌面應(yīng)用(保姆級教程)

    用IDEA開發(fā)的java仔) 安裝 Rust 語言工具鏈:首先,請確保你已安裝了 Rust 編程語言工具鏈,包括 Rust 編譯器 (rustc) 和包管理工具 (cargo)。可以
    的頭像 發(fā)表于 09-25 11:19 ?617次閱讀
    如<b class='flag-5'>何用</b><b class='flag-5'>Rust</b>編寫一個ChatGPT桌面應(yīng)用(保姆級教程)
    主站蜘蛛池模板: 亚洲精品久久久久午夜福 | 黄色精品| 俄罗斯一级特黄黄大片 | 一级特黄视频 | 中文字幕在线二区 | 午夜影院免费 | 国产成人经典三级在线观看 | 色多多网站在线观看 | 日本高清视频一区 | 欧美不在线 | 日本一区二区三区四区在线观看 | 四虎国产精品永久免费网址 | 中国一级特黄高清免费的大片 | 天天爽夜夜爽人人爽免费 | 国产情侣自拍小视频 | 久久性生活 | 狠狠色丁香婷婷综合久久片 | 日本成人黄色网址 | 日本www色| 西西人体www303sw大胆高清 | 天天网综合 | 1314亚洲人成网站在线观看 | 国产美女精品一区二区三区 | 欧美性爽xxxⅹbbbb | 天天狠天天透天干天天怕处 | 亚洲最大成人在线 | 国产一区二区在线观看免费 | 国产精品成人四虎免费视频 | 爱情社保片鲁丝片一区 | 中文字幕色婷婷在线精品中 | 激情综合激情五月 | 久久9精品| 狠狠干天天爱 | 久久久午夜精品理论片 | 一级毛片在线 | 免费国产成人午夜私人影视 | 看亚洲a级一级毛片 | 美女性视频网站 | 欧美成人天天综合在线视色 | 日本三级全黄 | 欧美一区二区三区激情啪啪 |