JavaFXPorts:Android上文本节点的渲染性能
我已经开始尝试获取有关此问题的一些信息(请参阅),但这次,我创建了一个示例应用程序来展示我的问题(希望是以比上次更好的方式) 在获取startet之前,以下是指向应用程序存储库的链接: 让我们从代码开始。这是我的毕业档案JavaFXPorts:Android上文本节点的渲染性能,android,javafx,javafxports,Android,Javafx,Javafxports,我已经开始尝试获取有关此问题的一些信息(请参阅),但这次,我创建了一个示例应用程序来展示我的问题(希望是以比上次更好的方式) 在获取startet之前,以下是指向应用程序存储库的链接: 让我们从代码开始。这是我的毕业档案 buildscript { repositories { jcenter() } dependencies { classpath 'org.javafxports:jfxmobile-plugin:1.2.0'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.javafxports:jfxmobile-plugin:1.2.0'
}
}
apply plugin: 'org.javafxports.jfxmobile'
repositories {
jcenter()
maven {
url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
}
}
mainClassName = 'eu.dzim.example.Main'
dependencies {
compile 'com.gluonhq:charm:4.2.0'
compile 'org.controlsfx:controlsfx:8.40.12'
compile 'de.jensd:fontawesomefx-commons:8.13'
compile 'de.jensd:fontawesomefx-fontawesome:4.7.0'
compile 'de.jensd:fontawesomefx-materialdesignfont:1.7.22'
compile 'com.fasterxml.jackson.core:jackson-databind:2.8.4'
compileNoRetrolambda 'com.airhacks:afterburner.mfx:1.6.2'
}
jfxmobile {
downConfig {
version = '3.1.0'
plugins 'display', 'lifecycle', 'statusbar', 'storage', 'settings'
}
android {
manifest = 'src/android/AndroidManifest.xml'
compileSdkVersion = 22
minSdkVersion = 19
targetSdkVersion = 22
dexOptions {
javaMaxHeapSize '2g'
}
packagingOptions {
pickFirst 'META-INF/LICENSE'
pickFirst 'META-INF/NOTICE'
pickFirst 'license/LICENSE.txt'
}
}
}
我使用Gluon插件方法创建了一个具有单一视图的应用程序。但是因为非常。。。特别的。。。设计要求我需要摒弃材料设计灵感的UI,Gluon提供。不幸的是。因此,我需要一个简单的javafx.application.application
类,并设置图标、CSS和大小
有一个应用程序模型的小单例实例。为什么?不幸的是,我需要提供一种技术来改变整个应用程序中的文本大小。该模型保存对当前大小的引用,并使用一个Utils类为指定的节点更改它(这或多或少是准确的,也或多或少是缓慢的)
最关键的部分是,有一个组件可以浏览页面,每个页面都可以根据需要从资源或文件系统(任何可用的)加载。刷卡就像一个魔咒,但加载和渲染需要相对较长的时间(取决于设备!)从3到7秒(从系统加载文件并通过fxmloader
处理它需要约2秒时间;其余时间UI冻结(请参阅进度指示器)直到显示为止)!即使是演示应用程序也要花那么多时间。我没做什么好想象的,下面是演示应用程序的加载机制:
package eu.dzim.example.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import eu.dzim.example.model.ApplicationModel;
import eu.dzim.example.util.DualAcceptor;
import eu.dzim.example.util.Utils;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
public class RootController {
private static final Logger LOG = Logger.getLogger(RootController.class.getName());
@FXML private StackPane content;
@FXML private ScrollPane scrollContent;
@FXML private VBox contentBox;
@FXML private ProgressIndicator progress;
@FXML private Button showContent;
@FXML private Button showTextSize;
private boolean contentLoaded = false;;
private List<CollapsibleItemPane> panes = new ArrayList<>();
private List<Node> resizableNodes = new ArrayList<>();
private ChangeListener<Number> onContentTextSizeChange = this::handleContentTextSizeChanged;
private DualAcceptor<CollapsibleItemButton, Boolean> collapsibleItemAction = this::handleCollapsibleItemAction;
private ExecutorService executor = Executors.newSingleThreadExecutor();
@FXML
public void initialize() {
ApplicationModel.getInstance().textSizeProperty().addListener(onContentTextSizeChange);
progress.managedProperty().bind(progress.visibleProperty());
showContent.setOnAction(e -> showContent());
showTextSize.setOnAction(e -> switchThroughTextSize());
}
private void showContent() {
if (contentLoaded)
return;
progress.setVisible(true);
Task<Pane> task = new Task<Pane>() {
@Override
protected Pane call() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/ObjectRestauration.fxml"));
return loader.load();
}
};
task.setOnSucceeded(value -> {
Pane result = (Pane) value.getSource().getValue();
if (result == null)
return;
for (CollapsibleItemPane pane : panes) {
pane.setOnActionAcceptor(null);
}
contentBox.getChildren().add(result);
panes = getCollapsibleItemPanesFromContent();
for (CollapsibleItemPane pane : panes) {
pane.setOnActionAcceptor(collapsibleItemAction);
}
resizableNodes.clear();
resizableNodes.addAll(Utils.getAllResizableNodes(contentBox));
handleContentTextSizeChanged(ApplicationModel.getInstance().textSizeProperty(), null, ApplicationModel.getInstance().getTextSize());
progress.setVisible(false);
contentLoaded = true;
executor.shutdownNow();
});
task.setOnFailed(value -> {
Throwable t = value.getSource().getException();
LOG.log(Level.SEVERE, t.getMessage(), t);
progress.setVisible(false);
});
executor.submit(task);
}
private void switchThroughTextSize() {
switch (ApplicationModel.getInstance().getTextSize()) {
case Utils.TEXT_SIZE_SMALL:
ApplicationModel.getInstance().setTextSize(Utils.TEXT_SIZE_DEFAULT);
case Utils.TEXT_SIZE_DEFAULT:
ApplicationModel.getInstance().setTextSize(Utils.TEXT_SIZE_LARGE);
case Utils.TEXT_SIZE_LARGE:
ApplicationModel.getInstance().setTextSize(Utils.TEXT_SIZE_SMALL);
default:
ApplicationModel.getInstance().setTextSize(Utils.TEXT_SIZE_DEFAULT);
}
}
private List<CollapsibleItemPane> getCollapsibleItemPanesFromContent() {
List<CollapsibleItemPane> panes = new ArrayList<>();
// only collect first OR second tier panes
for (Node node : content.getChildren()) {
if (node instanceof CollapsibleItemPane)
panes.add((CollapsibleItemPane) node);
else if (node instanceof Pane) {
for (Node child : ((Pane) node).getChildren()) {
if (child instanceof CollapsibleItemPane)
panes.add((CollapsibleItemPane) child);
}
}
}
return panes;
}
private void handleContentTextSizeChanged(ObservableValue<? extends Number> obs, Number o, Number n) {
new Thread(() -> {
Utils.handleTextSizeChange(ApplicationModel.getInstance().getTextSize(), null, resizableNodes.toArray(new Node[0]));
}).start();
}
private void handleCollapsibleItemAction(CollapsibleItemButton source, Boolean visible) {
if (!visible)
return;
Node parent = source.getParent();
for (CollapsibleItemPane pane : panes) {
if (parent == pane)
continue;
pane.getCollapsibleButton().hideContent();
}
}
}
也许他们和这事有关
当我按下加载按钮时,会出现以下日志
01-13 14:05:35.864: I/System.out(23324): don't add points, primary = -1
01-13 14:05:36.137: W/linker(23324): /data/app/eu.dzim.example-1/lib/arm/libjavafx_iio.so: is missing DT_SONAME will use basename as a replacement: "libjavafx_iio.so"
01-13 14:05:36.273: I/art(23324): Do full code cache collection, code=95KB, data=125KB
01-13 14:05:36.275: I/art(23324): After code cache collection, code=65KB, data=65KB
01-13 14:05:37.055: I/art(23324): Do partial code cache collection, code=96KB, data=124KB
01-13 14:05:37.056: I/art(23324): After code cache collection, code=95KB, data=124KB
01-13 14:05:37.056: I/art(23324): Increasing code cache capacity to 512KB
01-13 14:05:41.627: I/System.out(23324): ES2ResourceFactory: Prism - createStockShader: Texture_Color.frag
您可以看到手机显示UI所需的时间(6秒)。我可以在Nexus 6上重现您的发现 虽然我没有一个神奇的解决方案,这可能不是一个正确的答案,但我会尝试添加一些对您有价值的信息 首先,运行应用程序加载fxml文件同样需要6秒钟 因此,我运行了Android设备监控应用程序(Android sdk/tools/Monitor),因为它可以帮助您在应用程序上运行方法评测(运行监控程序,打开应用程序,选择设备选项卡下的流程,然后单击
开始方法评测按钮
),几秒钟后,再次单击停止
然后打开DDMS透视图,查看评测结果:
图中显示,大部分时间用于javafx应用程序线程内的文本布局(com.sun.javafx.text.PrismTextLayout.layout
)
为了测量TextFlow
节点的影响,我创建了一个新项目,并将TextFlow
节点从objectrestaration.fxml
文件添加到滚动窗格中的VBox视图中
<View fx:id="secondary" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.gluonhq.textflow.views.SecondaryPresenter">
<center>
<ScrollPane BorderPane.alignment="CENTER">
<content>
<VBox prefWidth="350">
<TextFlow lineSpacing="3">
<Text text="Die Restaurierung der Klosterkirche St. Martin ist dringend notwendig: Nur so können wir das Kulturgut nationaler Bedeutung und seinen hohen kunsthistorischen Wert erhalten.">
<font>
<Font size="12"/>
</font>
<userData>
<Integer fx:value="12"/>
</userData>
</Text>
<Text text=" "/>
<ImageView preserveRatio="true" fitHeight="300">
<image>
<Image url="@img/kloster_werbung.jpg"/>
</image>
</ImageView>
...
...
运行此项目时,加载包含所有内容的视图需要2-3秒,我看到com.sun.javafx.font.freetype.OSFreetype
或com.sun.javafx.iio.jpeg.JPEGImageLoader
消耗的时间
这是不可避免的,因为您无论如何都需要加载内容。但是您可以看到,您的自定义组件需要花费大量时间来加载和管理该内容:3秒用于内容,3秒用于自定义组件
我建议将FXML内容拆分为更小的内容,因为您希望使用背景线程、缓存技术以及其他解决方案在不同的可折叠窗格中显示它们
此外,删除每个文本节点的字体和用户数据分配可能会有所帮助
使用FXML和反射也有一些好处。因此,您可能也可以找到一种以不同方式加载内容的方法,或者也可以尝试使用FXML编译器选项,如下面所示。首先:非常感谢您为此付出的努力。我不想从何处降低复杂性-我想首先是mak我需要使用自定义组件纯代码。不幸的是,我需要依赖字体/用户数据(应用程序需要在运行时切换文本大小)。我需要通过FXML创建UI,直到我弄明白如何创建纯代码(我不是很擅长,因为我从早期的JavaFX2.1开始就依赖FXMLs)。我会在我的存储库中测试它,然后-我们会给出反馈。如果我能确定您的时间,我会接受您的回答,好吗?;-)非常感谢您的输入!这确实是一个很大的帮助。我已经用我所做的更改更新了存储库,以加快一切。我所做的是:1)小型定制组件没有FXML,2)内容的占位符节点(如果第一次请求,则覆盖)。我留下了文本大小调整(字体和用户数据属性),因为这是一个功能,我不能删除。时间缩短到大约2-3秒(和您一样),并且在第一次占位符更新时不会有太多延迟。甚至文本大小调整也带来了很多变化。也快多了!再次:非常感谢你,何塞!很高兴知道。正如你所看到的,总有改进的余地。我们在桌面上理所当然的事情可能需要在移动设备上进行几次迭代才能获得预期的性能。你完全正确。由于这一点,我已经改进了一些部件,但我显然没有考虑太多。干杯,Danielt这可能与这个问题有关:如果您能够构建自己的JRE,您可以尝试缓存
01-13 14:05:35.864: I/System.out(23324): don't add points, primary = -1
01-13 14:05:36.137: W/linker(23324): /data/app/eu.dzim.example-1/lib/arm/libjavafx_iio.so: is missing DT_SONAME will use basename as a replacement: "libjavafx_iio.so"
01-13 14:05:36.273: I/art(23324): Do full code cache collection, code=95KB, data=125KB
01-13 14:05:36.275: I/art(23324): After code cache collection, code=65KB, data=65KB
01-13 14:05:37.055: I/art(23324): Do partial code cache collection, code=96KB, data=124KB
01-13 14:05:37.056: I/art(23324): After code cache collection, code=95KB, data=124KB
01-13 14:05:37.056: I/art(23324): Increasing code cache capacity to 512KB
01-13 14:05:41.627: I/System.out(23324): ES2ResourceFactory: Prism - createStockShader: Texture_Color.frag
<View fx:id="secondary" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.gluonhq.textflow.views.SecondaryPresenter">
<center>
<ScrollPane BorderPane.alignment="CENTER">
<content>
<VBox prefWidth="350">
<TextFlow lineSpacing="3">
<Text text="Die Restaurierung der Klosterkirche St. Martin ist dringend notwendig: Nur so können wir das Kulturgut nationaler Bedeutung und seinen hohen kunsthistorischen Wert erhalten.">
<font>
<Font size="12"/>
</font>
<userData>
<Integer fx:value="12"/>
</userData>
</Text>
<Text text=" "/>
<ImageView preserveRatio="true" fitHeight="300">
<image>
<Image url="@img/kloster_werbung.jpg"/>
</image>
</ImageView>
...