Manipulating classes in JNI

The previous recipe discusses that Android JNI supports three different kinds of references. The references are used to access the reference data types, including string, class, instance object, and array. This recipe focuses on class manipulations in Android JNI.

Getting ready

The Managing References in NDK recipe should be read first before going through this recipe.

How to do it…

The following steps describe how to build a sample Android application that illustrates class manipulation in JNI:

  1. Create a project named ClassManipulation. Set the package name as cookbook.chapter2. Create an activity named ClassManipulationActivity. Under the project, create a folder named jni. Refer to the Loading native libraries and registering native methods recipe of this chapter if you want more detailed instructions.
  2. Create a file named classtest.c under the jni folder, then implement the findClassDemo, findClassDemo2, GetSuperclassDemo, and IsAssignableFromDemo methods. We can refer to the downloaded ClassManipulation project source code.
  3. Modify ClassManipulationActivity.java by adding code to load the native library, declare native methods, and invoke native methods.
  4. Create a Dummy class and a DummySubClass subclass that extends the Dummy class. Create a DummyInterface interface and a DummySubInterface subinterface, which extends the DummyInterface.
  5. Modify the layout XML file, add the Android.mk build file, and build the native library. Refer to steps 8 to 10 of the the Loading native libraries and registering native methods recipe of this chapter for details.
  6. We're now ready to run the project. We'll present the output while discussing each native method in the following section.

How it works…

This recipe demonstrates the manipulation of classes in JNI. We highlight a few points as follows:

  • Class descriptor: A class descriptor refers to the name of a class or an interface. It can be derived by replacing the "." character in Java with "/" in JNI programming. For example, the descriptor for class java.lang.String is java/lang/String.
  • FindClass and class loader: The JNI function FindClass has the following prototype:
    jclass FindClass(JNIEnv *env, const char *name);

    It accepts a JNIEnv pointer and a class descriptor, and then locates a class loader to load the corresponding class. It returns a local reference to an initialized class, or NULL in case of failure. FindClass uses the class loader associated with the topmost method of the call stack. If it cannot find one, it will use the "system" class loader. One typical example is that after we create a thread and attach it to the VM, the topmost method of the call stack will be as follows:

    dalvik.system.NativeStart.run(Native method)

    This method is not part of our application code. Therefore the "system" class loader is used.

    Tip

    A thread can be created at Java (called the managed thread or Java thread) or native code (called the native thread or non-VM thread). The native thread can be attached to a VM by calling the JNI function AttachCurrentThread. Once attached, the native thread works just like a Java thread, running inside a native method. It remains attached until the JNI function DetachCurrentThread is called.

    In our ClassManipulation project, we illustrated FindClass with findClassDemo and findClassDemo2 native methods. The findClassDemo method runs in a VM created thread. The FindClass call will locate the class loader properly. The findClassDemo2 method creates a non-VM thread and attaches the thread to VM. It illustrates the case we described in the preceding section. The logcat output for calling the two native methods is as follows:

    How it works…

    As shown in the output, the non-VM thread loads the String class successfully but not the Dummy class defined by us. The way to work around this issue is to cache a reference to the Dummy class in the JNI_OnLoad method. We'll provide a detailed example in the Caching jfieldID, jmethodID, and referencing data to improve performance recipe.

  • GetSuperclass: The JNI function GetSuperclass has the following prototype:
    jclass GetSuperclass(JNIEnv *env, jclass clazz);

    It helps us to find the superclass of a given class. If clazz is java.lang.Object, this function returns NULL; if it's an interface, it returns a local reference to java.lang.Object; if it's any other class, it returns a local reference to its superclass.

    In our ClassManipulation project, we illustrated GetSuperclass with the GetSuperclassDemo native method. We created a Dummy class and a DummyInterface interface in Java code, where DummySubClass extends Dummy, and DummySubInterface extends DummyInterface. In the native method, we then invoked GetSuperclass to java.lang.Object, DummySubClass, and DummySubInterface respectively. The following is a screenshot of the logcat output:

    How it works…

    As shown in the screenshot, GetSuperclass can find the superclass of DummySubClass successfully. In this native method, we used a utility function nativeGetClassName, where we called the toString method. We'll cover more about how to make such method calls in the Calling instance and static methods in JNI recipe.

  • IsAssignableFrom: The JNI function IsAssignableFrom has the following prototype:
    jboolean IsAssignableFrom(JNIEnv *env, jclass cls1, jclass cls2);

    This function returns JNI_TRUE if cls1 can be safely casted to cls2, and JNI_FALSE otherwise. We demonstrated its usage with the native method IsAssignableFromDemo. We obtained a local reference to DummySubClass, and called GetSuperclass to get a local reference to Dummy. Then, we called IsAssignableFrom to test if we can cast DummySubClass to Dummy and vice versa. The following is a screenshot of the logcat output:

    How it works…

    As expected, the subclass can be safely cast to its superclass, but not the other way round.

Tip

The JNI function DefineClass is not supported on Android. This is because the function requires the raw class data as input, and the Dalvik VM on Android doesn't use the Java bytecode or class files.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset