1. 程式人生 > >JNI資料型別轉換和JNIEnv的介紹、操作jobject,以及jstring的介紹

JNI資料型別轉換和JNIEnv的介紹、操作jobject,以及jstring的介紹

摘錄、參考文件:

1.深入理解Android:卷1 作者:鄧凡平

上一章,講了關於JNI註冊的相關知識;

這一章講的內容比較多,主要是以下幾方面的內容:

1)java與JNI之間的資料型別轉換;

2)JNIEnv的介紹;

3)JNIEnv的使用,如何操作jobject;

4)jstring介紹。


1. 資料型別轉換

在Java中呼叫native函式傳遞的引數是java資料型別,那麼這些引數到了JNI層會變成什麼呢?

java資料型別分為基本資料型別和應用資料型別兩種,JNI層也是區別對待兩者的。先來看看基本資料型別的轉換。

1.基本資料型別轉換

上面列出了java的基本資料型別和jni層資料型別對應的轉換關係,非常簡單。不過,務必注意轉換成native型別後對應資料型別的字長。

2.應用資料型別的轉換

由上表可知:除了java中基本資料型別的陣列、Class、String和Throwable外,其餘所有java物件的資料型別在JNI中都用jobject表示。

可以看看processFile這個函式:

從上面這段程式碼中可以發現:

1)java中的String型別對應jni中的jstring型別;

2)java中的MediaScannerClient物件型別在JNI層對應jobject型別。

如果物件型別都用jobject表示,就好比是native層的void*型別一樣,對於我們來說,它們完全是透明的。既然是透明的,那麼我們如何來操作它們呢?在回答這個問題之前,我們在仔細看一下上面的android_media_MediaScanner_processFile函式,程式碼如下:

該如何操作jobject,使用JNIEnv。JNIEnv是下面將要說的重點。

2.JNIEnv介紹

JNIEnv是一個與執行緒相關的代表JNI環境的結構體。從上圖可知,JNIEnv實際上就是提供了一些JNI系統函式。通過這些函式可以:

1)呼叫java函式;

2)操作jobject物件做很多事情。

JNIEnv是一個與執行緒相關的物件。也就是說,執行緒A有一個JNIEnv,執行緒B也有一個JNIEnv。由於執行緒相關,所以不能線上程B中使用執行緒A的JNIEnv結構體。但是,JNIEnv不都是native函式轉換成jni函式後由虛擬機器傳進來的嗎?使用傳進來的這個JNIEnv總不會錯吧?是的,在這種情況下使用當然不會報錯。只是當後臺執行緒收到一個網路訊息,而又需要由native層函式主動回撥java層函式時,JNIEnv從何而來?根據前面的介紹可知,我們不能儲存另外一個執行緒的JNIEnv結構體,然後把它放到後臺執行緒中用。這該如何是好?

還記得上一篇文章介紹動態註冊JNI函式時的那個JNI_OnLoad函式嗎?它的第一個引數是JavaVM,它是虛擬機器在JNI層的代表,全程序只有一個JavaVM物件,不論程序中有多少個執行緒,JavaVM卻是獨此一份,所以可以儲存,並且在任何地方使用都沒有問題。

那麼,JavaVM和JNIEnv又有什麼關係呢?答案如下:

1)呼叫JavaVM的AttachCurrentThread函式,就可得到這個執行緒的JNIEnv結構體。這樣就可以在後臺執行緒中回撥java函數了;

2)另外,在後臺執行緒退出前,需要呼叫JavaVM的DetachCurrentThread函式來釋放對應的資源。

對於JNIEnv的介紹到此為止,下面我們來說說JNIEnv的使用。

3.通過JNIEnv操作jobject

上面說到過一個問題,即Java的應用型別除了少數的幾個外,最終在JNI層都會用jobject來表示物件的資料型別,那麼該如何操作這個jobject呢?

從另外一個角度解釋這個問題。一個java物件是由什麼組成的?當然是它的成員變數和成員函數了。那麼,操作jobject的本質就應當是操作這些物件的成員變數和成員函式。所以應先來看與成員變數及成員函式有關的內容。

1)jfieldID和jmethodID介紹

我們知道,成員變數和成員函式都是由類定義的,它們是類的屬性,所以在JNI規則中,用jfieldID和jmethodID來表示java類的成員變數和成員函式,可通過JNIEnv的下面兩個函式得到:

其中,jclass代表java類,name表示成員變數或成員函式的名字,sig為這個函式或變數的簽名信息。如上圖所示,成員變數和成員函式都是類的資訊,這兩個函式的第一個引數都是jclass。

我們來看看在MediaScanner中是怎麼使用它們的:

在上面的程式碼中,講scanFile和handleStringTag函式的jmethodID儲存為MyMediaScannerClient的成員變數。為什麼要儲存它們?這個問題涉及一個關於程式執行效率的知識點:

如果每次操作jobject前都去查詢jfieldID和jmethodID,那麼將會影響程式執行的效率,所以我們在初始化的時候可以取出這些ID並儲存起來以供後續使用。

取出jmethod之後,又該如何使用它?

2.使用jfieldID和jmethodID

再看一個例子:

明白了吧,通過JNIEnv輸出CallVoidMethod,再把jobject、jmethodID和對應的引數傳進去,JNI層就能夠呼叫java物件的函數了。

實際上JNIEnv輸出了一系列類似CallVoidMethod的函式,形式如下:

NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID, ...)

其中type對應java函式的返回值型別,例如CallIntMethod、CallVoidMethod等。

上面是針對非static函式的,如果想呼叫java中的static函式,則用JNIEnv提供的CallStatic<type>Method系列函式。

現在我們知道了如何通過JNIEnv操作jobject的成員函式,那麼如何通過JNIEnv操作jobject的成員變數呢?如下所示:

現在,我們已經瞭解了jfieldID和jmethodID的作用,也知道了如何通過JNIEnv的函式來操作jobject。雖然jobject是透明的,但有了JNIEnv的幫助,還是能輕鬆操作jobject背後的實際物件的。

4. jstring介紹

Java中的String也是應用型別,不過由於它的使用頻率較高,所以在JNI規範中單獨建立了一個jstring型別來表示java中的String型別。雖然jstring是一種獨立的資料型別,但它並沒有提供成員函式以便操作。而c++中的string類是有自己的成員函式的。那麼該怎麼操作jstring呢?還是得依靠JNIEnv提供幫助。這裡看幾個有關jstring的函式:

1)呼叫JNIEnv的NewString(JNIEnv *env, const jchar *unicodeChars, jsize len),可以從native的字串得到一個jstring物件。其實,可以把一個jstring物件看成是java中String物件在JNI層的代表,也就是說,jstring就是一個java String。但是由於java String 儲存的是Unicode字串,所以NewString函式的引數也必須是Unicode字串。

2)呼叫JNIEnv的NewStringUTF函式,將根據native的一個UTF-8字串得到一個jstring物件,在實際工作中,這個函式用的最多。

3)上面兩個函式將本地字串轉換成了java的String物件,JNIEnv還提供了GetStringChars函式和GetStringUTFChars函式,它們可以將java的String物件轉換成本地字串。其中GetStringChars得到一個Unicode字串,而GetStringUTFChars得到一個UTF-8字串。

4)另外,如果在程式碼中呼叫了上面幾個函式,在做完相關工作後,就都需要呼叫ReleaseStringChars和ReleaseStringUTFChars函式來對應的釋放資源,否則會導致JVM洩露。這一點和jstring的內部實現有關,讀者寫程式碼時務必注意這個問題。

為了加深印象,來看processFile是怎麼做的。

文章到這裡就結束了。

在這裡,感謝深入理解Android的作者鄧凡平,謝謝他的貢獻。

相關推薦

JNI資料型別轉換JNIEnv介紹操作jobject以及jstring介紹

摘錄、參考文件: 1.深入理解Android:卷1 作者:鄧凡平 上一章,講了關於JNI註冊的相關知識; 這一章講的內容比較多,主要是以下幾方面的內容: 1)java與JNI之間的資料型別轉換; 2)JNIEnv的介紹; 3)JNIEnv的使用,如何操作jobject;

JNI native層C++非同步回撥JAVA程式碼。JNI資料型別轉換

現在專案做移植的。 windows轉移到Linux下,多程序的程式。 本來windows下用MFC做的介面,現在決定Linux下介面用java或者python做。。。 java和其他C++程序用JNI來融合到一起。。。 java部分的程式碼如下: public clas

C# 資料型別轉換 顯式轉型隱式轉型強制轉型

C# 的型別轉換有 顯式轉型 和 隱式轉型 兩種方式。 顯式轉型:有可能引發異常、精確度丟失及其他問題的轉換方式。需要使用手段進行轉換操作。 隱式轉型:不會改變原有資料精確度、引發異常,不會發生任何問題的轉換方式。由系統自動轉換。  不同型別的資料進行操作(加減乘除賦值等等),是需要

Android------JNI 資料型別轉換

  ndk可以用程式碼1呼叫 , 但例子中是用程式碼2。程式碼2可能是c++中的標準語法,程式碼1是ndk的。引用時要注意 程式碼1:env->GetStringUTFChars(str) 程式碼2:(*env)->NewStringUTF(env, st

C# 資料型別轉換 顯式轉型隱式轉型強制轉型

C# 的型別轉換有 顯式轉型 和 隱式轉型 兩種方式。 顯式轉型:有可能引發異常、精確度丟失及其他問題的轉換方式。需要使用手段進行轉換操作。 隱式轉型:不會改變原有資料精確度、引發異常,不會發生任何問題的轉換方式。由系統自動轉換。  不同型別的資料進行操作(加減乘除賦值等等),是需要進行 型別轉換 後,才能繼

NDK學習筆記:jni資料型別轉換

背景 隨著Android專案中c++程式碼部分功能複雜程度的增加,jni中需要傳遞的資料型別也越來越多,關於jni資料型別轉換網上有不少相關文章,但是在使用時發現這些例子中存在不少謬誤,遂在此重新總結相關內容,並附相關例程,以便日後參考。 下文我們將對以下幾

JS顯性資料型別轉換隱性資料型別轉換

一、JS需要型別轉換的原因 JS是一種弱型別語言,變數沒有型別限制,可以隨意賦值。如: var a=5; console.log(typeof a);//number a='我是字串'; console.log(typeof a);//string如上所示,當把數字5賦值

springMVC學習筆記四(資料型別轉換資料驗證)

=============================資料型別轉換和資料驗證======================= 資料型別轉換 Spring 內建的 PropertyEditor 如下所示: 類名                         說明    

C++之基本資料型別轉換轉換函式

基本資料型別轉換 C++語言中型別轉換有兩種:隱式轉換和強制轉換。在型別轉換的過程中還有保值轉換和非保值轉換之分。保值轉換是安全的,資料精度不會受到損失,如資料型別有低向高轉換;非保值轉換是不安全的,

第4章:介紹python物件型別/4.1 python的核心資料型別/4.2.1 字串獲取操作字串合併重複操作

字串獲取操作 概念:用雙引號或者單引號括起來的一串字元 字串按下標獲取操作 定義字串 >>> S="abcd" 給字串求長度 >>> len(S)      4

【JavaScript基礎筆記】資料型別轉換false值記憶體圖垃圾回收深淺拷貝簡易概念

其他型別轉換成字串 xxx.toString()   // var object = {a:1}; object.toString = [object Object]   //這種方法對null undefined使用會報錯 xxx +

4.Java_關鍵字thissuperstaticfinal(終結器)與基本資料型別轉換

一、關鍵字this 1.表示呼叫本類屬性:在類中訪問類的屬性,一定要加上this關鍵字。 2.表示呼叫本類方法:         (1)呼叫普通方法:this.方法名(引數);   當有類的繼承關係時,表示本類方法一定要加上th

談談JavaScript的算數運算二進位制浮點數舍入誤差及比較型別轉換變數宣告提前問題

  在《JavaScript權威指南》一書第三章節“型別、值和變數”中,作者詳細介紹了Javascript的數字、文字、布林值等型別,全域性物件,包裝物件,型別轉換,變數作用域等概念。其中有3個地方需要我們在使用過程中引起注意,可能稍不留神就犯錯:   1)算數運算與浮點數比較問題   2)

Android中JNI使用詳解(4)---Java與C之間資料型別轉換

Jni中基本型別轉換對應的表格 Java型別 本地型別 說明 boolean jboolean 無符號,8位 byte jbyte

Java中的基本資料型別型別轉換規則(自動強制)原反補碼使用者自定義識別符號

基本資料型別 資料型別指明瞭變數和表示式的狀態和行為。 基本資料型別 關鍵字 記憶體中佔用位元組數 取值範圍 布林型 boolean 1位元組(8bit)

C#之資料型別轉換迴圈三元表示式使用方法

轉換資料型別 Convert.To…… 想把資料轉換成什麼型別就寫些什麼樣的,在convert.To直接加 //這一行程式碼要用int型別的變數來接收,那麼可以說,這個方法的返回值是int型別 Int numbers=convert.ToInt32(“4”);  

Java種的基本資料型別轉換(自動強制提升)

Java種的8大基本資料型別,以及它們所佔記憶體大小和範圍 1、自動型別轉換 自動型別轉換是指:數字表示範圍小的資料型別可以自動轉換成範圍大的資料型別。 如: long l = 100; int i = 200; long ll = i; 具體自動轉換如如下圖所示:

資料型別轉換單位日期轉換工具

這是我在專案中常用到的資料型別轉換、單位、日期轉換工具,為了怕以後找不到故記錄於此。 import android.annotation.TargetApi; import android.content.Context; import android.content.res.C

Java SE基礎部分--02.Java資料型別轉換運算子方法入門

學習目標: 1、資料型別轉換、 2、算數運算子、 3、賦值運算子、 4、比較運算子、 5、邏輯運算子、 6、三元運算子、 7、方法定義和呼叫 一、資料型別轉換: 1、資料型別轉換分為:隱式型別轉換、強制型別轉換。 隱式轉換:是將容量小的型別自動轉成容量大的

C#程式設計基礎第十課:C#中的常用資料型別轉換:隱式轉換顯式轉換Convert類轉換

知識點:型別轉換、數值型別間的轉換、隱式型別轉換、顯式型別轉換、數值型別和string型別的轉換、Convert類轉換。 1、資料型別型別轉換 理解:從根本上說是型別鑄造,或者說是把資料從一種型別轉換為另一種型別。C#有多種資料型別,在使用各種型別的資料時,有時候需要將一種型別的資料轉換