Using Java¶
Use the Java API to interact with Kanzi through a platform-agnostic Java API
When you want to create applications that are tightly integrated with the Android APIs, use the Java programming language with the Kanzi Android framework. See Developing with the Kanzi Android framework and Kanzi Java API reference.
Using properties¶
Java API classes contain static members for built-in property types such as Node.OpacityProperty
. To read and write these properties:
// Access a node from the view.
Node2D node = view.getRoot();
// Set property.
node.setProperty(Node.OpacityProperty, 0.5);
// Get property.
float opacity = node.getProperty(Node.OpacityProperty);
To access a property by name, create an instance of DynamicPropertyType
or AbstractPropertyType
using a full name of a property type:
// Use DynamicPropertyType to access a property type when the type is known.
DynamicPropertyType<Boolean> visibleProperty =
new DynamicPropertyType("Node.Visible", Boolean.class);
node.setProperty(visibleProperty, false);
// Use AbstractPropertyType to access a property type when the type is not known.
AbstractPropertyType visibleProperty = new AbstractPropertyType("Node.Visible");
node.setProperty(visibleProperty, false);
See Property system and Reference for showing Kanzi Engine plugin custom types in Kanzi Studio.
Using messages¶
Java API classes contain static members for built-in message types such as Button2D.ClickMessage
. To dispatch these messages:
// Access a node from the view.
Node2D node = view.getRoot();
node.dispatchMessage(Button2D.ClickMessage, new MessageArguments());
To dispatch a message by name, create an instance of AbstractMessageType
using the full name of a message type:
// Get reference to a message type.
AbstractMessageType messageType = new AbstractMessageType("Message.MyMessage");
DynamicPropertyType<Boolean> argumentProperty =
new DynamicPropertyType("MyMessage.Argument", Boolean.class);
// Access a node from the view.
Node2D node = view.getRoot();
// Construct message arguments.
MessageArguments args = new MessageArguments();
// Set message arguments.
args.setArgument(argumentProperty, false);
// Send the message.
node.dispatchMessage(messageType, args);
To perform some functionality when a node receives a message, add a message handler to the node:
// Add message subscription.
button.addMessageHandler(Button2D.ToggleStateMessage,
new Node.MessageSubscriptionFunction()
{
@Override
public void handle(MessageArguments messageArguments)
{
// Extract arguments.
int toggleState = messageArguments.getArgument(Button2D.ToggleStateProperty);
}
});
See Using messages.
Object lifetime¶
The ObjectRef
class controls object lifetime in Java. The native backing for the object is not reclaimed until the ObjectRef
is either explicitly closed, or allowed to be garbage collected. Native objects can also have other native owners that are managed by the native portion of the Kanzi Engine. Therefore an ObjectRef
does not exclusively control the lifetime of the object.
It is good practice to close all ObjectRef
instances when the application no longer needs them.
Kanzi automatically wraps all newly created objects in an ObjectRef
instance as the sole owner.
When you remove all references to an object, and no native references exist, the object is immediately reclaimed. If application code attempts to access the object in this state, Kanzi throws a StaleObjectException
to indicate that the object is no longer valid. To prevent this, you can wrap the object in a new ObjectRef
instance before that object becomes stale. The new instance extends the lifetime and prevents the native object from being reclaimed.
// Create an EmptyNode2D node.
ObjectRef<EmptyNode2D> nodeRef = EmptyNode2D.create(domain, "My Empty Node");
// Access the EmptyNode2D instance from within the ObjectRef.
EmptyNode2D node = nodeRef.get();
// Use the node
// Close the ObjectRef.
nodeRef.close();
// If you try to access the node variable after closing its ObjectRef,
// Kanzi can throw a StaleObjectException.
node.setName("New Name");
To automatically manage the ObjectRef
instance, use the Java try-with-resources statement:
// Create an EmptyNode2D node.
try (ObjectRef<EmptyNode2D> nodeRef = EmptyNode2D.create(domain, "My Empty Node"))
{
// Access the EmptyNode2D instance from within the ObjectRef.
EmptyNode2D node = nodeRef.get();
// Use the node
// Kanzi automatically closes the nodeRef on the next line.
}
Exceptions¶
Kanzi uses checked exceptions to indicate recoverable errors. For example, if the resource is not found when you call the Node.acquireResource
method, Kanzi throws the ObjectNotFoundException
exception.
Kanzi Engine exceptions thrown by native code are packagaged into instances of NativeException
. It is not expected for Java code to handle these, instead allow them to propagate up the stack so that Kanzi retranslates it to a native exception that Kanzi Engine can handle them.
Platform-specific code¶
Java plugins can run on multiple platforms, such as Kanzi Android framework and in the Kanzi Studio Preview. Make sure that the application runs platform-specific code only when it is running on a compatible platform. For example, call the Android API only when the application is running in an Android environment.
To detect the environment, use the getPlatformContext()
method:
if (Platform.isAndroid())
{
// Initialize the code path for when the plugin runs on Android.
Object context = domain.getPlatformContext();
}
else
{
// Initialize the code path for when the plugin runs in the Kanzi Studio Preview.
}
Creating a custom node type¶
You can use Java to extend the functionality of Kanzi Engine by creating a custom node type.
To create a custom node type:
Create a class in a Java project.
class MyNode2D extends Node2D { }
Kanzi requires these items for each custom node:
Metaclass. The metaclass must be present in this form:
@Metadata public static final Metaclass metaclass = new Metaclass("Example.MyNode2D", MyNode2D.class);
You can provide additional metadata with the
@EditorInfo
annotation. See Reference for showing Kanzi Engine plugin custom types in Kanzi Studio.Construction. Construction of derived types must follow this pattern with a static
create
method and a private constructor with the specific arguments. The implementation of thecreate
method is usually a call to thecreateDerived
method on the type that you are extending.
static public ObjectRef<MyNode2D> create(Domain domain, String name) { return Node2D.createDerived(domain, name, metaclass); } private MyNode2D(Domain domain, long nativeNode, Metaclass metaclass) { super(domain, nativeNode, metaclass); }
(Optional) Custom initialization. During the constructor Kanzi does not fully initialize custom Kanzi Java objects. Place custom initialization code in the
initialize()
method.
@Override protected void initialize() { super.initialize(); // Add custom initialization code after calling super.initialize() }
(Optional) Create custom property types.
@Metadata @EditorInfo(displayName = "Custom Property") public static final PropertyType<String> = CustomProperty = new PropertyType("MyNode2D.Custom", String.class); // Properties can also have an automatically generated name. // In this case 'MyNode2D.Other'. @Metadata public static final PropertyType<String> = OtherProperty = new PropertyType(String.class);
(Optional) Create custom message types.
Define the message type.
@Metadata public static final MessageType ExampleMessage = new MessageType("Message.MyNode2D.Example", MessageRouting.MessageRoutingBubbling);
Add a message handler.
// Add message subscription. addMessageHandler(MyNode2D.ExampleMessage, new Node.MessageSubscriptionFunction() { @Override public void handle(MessageArguments messageArguments) { ... } });
(Optional) Define custom node behavior by overriding relevant methods.
@Override protected void arrangeOverride(Vector2 actualSize) { super.arrangeOverride(actualSize); ... }
Creating a custom resource manager protocol¶
To load resources using Java, implement a custom resource manager protocol.
ResourceManager resourceManager = domain.getResourceManager();
resourceManager.registerProtocolHandler("MyProtocol",
new ResourceManager.ProtocolHandler()
{
@Override
public Result handle(ResourceManager resourceManager, String url, String protocol,
String hostname, String path) {
if (some condition)
{
// Load a resource synchronously.
Resource resource = ...;
return new Result(resource);
}
else
{
// Use load task for asynchronous loading.
return new ResourceManager.LoadTask() {
@Override
public void loadFunction(ResourceManager resourceManager) {
// Perform thread-independent loading.
}
@Override
public void finishFunction(ResourceManager resourceManager) {
// Create and store the resource.
}
@Override
public Resource getResult() {
// Return the created resource.
}
};
}
}
});
In case of failure, the resource protocol handle
can throw an exception, which the Kanzi Resource Manager handles.
Creating a data source¶
A data source enables you to access third-party data from your Kanzi application.
To create a data source:
Create the
DataSource
class.import com.rightware.kanzi.DataSource; class MyDataSource extends DataSource { @Metadata public static final Metaclass metaclass = new Metaclass("MyPlugin.MyDataSource", MyDataSource.class); public static ObjectRef<MyDataSource> create(Domain domain, String name) { return DataSource.createDerived(domain, name, MyDataSource.metaclass); } private MyDataSource(Domain domain, long nativeHandle, Metaclass metaclass) { super(domain, nativeHandle, metaclass); } @Override protected void initialize() { super.initialize(); createData(); } }
Add the required members, accessors, and the method to initialize the data.
private ObjectRef<DataObject> mDataRef; private DataObject mData; private DataObjectBool mMyBool; public DataObject getData() { return mData; } private void createData() { Domain domain = getDomain(); mDataRef = DataObject.create(domain, "root"); mData = mDataRef.get(); // Add the remaining structure of the DataSource try (ObjectRef<DataObjectBool> objRef = DataObjectBool.create(domain, "MyBoolean", true)) { mMyBool = objRef.get(); mData.addChild(mMyBool); } }
To learn more about the data sources in Kanzi, see Data sources.
Creating Kanzi Engine Java plugins¶
You can combine custom Java types into a Kanzi Engine plugin.
To create a Kanzi Engine Java plugin:
Create a class for the plugin.
class TestPlugin extends Plugin { @Override public String getName() { return "mytestplugin"; } }
Tip
The string returned from
getName()
must match the name of the Jar file.Add metadata for the custom types.
Register the metadata of all the custom Kanzi types in that plugin.
@Override public void registerClasses(MetaclassRegistry metaclassRegistry) { metaclassRegistry.registerClass(ExampleNode2D.class); }
Tip
You can override an existing type by using the
overrideClass
instead ofregisterClass()
.Load the plugin in your Android application.
For example, load the plugin during the initialization of an Activity.
@Override protected void onCreate(Bundle icicle) { ... try (KanziRuntime.Reference runtimeRef = KanziRuntime.acquire(this)) { runtimeRef.Get().getDomain().registerPlugin(new TestPlugin()); } ... }
To learn more about creating Kanzi Engine Java plugins, see Creating a Java Kanzi Engine plugin manually.