Writing Native Methods

J2ObjC supports embedding Objective-C into Java native methods, very similar to how GWT's JSNI supports JavaScript embedding. The main difference between J2ObjC embedding and GWT's is that J2ObjC uses /*-[ and ]-*/ to delineate Objective-C code. This facility is called OCNI (Objective-C Native Interface), to differentiate itself from GWT's JSNI.

Here's an example from the JRE emulation library's version of java.lang.System:

  public static native long currentTimeMillis() /*-[
    // Use NSDate
    return (long long) ([[NSDate date] timeIntervalSince1970] * 1000);
  ]-*/;

J2ObjC copies the comment, minus the delimiters, to create the method body:

  + (long long int)currentTimeMillis {
    // Use NSDate
    return (long long) ([[NSDate date] timeIntervalSince1970] * 1000);
  }

Native Imports

J2ObjC scans the Java code being translated to add #import directives for its dependencies, as well as importing the Foundation framework. However, any imports needed only by native code need to be separately added. To add imports, add an OCNI section above the first class in the Java source file and specify the imports there; for example:

  package my.project;

  /*-[
  #import "java/lang/NullPointerException.h"
  ]-*/

  public class Test {
    native void test() /*-[
      @throw [[JavaLangNullPointerException alloc] init];
    ]-*/;
  }

The import is necessary in the above example because the only place that type is referenced is in native code.

Native Blocks

Within a class body, J2ObjC scans for OCNI blocks. These blocks are added unmodified to the translated file, in the same position relative to translated class members. Here's an example:

  /*-[
    static void log(NSString *msg) {
      NSLog(@"%@", msg);
    }
  ]-*/;

This C function can be invoked from any native methods that are declared after this OCNI block.

A special variant of this OCNI block inserts code in the generated header instead of the .m file: /*-HEADER[...]

Invoking Java Methods from Native Code

public native void bar(JSNIExample x, String s) /*-[
  // Call instance method instanceFoo() on this
  [self instanceFooWithNSString:s];

  // Call instance method instanceFoo() on x
  [x instanceFooWithNSString:s];

  // Call static method staticFoo()
  JSNIExample_staticFooWithNSString_(s);
]-*/;

Accessing fields from Native Code

To read an instance field, use either myInstanceField_ or self->myInstanceField_. The trailing suffix avoids a clash with methods having the same name.

Note that fields that have reserved names will have two underscores. For example, a field named "id" is legal in Java, but not in Objective C. When translated, that field will be named "id__". Hence, please check the generated files if there are "no-such-field" compiler errors.

To write to an object instance field, use JSNIExample_set_myInstanceField(string)

Read a static field: JSNIExample_get_myStaticField()

Write a static field: JSNIExample_set_myStaticField(value)

J2ObjC and GWT

Different delimiters were chosen so that in the next J2ObjC release, GWT JSNI comments will be ignored (previously the same delimiters were used as GWT). This means that a single Java source can have native methods that have Objective-C, GWT, and Android (via JNI) implementations:

  static native void log(String text) /*-{ // left-brace for JavaScript
    console.log(text);
  }-*/ /*-[                                // left-bracket for Objective-C
     NSLog(@"%@", text);
  ]-*/;

J2ObjC and Android

J2ObjC and Android native method implementations "just work", because Android native methods are implemented in a separate JNI C or C++ file. Any OCNI comments in Java classes are removed when compiled by javac for Android or any other Java platform.