Livecode Wiki
Advertisement
Warning Warning

The following topics are only for very skilled livecode programmers, not for beginners. The majority of livecoders doesn't need to know how to code widgets, they can use group.

It is now possible to bind to Java class methods and fields in LCB via the Java Native Interface (JNI). Foreign handlers bind to Java class using the existing foreign handler syntax, but with an appropriate binding string.

The Java binding string has the following form:

"java:[className>][functionType.]function[!calling]"

Where className is the qualified name of the Java class to bind to, functionType is either empty, or get or set, which are currently used for getting and setting member fields of a Java class, and function specifies the name of the method or field to bind to. The function new may be used to call a class constructor.

function also includes the specification of function signature, according to the standard rules for forming these when calling the JNI.

calling specifies the calling convention which can be one of:

  • instance
  • static
  • nonvirtual

Instance and nonvirtual calling conventions require instances of the given Java class, so the foreign handler declaration will always require a Java object parameter.

Examples[]

Binding to a class constructor with no parameters:

foreign handler CreateJavaObject() returns JObject binds to "java:java.lang.Object>new()"

Binding to a class constructor with parameters:

foreign handler CreateJavaString(in pBytes as JByteArray) returns JString binds to "java:java.lang.String>new([B)"

Binding to a class instance method:

foreign handler JavaStringIsEmpty(in pString as JString) returns CBool binds to "java:java.lang.String>isEmpty()Z"

Binding to a class static method:

foreign handler CallJavaAdd(in pLeft as CInt, in pRight as CInt) returns CInt binds to "java:java.lang.Math>addExact(JJ)J!static"

Binding to a class field:

foreign handler JavaCalendarSetTime(in pObj as JObject) returns nothing binds to "java:java.util.Calendar>set.time(J)" 
foreign handler JavaCalendarGetTime(in pObj as JObject) returns CInt binds to "java:java.util.Calendar>get.time()J"

Binding to a class constant:

foreign handler GetJavaPi() returns CDouble binds to "java:java.lang.Math>get.PI()D!static"

Android battery level example[]

Android is all Java based, so we have to work with Java. This example show you how to create the GetBatteryPercentage() function to get the value for the battery.

We'll use the Java FFI to monitor the battery level of an Android device. We jump through a few hoops in order to fetch the application’s Context– in the future we plan to provide an API for some useful things like this.

The battery changed intent is known as a sticky intent – this is why we can access the relevant values at any time rather than registering a BroadcastReceiver to respond to changes in the battery state.

All such sticky intents can be obtained in exactly the same way, passing null as the BroadcastReceiver (in this case passing nothing as the first parameter in ApplicationRegisterReceiver).

Then various values can be accessed using the appropriate EXTRA_ constants. For more information see the Android Intent API.

Here the code:

library com.livecode.library.androidbattery

use com.livecode.foreign
use com.livecode.java

metadata title is "Android Battery Library"
metadata author is "LiveCode"
metadata version is "1.0.0"

foreign handler ClassForName(in pClass as JString) returns JObject binds to "java:java.lang.Class>forName(Ljava/lang/String;)Ljava/lang/Class;!static"

foreign handler GetCurrentApplicationMethod(in pActivityThread as JObject, in pMethod as JString, in pParams as optional JObject) returns JObject binds to "java:java.lang.Class>getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"

foreign handler MethodInvoke(in pMethod as JObject, in pInstance as optional JObject, in pParams as optional JObject) returns JObject binds to "java:java.lang.reflect.Method>invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"

foreign handler GetIntentFilter(in pAction as JString) returns JObject binds to "java:android.content.IntentFilter>new(Ljava/lang/String;)"

foreign handler ApplicationRegisterReceiver(in pContext as JObject, in pObj as optional JObject, in pFilter as JObject) returns JObject binds to "java:android.content.Context>registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;"

foreign handler GetIntentValueInt(in pIntent as JObject, in pWhich as JString, in pDefault as CInt) returns CInt binds to "java:android.content.Intent>getIntExtra(Ljava/lang/String;I)I"

public handler GetBatteryPercentage() returns Number
   unsafe 
   // Get the application context. We rely on an undocumented 
   // method to get this 'statically' at the moment.
   variable tClass as JObject
   put ClassForName(StringToJString("android.app.ActivityThread")) into tClass
   variable tMethod as JObject
   put GetCurrentApplicationMethod(tClass, StringToJString("currentApplication"), nothing) into tMethod
   variable tContext as JObject
   put MethodInvoke(tMethod, nothing, nothing) into tContext
   // Get an intent filter for the battery changed intent		
   variable tFilter as JObject
   put GetIntentFilter(StringToJString("android.intent.action.BATTERY_CHANGED")) into tFilter
   // Get the intent
   variable tIntent as JObject
   put ApplicationRegisterReceiver(tContext, nothing, tFilter) into tIntent
   // Get the relevant values from the intent.
   variable tLevel as Integer
   put GetIntentValueInt(tIntent, StringToJString("level"), -1) into tLevel
   variable tScale as Integer
   put GetIntentValueInt(tIntent, StringToJString("scale"), -1) into tScale
   return tLevel / tScale * 100	
   end unsafe
 end handler
end library

lc-compile-ffi-java[]

lc-compile-ffi-java is a tool to create LiveCode Builder code which provides an interface between LCB modules and the Java Native Interface (JNI). It does this by taking a specification of a java package written in a domain-specific language and creating an LCB module which wraps the necessary foreign handlers which call the appropriate JNI methods.

The lc-compile-ffi-java program is usually in livecodecommunity-9.xxx/Toolchain/ folder

See the documentation for the tool and the description of the DSL for more details

Note[]

Note of 10th March 2017: Foreign bindings to Java feature is currently supported on Android, Mac and Linux. Binding to java classes requires the availability of a Java runtime and access to the appropriate libraries. On Mac, the JAVA_HOME environment variable must be set to the path to your Java installation (usually at /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home ). On Linux, your LD_LIBRARY_PATH must be set to the folder containing the libjvm.so library (usually at ${JAVA_HOME}/jre/lib/amd64/server) on 64-bit Linux.

Advertisement