2
votes

I'm trying to get an application written in JavaFX ported across to Android and for the most part I have been successful in my attempts. In that the application will run on Android after packaging an apk file using Netbeans IDE and Gradle.

But the problem I have is that the application will crash when trying to access a google map through JavaFX Webview component. What I am using is the following in JavaFX:

googleMapEngine.load(getClass().getResource("/googleLondonMap.html").toExternalForm());

And the html file is located under the Resources folder in the Gradle root folder.

When I test the application in Netbeans by right clicking on the Gradle root node and selecting Tasks --> Run --> Run. My application runs perfectly, including accessing the google map using the html file and JavaFX Webview.

The creation of the apk file is also successful in using Tasks --> Android --> Android. But once I install the apk file on an android device and access the google map portion of my application it throws me out of the application saying "Unfortunately has stopped". Any clues on how to fix this?

Looking at the log file created using the adb command I saw the following output:

E/AndroidRuntime( 6038): FATAL EXCEPTION: JavaFX Application Thread

E/AndroidRuntime( 6038): Process: com.SundaeThePugApp, PID: 6038

E/AndroidRuntime( 6038): java.lang.RuntimeException: java.lang.reflect.InvocationTargetException

E/AndroidRuntime( 6038):    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1774)

E/AndroidRuntime( 6038):    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)

E/AndroidRuntime( 6038):    at javafx.event.Event.fireEvent(Event.java:198)

E/AndroidRuntime( 6038):    at javafx.scene.Node.fireEvent(Node.java:8411)

E/AndroidRuntime( 6038):    at javafx.scene.control.Button.fire(Button.java:185)

E/AndroidRuntime( 6038):    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)

E/AndroidRuntime( 6038):    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)

E/AndroidRuntime( 6038):    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)

E/AndroidRuntime( 6038):    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)

E/AndroidRuntime( 6038):    at javafx.event.Event.fireEvent(Event.java:198)

E/AndroidRuntime( 6038):    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)

E/AndroidRuntime( 6038):    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)

E/AndroidRuntime( 6038):    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)

E/AndroidRuntime( 6038):    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)

E/AndroidRuntime( 6038):    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:352)

E/AndroidRuntime( 6038):    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:275)

E/AndroidRuntime( 6038):    at java.security.AccessController.doPrivileged(AccessController.java:52)

E/AndroidRuntime( 6038):    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$336(GlassViewEventHandler.java:388)

E/AndroidRuntime( 6038):    at com.sun.javafx.tk.quantum.GlassViewEventHandler.access$lambda$1(GlassViewEventHandler.java)

E/AndroidRuntime( 6038):    at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda$4.get(Unknown Source)

E/AndroidRuntime( 6038):    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)

E/AndroidRuntime( 6038):    at com.sun.javafx.tk.quantum.GlassViewE

Here is the build.gradle file and yes I am using the Verdana font. But the font is coming through OK from what I can see on my android device.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.0.7'
    }
}

apply plugin: 'org.javafxports.jfxmobile'

repositories {
    jcenter()
    maven {
        url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
    }
}

mainClassName = 'com.SundaeThePugApp.SundaeThePugFX'

dependencies {
    compile 'com.gluonhq:charm:2.1.0'

    androidRuntime 'com.gluonhq:charm-android:2.1.0'
    iosRuntime 'com.gluonhq:charm-ios:2.1.0'
    desktopRuntime 'com.gluonhq:charm-desktop:2.1.0'
}

jfxmobile {
    android {
        manifest = 'src/android/AndroidManifest.xml'
        androidSdk = 'C:/Users/Zermatt/AppData/Local/Android/android-sdk/'
    }
}

Edit

Found the cause for the Fatal Exception. It appears Android does not like the following block of code:

URL resource= getClass().getClassLoader().getResource("Music/ThemeMusic.mp3");
        themeSong = new MediaPlayer(new Media(resource.toString()));
        themeSong.setOnEndOfMedia(new Runnable()
        {   public void run()
            {   themeSong.seek(Duration.ZERO);
            }
        });
        themeSong.play();

And this block of code comes up with error message in Android:

googleMapEngine.load(getClass().getResource("/googleLondonMap.html").toExternalForm());

Webpage not available: The webpage at jar:file:/data/app/com.SundaeThePugApp - 1/base .apk!/googleLondonMap.html could not be loaded because:

net::ERR_UNKNOWN_URL_SCHEME

so what can I do now to rectify this?

EDIT AGAIN

Experimenting with Jose solution and building upon it, I found a way to get the JavaScript in the Html working although it ain't pretty. Essentially the code he supplied I put into a method (called createAndroidURLScheme) and returned a string. Here is the code and yes it works fine in JavaFX, but not in Androids webview again:

String url = createAndroidURLScheme("/googleTestMap.html");
//System.out.println(url);
googleMapEngine.loadContent(url, "text/html");
googleMapEngine.setJavaScriptEnabled(true);
                googleMapEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>()
                {   public void changed(final ObservableValue<? extends Worker.State> observableValue, final State oldState, final State newState)
                    {   if (newState == State.SUCCEEDED)
                        {   googleMapEngine.executeScript(
                                "var latlng = new google.maps.LatLng(59.438722, 24.745278);" +
                                "var myOptions = {" + 
                                "zoom: 15," +
                                "center: latlng," +
                                "mapTypeId: google.maps.MapTypeId.ROADMAP," +
                                "mapTypeControl: false," +
                                "navigationControl: false," +
                                "streetViewControl: false," +
                                "backgroundColor: '#666970'," +
                                "disableDefaultUI: true" +
                                "};" +
                                "" +
                                "var map = new google.maps.Map(document.getElementById('map_canvas'), myOptions);");
                        }
                    }
                });

Now through logcat I get this new Android Fatal Exception:

    I/System.out(  809): [JVDBG] Error firing event

W/System.err(  809): java.lang.ClassNotFoundException: com.sun.webkit.NativeWebView

W/System.err(  809):    at java.lang.Class.classForName(Native Method)

W/System.err(  809):    at java.lang.Class.forName(Class.java:309)

W/System.err(  809):    at com.oracle.dalvik.InternalWebView._fireLoadEvent(InternalWebView.java:347)

W/System.err(  809):    at com.oracle.dalvik.InternalWebView.fireLoadEvent(InternalWebView.java:140)

W/System.err(  809):    at com.oracle.dalvik.InternalWebView.access$400(InternalWebView.java:43)

W/System.err(  809):    at com.oracle.dalvik.InternalWebView$MyJavaScriptInterface.processHTML(InternalWebView.java:363)

W/System.err(  809):    at org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)

W/System.err(  809):    at org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:39)

W/System.err(  809):    at android.os.Handler.dispatchMessage(Handler.java:102)

W/System.err(  809):    at android.os.Looper.loop(Looper.java:145)

W/System.err(  809):    at android.os.HandlerThread.run(HandlerThread.java:61)

W/System.err(  809): Caused by: java.lang.ClassNotFoundException: Didn't find class "com.sun.webkit.NativeWebView" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]

W/System.err(  809):    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)

W/System.err(  809):    at java.lang.ClassLoader.loadClass(ClassLoader.java:511)

W/System.err(  809):    at java.lang.ClassLoader.loadClass(ClassLoader.java:469)

W/System.err(  809):    ... 11 more

W/System.err(  809):    Suppressed: java.lang.ClassNotFoundException: com.sun.webkit.NativeWebView

W/System.err(  809):        at java.lang.Class.classForName(Native Method)

W/System.err(  809):        at java.lang.BootClassLoader.findClass(ClassLoader.java:781)

W/System.err(  809):        at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)

W/System.err(  809):        at java.lang.ClassLoader.loadClass(ClassLoader.java:504)

W/System.err(  809):        ... 12 more

W/System.err(  809):    Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available

E/AndroidRuntime(  809): FATAL EXCEPTION: JavaFX Application Thread

E/AndroidRuntime(  809): Process: com.SundaeThePugApp, PID: 809

E/AndroidRuntime(  809): java.lang.UnsupportedOperationException: Not supported yet.

E/AndroidRuntime(  809):    at com.sun.webkit.WebPage.executeScript(WebPage.java:153)

E/AndroidRuntime(  809):    at javafx.scene.web.WebEngine.executeScript(WebEngine.java:860)

E/AndroidRuntime(  809):    at com.SundaeThePugApp.SundaeThePugController$1.changed(SundaeThePugController.java:202)

E/AndroidRuntime(  809):    at com.SundaeThePugApp.SundaeThePugController$1.changed(SundaeThePugController.java:200)

E/AndroidRuntime(  809):    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)

E/AndroidRuntime(  809):    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)

E/AndroidRuntime(  809):    at javafx.beans.property.ReadOnlyObjectWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyObjectWrapper.java:176)

E/AndroidRuntime(  809):    at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:142)

E/AndroidRuntime(  809):    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)

E/AndroidRuntime(  809):    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146)

E/AndroidRuntime(  809):    at javafx.scene.web.WebEngine$LoadWorker.updateState(WebEngine.java:1023)

E/AndroidRuntime(  809):    at javafx.scene.web.WebEngine$LoadWorker.dispatchLoadEvent(WebEngine.java:1134)

E/AndroidRuntime(  809):    at javafx.scene.web.WebEngine$LoadWorker.access$1000(WebEngine.java:1016)

E/AndroidRuntime(  809):    at javafx.scene.web.WebEngine$PageLoadListener.dispatchLoadEvent(WebEngine.java:1003)

E/AndroidRuntime(  809):    at com.sun.webkit.WebPage.fireLoadEvent(WebPage.java:251)

E/AndroidRuntime(  809):    at com.sun.webkit.NativeWebView$1.run(NativeWebView.java:100)

E/AndroidRuntime(  809):    at com.sun.javafx.application.PlatformImpl.lambda$null$155(PlatformImpl.java:295)

E/AndroidRuntime(  809):    at com.sun.javafx.application.PlatformImpl.access$lambda$18(PlatformImpl.java)

E/AndroidRuntime(  809):    at com.sun.javafx.application.PlatformImpl$$Lambda$19.run(Unknown Source)

E/AndroidRuntime(  809):    at java.security.AccessController.doPrivileged(AccessController.java:52)

E/AndroidRuntime(  809):    at com.sun.javafx.application.PlatformImpl.lambda$runLater$156(PlatformImpl.java:294)

E/AndroidRuntime(  809):    at com.sun.javafx.application.PlatformImpl.access$lambda$5(PlatformImpl.java)

E/AndroidRuntime(  809):    at com.sun.javafx.application.PlatformImpl$$Lambda$6.run(Unknown Source)

E/AndroidRuntime(  809):    at com.sun.glass.ui.monocle.RunnableProcessor.runLoop(RunnableProcessor.java:92)

E/AndroidRuntime(  809):    at com.sun.glass.ui.monocle.RunnableProcessor.run(RunnableProcessor.java:51)

E/AndroidRuntime(  809):    at java.lang.Thread.run(Thread.java:818)

V/ApplicationPolicy( 3468): isApplicationStateBlocked userId 0 pkgname com.SundaeThePugApp

V/ApplicationPolicy( 3468): isApplicationStateBlocked userId 0 pkgname com.SundaeThePugApp

V/ApplicationPolicy( 3468): isApplicationStateBlocked userId 0 pkgname com.sec.android.app.launcher

What can I do now to alleviate this and keep Androids Webview happy?

1
With your device connected, can you run on command line adb logcat -v threadtime, and look for an exception? Post the relevant partJosé Pereda
Hi Jose, Thank you for trying to help me. I found the adb.exe file under: c:\Users\Zermatt\AppData\Local\Android\android-sdk\platform-tools, so I connected my android device via usb cable to the computer then I ran the command you suggested in the command prompt screen at the above folder address location. When I ran the command all I got is: daemon not running. starting it now on port 5037, next line it said daemon started successfully, next line - waiting for device - now after 15 minutes nothing more seems to be happening. Should it take a long time? Sorry not familiar with this tool!zermatt
Sorry Jose, New to this stuff after reading further. I realised I had to turn developer options on and activate usb debugging. I have now created a txt file using the following command: adb logcat -d > logcat.txt. As the verbose was so long I could not see all of it via the command prompt window.zermatt
Now that we have a trace of the exception, could you share the full output (via gist or pastebin) ?José Pereda
I've looked through by doing a word search on the text file and there is no line saying "Caused By" to give an indication for the reason why I receive an InvocationTargetException. This exception never occurs at any other stage of testing?zermatt

1 Answers

1
votes

Based on the latest information provided, you have two known issues:

Media

If you have a look at this, Media is not supported yet, so you'll need to take it out.

Url scheme error

If you have a look at the URL showed by the error message, jar:file:/data/.../base.apk!/googleLondonMap.html, the net::ERR_UNKNOWN_URL_SCHEME means that jar:file is not a known protocol on Android.

The reason for this is that loading an html file from a jar file is not supported by the android SDK.

Anyhow, the resource is accessible, so as a workaround, you could just load the html file into a string and provide that to the WebEngine using the method loadContent instead.

Something like this should work:

StringWriter writer = new StringWriter();
BufferedReader reader = null;
try {
    reader = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/googleLondonMap.html")));
    String line = null;
    while ((line = reader.readLine()) != null) {
        writer.append(line);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
engine.loadContent(writer.toString());