Search code examples
javaandroidjavafxgluongraalvm-native-image

gluonfx nativePackage for Android


How do i enable multiDex for classes so they can be included or traced on the DexPathList?

I am using graalvm (with native-image) and gluonfx nativePackage task to generate the apk.

id 'com.gluonhq.gluonfx-gradle-plugin' version '1.0.3' //build.gradle

but when I try to access an SMS BroadcastReceiver, I get Class not found in the DexPathList ...

this is the androidManifest setup ..

     <application ... package="demo"   ...>
        <uses-permission android:name="android.permission.RECEIVE_SMS"/>
        <receiver android:name=".services.SmsListener" 
                  android:enabled="true" 
                  android:exported="true"
                  android:permission="android.permission.BROADCAST_SMS">
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
            </intent-filter>
        </receiver>
     </application>

and the error i get when an sms drops on the phone is as below .. Unable to instantiate receiver demo.services.SmsListener: java.lang.ClassNotFoundException: Didn't find class "demo.services.SmsListener" on path: DexPathList[[zip file "/data/app/.../base.apk"],nativeLibraryDirectories=[/data/app/...-g==/lib/arm64, /data/app/..g==/base.apk!/lib/arm64-v8a, /system/lib64, /system_ext/lib64]]

So how go I pass MultiDexEnabled True to android task? or Is there a way i can start this broadcast class through java code? project structure


Solution

  • The initial question is about adding Java code to the Android sources, before packaging the project into the final APK.

    Modifying the Android project

    Since there is no automatic procedure from the gluonfx plugin yet, it is a manual process:

    • Run mvn -Pandroid gluonfx:build gluonfx:package once, that will create the Android project under target/gluonfx/aarch64-android/gvm/android_project.

    • Add the Java/Android sources to target/gluonfx/aarch64-android/gvm/android_project/app/src/main/java

    • Build manually the APK:

      cd target/gluonfx/aarch64-android/gvm/android_project/
      export ANDROID_SDK_ROOT=~/.gluon/substrate/Android
      ./gradlew app:assembleDebug
      
    • Continue the process: mvn -Pandroid gluonfx:install gluonfx:run

    While this allows modifying the Android part, the main caveat is the lack of communication from this side to the Java/JavaFX/GraalVM part, so it will only work in case it is an isolated modification on the Android side.

    Creating a custom Attach service

    In order to modify the Android part and also get access to the Java/JavaFX side, the best approach is to create an Attach service.

    For this, one option is to clone Attach, add the new service, and publish/distribute this modified version of Attach, but better is to create just a custom Attach service outside of Attach.

    LogService

    This experimental repository contains a new service: LogService, just as proof of concept.

    The readme already contains instructions on how to get started and use this service.

    Build the service

    To summarise:

    • Make sure you follow the requisites
    • Clone the repository
    • Build and publish to local: ./gradlew clean publishToMavenLocal.
    • Make sure you have the artifacts under ~/.m2/repository/org/jpereda/attach/log/4.0.12-SNAPSHOT:
       log-4.0.12-SNAPSHOT-android.jar
       log-4.0.12-SNAPSHOT-ios.jar 
       log-4.0.12-SNAPSHOT-desktop.jar
       log-4.0.12-SNAPSHOT.jar
    

    Use LogService in your project

    Assuming you are running [HelloGluon](https://github.com/gluonhq/gluon-samples/tree/master/HelloGluon):
    pom

    Add the dependency to the pom. Note that the official Attach dependencies remain the same, but you will need 4.0.12-SNAPSHOT.

    <dependencies>
        <dependency>
            <groupId>com.gluonhq.attach</groupId>
            <artifactId>storage</artifactId>
            <version>4.0.12-SNAPSHOT</version>
        </dependency>
        ...
        <dependency>
            <groupId>org.jpereda.attach</groupId>
            <artifactId>log</artifactId>
            <version>4.0.12-SNAPSHOT</version>
        </dependency>
    </dependencies>
    
    <repositories>
            <repository>
                <id>snapshots</id>
                <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            </repository>
        </repositories>
    

    Notice that since it is not an official Attach service, it can't use the package name com.gluonhq.attach, and it can't be added to the attachList. Therefore, it is treated as a regular dependency, and you need to add the platform dependency based on the target:

    For instance, for Android:

            <profile>
                <id>android</id>
                <properties>
                    <gluonfx.target>android</gluonfx.target>
                </properties>
                <dependencies>
                    <dependency>
                        <groupId>org.jpereda.attach</groupId>
                        <artifactId>log</artifactId>
                        <version>4.0.12-SNAPSHOT</version>
                        <classifier>android</classifier>
                        <scope>runtime</scope>
                    </dependency>
                </dependencies>
            </profile>
    
    code

    From your code, call the service as usual:

    LogService.create().ifPresent(service -> service.log("This is a message"));
    

    Test the service

    For instance, on Android:

    mvn -Pandroid gluonfx:build gluonfx:package gluonfx:install gluonfx:nativerun
    

    Create your own service

    First of all, read https://docs.gluonhq.com/#_device_interface for a better understanding of an Attach service.

    Following the LogService project it should be more or less straightforward to create your own service.

    The more complex part is in the communication between Java(11+)/JavaFX/GraalVM and Java(7)/Android, where JNI is required. The Java side is AOT compiled and goes into lib${service}.a and the Android side goes to the Android project and distributed as ${service}>.aar, without JavaFX support.