JNI編程答疑

原文 https://developer.android.com/training/articles/perf-jni?hl=zh-cn#java

1 JaveVM 和JNIEnv是什麼

jni相關的方法幾乎都放在JNIEnv裏面,JNIEnv是用於線程本地存儲的,因此不能跨線程共享使用。如果一個類需要JNIEnv對象,不要直接傳遞JNIEnv,而應該通過JaveVM,通過GetEnv方法獲得。

JNI defines two key data structures, "JavaVM" and "JNIEnv". Both of these are essentially pointers to pointers to function tables. (In the C++ version, they're classes with a pointer to a function table and a member function for each JNI function that indirects through the table.) The JavaVM provides the "invocation interface" functions, which allow you to create and destroy a JavaVM. In theory you can have multiple JavaVMs per process, but Android only allows one.

The JNIEnv provides most of the JNI functions. Your native functions all receive a JNIEnv as the first argument.

The JNIEnv is used for thread-local storage. For this reason, you cannot share a JNIEnv between threads. If a piece of code has no other way to get its JNIEnv, you should share the JavaVM, and use GetEnv to discover the thread's JNIEnv. (Assuming it has one; see AttachCurrentThread below.)

The C declarations of JNIEnv and JavaVM are different from the C++ declarations. The "jni.h" include file provides different typedefs depending on whether it's included into C or C++. For this reason it's a bad idea to include JNIEnv arguments in header files included by both languages. (Put another way: if your header file requires #ifdef __cplusplus, you may have to do some extra work if anything in that header refers to JNIEnv.)

2 線程和AttachCurrentThread 

android裏面線程有兩種,java層通過Thread.start方式啓動的託管線程,是被JavaVM管理的。還有一種是native中通過pthread_create方法創建的線程,這種線程不受JavaVM管理,沒有JNIEnv,因此不能做任何JNI調用,必須要AttachCurrentThread,這個方法會導致一個java.lang.Thread對象被構建,並且扔到“main” ThreadGroup中,讓調試器可以監視到。

All threads are Linux threads, scheduled by the kernel. They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then attached to the JavaVM. For example, a thread started with pthread_create can be attached with the JNI AttachCurrentThread or AttachCurrentThreadAsDaemonfunctions. Until a thread is attached, it has no JNIEnv, and cannot make JNI calls.

Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main" ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread is a no-op.

Android does not suspend threads executing native code. If garbage collection is in progress, or the debugger has issued a suspend request, Android will pause the thread the next time it makes a JNI call.

Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward, in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)

3 局部和全局引用

傳遞到jni函數的參數和返回值都是一個局部引用,生命週期只限於當前native方法中,這個和c++是一樣的,在方法內部的局部對象一旦在方法執行完畢就會被釋放。這些對象包括jobect及其子類,像jclass,jstring 和jarray。如果想要將局部引用變成全局的,就要用NewGlobalRef或者NewWeakGlobalRef方法了。因此native方法內部jstring,jarray等對象不去釋放也是可以的,GC是會回收的,但是受限於系統資源限制,推薦使用DeleteLocalRef方法去手動釋放它

4 native方法中通過NewStringUTF或者NewFloatArray之類申請的對象需要手動釋放嗎?

不需要,但是使用DeleLocalRef釋放更好

5 GetStringUTFChars或GetByteArrayElements獲取的數據指針需要釋放嗎?

需要,調用這兩個方法獲取數據指針後,必須要ReleaseStringUTFChars或者ReleaseByteArrayElements,他會告訴GC,可以對對象進行正常gc操作了,否則GC不確定當前指針還在有效期,就提前回收,會照成事故的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章