- Android Native Development Kit Cookbook
- Feipeng Liu
- 1647字
- 2021-07-27 18:07:26
Managing references in JNI
JNI exposes strings, classes, instance objects, and arrays as reference types. The previous recipe introduces the string type. This recipe will cover reference management and the subsequent three recipes will discuss class, object, and arrays respectively.
How to do it…
The following steps create a sample Android project that illustrates reference management in JNI:
- Create a project named
ManagingReference
. Set the package name ascookbook.chapter2
. Create an activity namedManagingReferenceActivity
. Under the project, create a folder namedjni
. Refer to the Loading native libraries and registering native methods recipe in this chapter, if you want more detailed instructions. - Create a file named
referencetest.c
under thejni
folder, then implement thelocalReference
,globalReference
,weakReference
, andreferenceAssignmentAndNew
methods. This is shown in the following code snippet:JNIEXPORT void JNICALL Java_cookbook_chapter2_ManagingReferenceActivity_localReference(JNIEnv *pEnv, jobject pObj, jstring pStringP, jboolean pDelete){ jstring stStr; int i; for (i = 0; i < 10000; ++i) { stStr = (*pEnv)->NewLocalRef(pEnv, pStringP); if (pDelete) { (*pEnv)->DeleteLocalRef(pEnv, stStr); } } } JNIEXPORT void JNICALL Java_cookbook_chapter2_ManagingReferenceActivity_globalReference(JNIEnv *pEnv, jobject pObj, jstring pStringP, jboolean pDelete){ static jstring stStr; const jbyte *str; jboolean *isCopy; if (NULL == stStr) { stStr = (*pEnv)->NewGlobalRef(pEnv, pStringP); } str = (*pEnv)->GetStringUTFChars(pEnv, stStr, isCopy); if (pDelete) { (*pEnv)->DeleteGlobalRef(pEnv, stStr); stStr = NULL; } } JNIEXPORT void JNICALL Java_cookbook_chapter2_ManagingReferenceActivity_weakReference(JNIEnv *pEnv, jobject pObj, jstring pStringP, jboolean pDelete){ static jstring stStr; const jbyte *str; jboolean *isCopy; if (NULL == stStr) { stStr = (*pEnv)->NewWeakGlobalRef(pEnv, pStringP); } str = (*pEnv)->GetStringUTFChars(pEnv, stStr, isCopy); if (pDelete) { (*pEnv)->DeleteWeakGlobalRef(pEnv, stStr); stStr = NULL; } }
- Modify the
ManagingReferenceActivity.java
file by adding code to load the native library, then declare and invoke the native methods. - Modify the
res/layout/activity_managing_reference.xml
file according to step 8 of the Loading native libraries and registering native methods recipe in this chapter, or the downloaded project code. - Create a file named
Android.mk
under thejni
folder. Refer to step 9 of the Loading native libraries and registering native methods recipe of this chapter, or the downloaded code for details. - Start a terminal, go to the
jni
folder, and typendk-build
to build the native library. - Run the project on an Android device or emulator and monitor the logcat output with either eclipse or the
adb logcat -v time
command in your terminal. We'll show the sample results for each native method when while going through the details in the following section.
How it works…
This recipe covers reference management in JNI:
- JNI reference: JNI exposes strings, classes, instance objects, and arrays as references. The basic idea of a reference can be illustrated using the following diagram:
The reference adds one more level of indirection to an object (an object can be a class, an instance object, a string, or an array) access. An object is pointed by an object pointer, and a reference is used to locate the object pointer. Although this indirection introduces an overhead for object manipulation, it allows VM to conceal the object pointer from developers. The VM can therefore move the underlying object at runtime memory management and update the object pointer value accordingly, without affecting the reference.
Note that the garbage collector at VM moves the objects around to achieve cheap memory allocation, bulk de-allocation, reduce heap fragmentation, improve locality, and so on.
- Local reference versus global reference versus weak reference: Three different types of references can be created to refer to the same data, namely local reference, global reference, and weak reference. Unless we explicitly create a global or weak reference, JNI operates using a local reference. The following table summarizes the differences between the three different types of references:

We will now take a look at the reference types one by one while referring to the sample source code:
- Local reference: The native method
localReference
shows the two basic JNI functions, namelyNewLocalRef
andDeleteLocalRef
. The first function creates a local reference, while the second frees it. Note that normally we don't have to free a local reference explicitly, as it will be automatically freed after the native method returns. However, there are two exceptions. First, if we're creating lots of local references within a native method invocation, we can cause overflow. This is illustrated in our sample method when we passfalse
to thepDelete
input parameter. The following screenshot represents an example of such a scenario:The first execution deletes the local reference right after using it, so it's finished fine, while the second doesn't delete the local reference and eventually causes the
ReferenceTable
overflow.Secondly, when we implement a utility function that is called by other native functions, we should not leak any references other than the return value. Otherwise, if the utility function is invoked by a native method many times, it will also cause an overflow issue.
Tip
Prior to Android 4.0, the local references were implemented using direct pointers to objects. Furthermore, those direct pointers were never invalidated even after
DeleteLocalRef
was called. Therefore, programmers can use local references as direct pointers, even after the reference is claimed to be deleted. A lot of JNI code not coded correctly worked due to this design. However, local references have been changed to use an indirection mechanism from Android 4.0 onwards. Hence, the buggy code using local references as direct pointers are going to break in Android 4.0 onwards. It is strongly recommended that you always follow JNI specifications. - Global reference: The native method,
globalReference
, demonstrates a typical usage of a global reference. The global reference is retained when passingfalse
to thepDelete
input parameter, since it is a static variable. Next time the method is called, the static global reference will still reference to the same object. Therefore, we don't need to callNewGlobalRef
again. This technique can save us from carrying out the same operation at every invocation of global reference.We invoke
globalReference
three times in the Java code, as follows:globalReference("hello global ref", false); globalReference("hello global ref 2", true); globalReference("hello global ref 3", true);
The results should look similar to the following:
The string passed along with the first method call is retained, and therefore the first two invocations display the same string. After we delete the global reference at the end of the second call, the third call displays the string passed along with it.
Note that although
DeleteGlobalRef
frees the global reference, it doesn't set it toNULL
. We have explicitly set the global reference toNULL
after the deletion. - Weak reference: Weak reference is similar to global reference, except that it doesn't prevent the Garbage Collector (GC) from garbage collecting the underlying object referenced by it. Weak reference is not used as often as local and global reference. A typical use case is when we are referencing to lots of non-critical objects, and we don't want to prevent the GC from garbage collecting some of those objects when the GC thinks it's necessary.
Tip
The support for weak references in Android is version dependent. Prior to Android 2.2, weak references were not implemented at all. Prior to Android 4.0, it can only be passed to
NewLocalRef
,NewGlobalRef
, andDeleteWeakGlobalRef
. From Android 4.0 onwards, Android has full support for weak references. - Assignment versus New<ReferenceType>Ref: In the
referencetest.c
source code, we implemented the nativeReferenceAssignmentAndNew
method. This method illustrates the difference between assignment and allocating a new reference.We passed the input jstring
pStringP
to the JNI functionNewGlobalRef
twice, to create two global references (globalRefNew
andglobalRefNew2
), and assigned one of the global references to a variableglobalRefAssignment
. We then tested if they were all referencing to the same object.Since
jobject
andjstring
are actually pointers to void data type, we can print out their values as integers. Lastly, we invokedDeleteGlobalRef
three times. The following is a screenshot of the Android logcat output:The first three lines indicate that the input jstring
pStringP
, two global referencesglobalRefNew
andglobalRefNew2
, and the assigned jstringglobalRefAssignment
all reference to the same object. Lines five to eight of the output show the same value, which means all the references themselves are equivalent. Lastly, the first two calls ofDeleteGlobalRef
succeed, while the last one fails.The
New<ReferenceType>Ref JNI
function actually locates the underlying object and then adds a reference to the object. It allows multiple references added for the same object. Note that although our sample execution shows the values of references created byNew<ReferenceType>Ref
are the same, it is not guaranteed. It is possible that two object pointers pointing to the same object and references referencing to the same object are associated with the two different pointers.It is recommended that you never rely on the value of a reference; you should use JNI functions instead. One example of this is to use
IsSameObject
and never use "==
" to test if two references point to the same underlying object unless we test againstNULL
.The number of
Delete<ReferenceType>Ref
calls must match the number ofNew<ReferenceType>Ref
invocations. Fewer calls will potentially cause a memory leak, while having more calls will fail, as shown in the preceding result.The assignment operation doesn't go through the VM, therefore it won't cause the VM to add a new reference.
Note that although we used a global reference for illustration, the principles also apply to local and weak references.
There's more…
There's another method to manage the local references with JNI functions PushLocalFrame
and PopLocalFrame
. Interested readers can refer to the JNI specification for more information.
After attaching a native thread with AttachCurrentThread
, the code running in the thread would not free the local references until the thread is detached. The local reference should be freed explicitly. In general, it is a good practice that we free local reference explicitly, as long as we don't need it any more.
- The Complete Rust Programming Reference Guide
- 程序員數學:用Python學透線性代數和微積分
- Animate CC二維動畫設計與制作(微課版)
- JSP開發案例教程
- 人人都是網站分析師:從分析師的視角理解網站和解讀數據
- JavaScript動態網頁開發詳解
- C語言程序設計學習指導與習題解答
- Instant Ext.NET Application Development
- Learning Laravel's Eloquent
- R語言數據可視化:科技圖表繪制
- Buildbox 2.x Game Development
- Python數據可視化之美:專業圖表繪制指南(全彩)
- Python Programming for Arduino
- Java EE實用教程
- Java網絡編程實用精解