B4J Code Snippet How to load a Class file at runtime

After seeing a qustion in B4A asking how to 'insert' a class at runtime I thought I would post an example.
Uses JavaObject
I just use GraalVm as I like the native-image support.
The class can be on local machine or on a server. (file:/// or https://)

B4X:
'Non-UI application (console / server application)
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
    #JavaCompilerPath: 22, D:/graalvm-comm-openjdk-22/bin/javac.exe
#End Region

Sub Process_Globals
    Dim myNewClass As Object 'ignore

End Sub

Sub AppStart (Args() As String)
    myNewClass = Me.as(JavaObject).RunMethod("loadTheClass",Array(Me,"org.dynamic.DynamicClass","file:///C:\SimpleLibraryCompiler\DynamicClass\bin\classes\"))
    'direct calls
    'Me.as(JavaObject).RunMethod("call",Array(myNewClass,"say1","Hello there world!"))
    'Log(Me.as(JavaObject).RunMethod("call",Array(myNewClass,"say2","Hello there world! ")))
  
    'calls via subs
    say1("Hello world!")
    Log(say2("Hello world! "))
End Sub

Sub say1(message As String)
    Me.as(JavaObject).RunMethod("call",Array(myNewClass,"say1",message))
End Sub

Sub say2(message As String) As String
    Return Me.as(JavaObject).RunMethod("call",Array(myNewClass,"say2",message))
End Sub
#if java
import java.net.URL;
import java.net.URLClassLoader;
import java.lang.reflect.*;

public static Object loadTheClass(Class c,String className, String path) throws Exception {
    // url for the class path to look in
    URL url = new URL(path);
    //get the app class loader
    ClassLoader parent = c.getClassLoader();
    //create a url class loader with app class loader as parent
    URLClassLoader classLoader = new URLClassLoader(new URL[]{url}, parent);
    // load the class
    Class<?> MyClass = classLoader.loadClass(className);
    // object to hold reference as MyClass cannot be directly used
    Object o = MyClass.newInstance();
    return o;
}
// call the method in the object - you need to know the name and params and what to expect to get back
public static Object call(Object c,String methodName, Object arg) throws NoSuchMethodException , IllegalAccessException, InvocationTargetException{
    return c.getClass().getMethod(methodName,arg.getClass()).invoke(c,arg);
}
#End If

' DynamicClass was compiled with SLC /bin/classes holds the compiled class
'
'package org.dynamic;
'public class DynamicClass {
'    public DynamicClass(){}
'  
'
'    public void say1(String s){
'        System.out.println(s);
'    }
'
'    public String say2(String s){
'        Return s + "message from class";
'    }
'}
 
Last edited:

rboeck

Well-Known Member
Licensed User
Longtime User
After seeing a qustion in B4A asking how to 'insert' a class at runtime I thought I would post an example.
Uses JavaObject
I just use GraalVm as I like the native-image support.
The class can be on local machine or on a server. (file:/// or https://)

B4X:
'Non-UI application (console / server application)
#Region Project Attributes
    #CommandLineArgs:
    #MergeLibraries: True
    #JavaCompilerPath: 22, D:/graalvm-comm-openjdk-22/bin/javac.exe
#End Region

Sub Process_Globals
    Dim myNewClass As Object 'ignore

End Sub

Sub AppStart (Args() As String)
    myNewClass = Me.as(JavaObject).RunMethod("loadTheClass",Array(Me,"org.dynamic.DynamicClass","file:///C:\SimpleLibraryCompiler\DynamicClass\bin\classes\"))
    'direct calls
    'Me.as(JavaObject).RunMethod("call",Array(myNewClass,"say1","Hello there world!"))
    'Log(Me.as(JavaObject).RunMethod("call",Array(myNewClass,"say2","Hello there world! ")))
 
    'calls via subs
    say1("Hello world!")
    Log(say2("Hello world! "))
End Sub

Sub say1(message As String)
    Me.as(JavaObject).RunMethod("call",Array(myNewClass,"say1",message))
End Sub

Sub say2(message As String) As String
    Return Me.as(JavaObject).RunMethod("call",Array(myNewClass,"say2",message))
End Sub
#if java
import java.net.URL;
import java.net.URLClassLoader;
import java.lang.reflect.*;

public static Object loadTheClass(Class c,String className, String path) throws Exception {
    // url for the class path to look in
    URL url = new URL(path);
    //get the app class loader
    ClassLoader parent = c.getClassLoader();
    //create a url class loader with app class loader as parent
    URLClassLoader classLoader = new URLClassLoader(new URL[]{url}, parent);
    // load the class
    Class<?> MyClass = classLoader.loadClass(className);
    // object to hold reference as MyClass cannot be directly used
    Object o = MyClass.newInstance();
    return o;
}
// call the method in the object - you need to know the name and params and what to expect to get back
public static Object call(Object c,String methodName, Object arg) throws NoSuchMethodException , IllegalAccessException, InvocationTargetException{
    return c.getClass().getMethod(methodName,arg.getClass()).invoke(c,arg);
}
#End If

' DynamicClass was compiled with SLC /bin/classes holds the compiled class
'
'package org.dynamic;
'public class DynamicClass {
'    public DynamicClass(){}
' 
'
'    public void say1(String s){
'        System.out.println(s);
'    }
'
'    public String say2(String s){
'        Return s + "message from class";
'    }
'}
Today i was thinking to try, which use graalvm could be for b4j users. Can you make a short description, how to install and use and what differences in performance you can see?
 

Daestrum

Expert
Licensed User
Longtime User
Using GraaalVM is as easy as using openJDK - just create the folder structure the same as in openJDK (javafx folder + sub directories).
To use native-image you need Visual Studio installed, as it uses that to compile to an exe. If you us reflection (as B4J does itself) it will produce an exe but requires the jar to run. It basically creates a jvm contaning only enough to run your app.

As an example I have a jar that is 106Kb (a.jar)
The jvm it 'makes' is 13.3MB (a.exe)

To use anywhere (on a windows machine) I just copy the jar and exe and run the exe = 13.4MB

As an aside, I made the example in post#1 into a native-image (thats how I knew the sizes of the jar and exe) wherever I move the jar & exe to, it still pulls in the class at run time.
 
Last edited:

Cableguy

Expert
Licensed User
Longtime User
I just use GraalVm as I like the native-image support.
Hi @Daestrum
For those of us who still use OpenJDK, what would be the changes needed to your code?
I imagine that commenting out the #JavaCompilerPath: 22, D:/graalvm-comm-openjdk-22/bin/javac.exe line won't suffice.
 

Daestrum

Expert
Licensed User
Longtime User
addendum: If the class (to be pulled in) is produced by the IDE then you must compile in release to get a usable .class file Also the names will be like B4X in the class.
ie public sub fred() will be _fred (lowercased and prepended with underscore)
 
Top