Advanced usage

Memory management

In most cases, memory management of native resources is automatically taken care of. Java-GI uses GObject toggle references to dispose the native object when the Java instance is garbage-collected, and manages all memory allocations for marshaling of string, array and struct parameters.

Allocating structs with an Arena

Struct definitions (in contrast to GObjects) in native code are mapped to Java classes. Because structs don't necessarily have a constructor method, the Java classes offer an allocate() method to allocate a new uninitialized struct. To determine the size of the struct and its members, the memory layouts have been generated from the field definitions in the gir files.

Java-GI requires an Arena to allocate memory for these instances. Users can choose between different arenas in OpenJDK, depending on the use case. All allocate() methods generated by Java-GI for record (struct) and union types expect an Arena as the first parameter.

Example:

try (var arena = Arena.ofConfined()) {
    var red = RGBA.allocate(arena, 1.0, 0.0, 0.0, 0.0);
    // memory is now allocated
}
// memory is now deallocated

var blue = RGBA.allocate(Arena.ofAuto(), 0.0, 0.0, 1.0, 0.0);
// memory will be deallocated when the variable is garbage collected

var green = RGBA.allocate(Arena.global(), 0.0, 1.0, 0.0, 0.0);
// memory will be allocated during the entire application runtime

Read the OpenJDK Arena documentation for more information.

To ease the transition, in Java-GI version 0.8.x, the old allocate() methods without an Arena parameter are still available. These methods use an Arena.ofAuto() behind the scenes. Dealing with native memory deallocations during GC is rather ugly, so the Arena.ofAuto() should preferrably not be the default option. Therefore, these methods have been marked as deprecated, and have been removed in Java-GI 0.9.0.

Builder pattern

You can construct an object with GObject properties using a Builder pattern. For example, to create a new ApplicationWindow:

var window = ApplicationWindow.builder()
    .setApplication(this)
    .setTitle("Window")
    .setDefaultWidth(300)
    .setDefaultHeight(200)
    .build();

With a Builder you can set the properties of the class, its parents, and all implemented interfaces. Behind the scenes, this will call g_object_new_with_properties().

Exceptions

GError** parameters are mapped to Java GErrorExceptions.

try {
    file.replaceContents(contents, null, false, FileCreateFlags.NONE, null, null);
} catch (GErrorException e) {
    e.printStackTrace();
}

Use GErrorException.getCode(), getDomain() and getMessage() to get the GError code, domain and message. Java-GI does not generate separate Exception types for different GError domains, because the domain is only set at runtime.

Nullable/NotNull parameter annotations

Nullability of parameters (as defined in the GObject-introspection attributes) is indicated with @Nullable and @NotNull attributes, and checked at runtime. The nullability annotations are imported from Jetbrains Annotations.

Arrays

C functions that work with arrays, often expect the array length as an additional parameter. In the corresponding Java methods, that parameter is unnecessary, because Java-GI will set it automatically.

Out-parameters

Out-parameters are mapped to a simple Out<T> container-type in Java, that offers typesafe get() and set() methods to retrieve or modify the value.

File file = ...
Out<byte[]> contents = new Out<byte[]>();
file.loadContents(null, contents, null));
System.out.printf("Read %d bytes\n", contents.get().length);

Varargs

Variadic functions are available in Java using varargs:

Dialog d = Dialog.withButtons(
        "Test dialog",
        window,
        DialogFlags.MODAL,
        "Accept",
        ResponseType.ACCEPT,
        "Cancel",
        ResponseType.CANCEL,
        null
);
d.show();

Be aware that with most variadic functions in GLib, you are expected to add null as a final parameter.

Note: Java-GI does not provide bindings for functions with a va_list parameter.

Signals and callbacks

Signals are mapped to type-safe methods and objects in Java. (Detailed signals like notify have an extra String parameter.) A signal can be connected to a lambda expression or method reference:

var button = Button.withLabel("Close");
button.onClicked(window::close);

For every signal, a method to connect (e.g. onClicked) and emit the signal (emitClicked) is included in the API. New signal connections return a Signal object, that allows you to disconnect, block and unblock a signal, or check whether the signal is still connected.

Functions with callback parameters are supported too. The generated Java bindings contain @FunctionalInterface definitions for all callback functions to ensure type safety.

Closures

Closures can be marshaled to Java methods. Similar to the CClosure type in C code, Java-GI offers a JavaClosure. You can create a JavaClosure for a lambda fuction or a java.lang.reflect.Method and then pass it to native code (for example, the last two parameters of GObject.bindPropertyFull()).

Be aware that the Java lambda or method references that is wrapped in a JavaClosure must have the correct signature, or else the application will fail at runtime. Closures cannot be type-checked by the compiler!

Registering a new type

Registering a Java class as a new GType is documented here.

Creating a Gtk composite template class

To create a composite template class to use in a Gtk application, read these instructions.