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_關鍵字this、super、static、final(終結器)與基本資料型別轉換
一、關鍵字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#有多種資料型別,在使用各種型別的資料時,有時候需要將一種型別的資料轉換