【问题标题】:Converting JavaFX application into Android APK - issues with HTML file loading Google Maps将 JavaFX 应用程序转换为 Android APK - 加载 Google 地图的 HTML 文件问题
【发布时间】:2016-04-06 12:19:49
【问题描述】:

我正在尝试将一个用 JavaFX 编写的应用程序移植到 Android 上,并且在大多数情况下,我的尝试都取得了成功。在使用 Netbeans IDE 和 Gradle 打包 apk 文件后,应用程序将在 Android 上运行。

但我遇到的问题是,当尝试通过 JavaFX Webview 组件访问谷歌地图时,应用程序会崩溃。我在 JavaFX 中使用的是以下内容:

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

并且该html文件位于Gradle根文件夹的Resources文件夹下。

当我通过右键单击 Gradle 根节点并选择 Tasks --> Run --> Run 在 Netbeans 中测试应用程序时。我的应用程序运行完美,包括使用 html 文件和 JavaFX Webview 访问谷歌地图。

使用Tasks --> Android --> Android 创建apk文件也是成功的。但是,一旦我在 android 设备上安装了 apk 文件并访问了我的应用程序的谷歌地图部分,它就会让我退出应用程序 说“不幸的是已经停止了”。有关如何解决此问题的任何线索?

查看使用 adb 命令创建的日志文件,我看到了以下输出:

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

这是 build.gradle 文件,是的,我使用的是 Verdana 字体。但是从我在我的安卓设备上看到的字体可以通过。

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/'
    }
}

编辑

找到致命异常的原因。看来 Android 不喜欢以下代码块:

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();

而这段代码在 Android 中会出现错误消息:

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

网页不可用: jar:file:/data/app/com.SundaeThePugApp - 1/base .apk!/googleLondonMap.html 处的网页 无法加载,因为:

net::ERR_UNKNOWN_URL_SCHEME

那么我现在可以做些什么来纠正这个问题?

再次编辑

尝试使用 Jose 解决方案并在此基础上进行构建,我找到了一种让 HTML 中的 JavaScript 工作的方法,尽管它并不漂亮。本质上,我将他提供的代码放入一个方法(称为 createAndroidURLScheme)并返回一个字符串。这是代码,是的,它在 JavaFX 中运行良好,但在 Android 的 webview 中又不行:

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);");
                        }
                    }
                });

现在通过 logcat 我得到了这个新的 Android 致命异常:

    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

我现在可以做些什么来缓解这种情况并让 Android 的 Webview 满意?

【问题讨论】:

  • 连接设备后,您可以在命令行adb logcat -v threadtime 上运行并查找异常吗?贴出相关部分
  • 您好 Jose,感谢您帮助我。我在以下位置找到了 adb.exe 文件:c:\Users\Zermatt\AppData\Local\Android\android-sdk\platform-tools,所以我通过 USB 电缆将我的 android 设备连接到计算机,然后我运行了你建议的命令上述文件夹地址位置的命令提示符屏幕。当我运行命令时,我得到的只是:守护进程没有运行。现在在端口 5037 上启动它,下一行表示守护程序已成功启动,下一行 - 等待设备 - 现在 15 分钟后似乎没有任何事情发生。应该需要很长时间吗?抱歉不熟悉这个工具!
  • 对不起,何塞,在进一步阅读后对这些东西不熟悉。我意识到我必须打开开发人员选项并激活 USB 调试。我现在使用以下命令创建了一个 txt 文件:adb logcat -d > logcat.txt。由于冗长的内容太长了,我无法通过命令提示符窗口看到所有内容。
  • 现在我们已经对异常进行了跟踪,您能否分享完整的输出(通过 gist 或 pastebin)?
  • 我已经通过对文本文件进行单词搜索进行了查看,并且没有一行说“Caused By”来说明我收到 InvocationTargetException 的原因。在测试的任何其他阶段都不会出现此异常?

标签: android javafx apk javafxports gluon-mobile


【解决方案1】:

根据提供的最新信息,您有两个已知问题:

媒体

如果您查看this,媒体尚不支持,因此您需要将其取出。

网址方案错误

如果您查看错误消息jar:file:/data/.../base.apk!/googleLondonMap.html 中显示的URL,则net::ERR_UNKNOWN_URL_SCHEME 表示jar:file 不是Android 上的已知协议。

原因是android SDK不支持从jar文件加载html文件。

无论如何,资源是可访问的,因此作为一种解决方法,您可以将 html 文件加载到字符串中,然后使用方法 loadContent 将其提供给 WebEngine

这样的事情应该可以工作:

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());

【讨论】:

  • 首先,非常感谢您对 Jose 的帮助。我喜欢上面关于从 jar 文件加载 html 文件的解释,Android SDK 不支持。我以前不知道这个限制。由于这个限制,我不知道我的移植现在是否可以进行?虽然它摆脱了 url 方案错误。我觉得我稍微靠近了一点,但现在我得到的是一个空白的白色屏幕,现在应该是地图所在的位置,并且它的定位不符合 Scene Builder 中设计的 GUI。作为我的第一个 JavaFX 到 Android 端口,我仍然从这个过程中学到了很多东西。
  • 尝试加载一个更简单的 html 页面,检查它是否已完全加载(可能的最小内容)。您可以打印到控制台 (System.out.println()),然后使用 adb logcat 检查您是否到达那里。如果可行,请开始添加内容,并找出可能存在的问题。还要检查它是否首先在桌面上运行。
  • 当我尝试使用 Netbeans 在桌面上运行它时,该方法不起作用 - 任务 --> 运行 --> 运行。应用程序出现了,但根本没有显示谷歌地图。我得到的只是一个没有错误的灰色背景。直到我尝试在 html 文件中执行其他函数之一并得到以下信息: 原因:netscape.javascript.JSException: TypeError: undefined is not a function
  • 如果它不能在桌面上运行,它也不能在移动设备上运行。所以尝试先在桌面上修复它,并检查它为什么不加载 html 内容。
  • OK Jose,我认为我使用这种方法的问题是,是的,它会拾取 html 文档的 标记之间写入的内容,例如你好世界。但是,当调用 javascript onload 函数时,它不能使用 engine.loadContent(writer.toString())。使用 System.out.println() 我可以看到文件的全部内容都在那里,这令人放心。我的 html 文档与 FXExperience 网站中的地图示例非常相似:link
猜你喜欢
  • 2022-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-19
  • 1970-01-01
  • 1970-01-01
  • 2011-03-14
相关资源
最近更新 更多