Skip to content

Implementing Native Methods

gershnik-smartsheet edited this page May 22, 2016 · 3 revisions

One of the most common tasks when writing JNI code is to implement native Java methods. SmJNI makes it very easy and straightforward. Make sure you read about Declaring Java Types and Representing Java Classes before reading this page; it builds upon information explained there.

There are 2 ways of implementing native methods in raw JNI. One is by giving special names to methods exposed from a shared library. Another is by manually registering implementations of Java methods. The first approach can be used with SmJNI, if desired, but it has many known deficiencies and is not generally recommended. SmJNI provides direct support for manual registration.

In order to register a native method using SmJNI you will need to

  1. Define all the Java types used in method signature as described in Declaring Java Types. For example if the method you are implementing uses java.util.Map as an argument or return value, you will need to do something along the lines of

    DEFINE_JAVA_TYPE(jMap,        "java.util.Map"); 
  2. Declare the C++ implementation using the precise mapping of Java types to C++. The C++ method will have 2 parameters in addition to whatever parameters Java method takes. For static methods these will be:

    JNIEnv *, jclass

    For instance methods these will be

    JNIEnv *, <mapped type you defined>

    For example consider

    class Something
    {
        native boolean instanceMethod(int i, Map m);
        static native boolean staticMethod(int i, Map m);
    }

    These will require the following C++ declarations

    DEFINE_JAVA_TYPE(jMap,        "java.util.Map");
    DEFINE_JAVA_TYPE(jSomething,  "com.mystuff.Something"); 
    
    jboolean instanceMethod(JNIEnv * env, jSomething obj, jint i, jMap m);
    jboolean staticMethod(JNIEnv * env, jclass cls, jint i, jMap m);
  3. Obtain a java_class<type> instance for the type your methods belong to somehow. See Representing Java Classes for various ways to do so.

  4. Use java_registration class instance to register C++ methods. For the example above you would do something like this

    java_class<jSomething> cls = ...;
    
    java_registration<jSomething> registration;
    registration.add_instance_method("instanceMethod", instanceMethod);
    registration.add_static_method("staticMethod", staticMethod);
    registration.perform(env, cls);

Similar to other reasons given in Representing Java Classes it is usually advantageous to perform the registration together with all other Java resolving from within JNI_OnLoad. You can accomplish this by putting the registration code into the constructor of the C++ representation class. The representation class also provides a good scope to put the native implementations in. For the example above

class ClassOfSomething : public java_runtime::simple_java_class<jSomething>
{
    ClassOfSomething(JNIEnv * env):
        simple_java_class(env)
    {
        java_registration<jSomething> registration;
        registration.add_instance_method("instanceMethod", instanceMethod);
        registration.add_static_method("staticMethod", staticMethod);
        registration.perform(env, *this);
    } 

    static jboolean instanceMethod(JNIEnv * env, jSomething obj, jint i, jMap m);
    static jboolean staticMethod(JNIEnv * env, jclass cls, jint i, jMap m);
};

jboolean ClassOfSomething::instanceMethod(JNIEnv * env, jSomething obj, jint i, jMap m)
{
    //implementation
}
jboolean ClassOfSomething::staticMethod(JNIEnv * env, jclass cls, jint i, jMap m)
{
    //implementation
}

Clone this wiki locally