绑定对象在FXML UI控制器的initialize()方法外部不起作用

时间:2019-04-27 09:28:30

标签: java javafx

核心问题:如果绑定属性在声明它们的Binding方法之外更新,则不会触发initialize()对象失效侦听器。

采用在JavaFX UI控制器类中声明的initialize()方法:

@FXML
private void initialize() {
    final StringProperty stringProperty = textField.textProperty();
    stringProperty.addListener(
            (observable, oldValue, newValue) -> System.out.println("stringProperty value: " + newValue));
    Bindings.createStringBinding(() -> "PREFIX - " + stringProperty.getValue(), stringProperty).addListener(
            (observable, oldValue, newValue) -> System.out.println("StringBinding value: " + newValue));

    // Editing stringProperty value inside initialize() method
    stringProperty.setValue("u");
    stringProperty.setValue("ua");
    stringProperty.setValue("ua");
    stringProperty.setValue("uaa");
}

如您所见,我声明了一个StringBinding,它依赖于一个名为stringProperty的TextField的text属性,而一个ChangeListener则请求计算{{1} }无效。

如果我在初始化方法中编辑StringBinding值,则stringPropertystringProperty的更改侦听器都会被触发,而如果我从UI编辑StringBinding的值,则仅{ {1}}更改侦听器已触发。

有人可以解释一下为什么会这样吗?

1 个答案:

答案 0 :(得分:0)

由于不存在对StringBinding创建的Bindings.createStringBinding的强烈引用,因此最终将被垃圾回收。一旦发生这种情况,您添加的侦听器将随之被垃圾收集。

  

我认为这不是重点,因为Binding对象通过Observable监听其依赖项(InvalidationListener对象),并且Obsevable.addListener(InvalidationListener)文档指出“ Observable存储了对侦听器的强大引用,这将防止侦听器被垃圾回收,并可能导致内存泄漏。”

这是真的,但是请看一下XXXBinding类使用的侦听器实现:

/*
 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.binding;

import java.lang.ref.WeakReference;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.WeakListener;
import javafx.beans.binding.Binding;

public class BindingHelperObserver implements InvalidationListener, WeakListener {

    private final WeakReference<Binding<?>> ref;

    public BindingHelperObserver(Binding<?> binding) {
        if (binding == null) {
            throw new NullPointerException("Binding has to be specified.");
        }
        ref = new WeakReference<Binding<?>>(binding);
    }

    @Override
    public void invalidated(Observable observable) {
        final Binding<?> binding = ref.get();
        if (binding == null) {
            observable.removeListener(this);
        } else {
            binding.invalidate();
        }
    }

    @Override
    public boolean wasGarbageCollected() {
        return ref.get() == null;
    }
}

如您所见,添加到依赖项(即Observable)的侦听器实例是WeakListener,并且仅维护对Binding的弱引用。即使未正确处理Binding,也可以将其Binding进行垃圾回收。这样做是为了防止在Observable超出范围而Observable不在范围内的情况下发生内存泄漏。

换句话说,InvalidationListener维持对InvalidationListener的强引用,而Binding维持对Property的弱引用。


这种行为记录在“远程”位置,包括Property#bind(ObservableValue)

  

为此Binding创建一个单向绑定。

     

请注意,JavaFX具有通过弱侦听器实现的所有绑定调用。这意味着可以对垃圾属性进行垃圾收集,并阻止其更新。

还有Binding#dispose()

  

WeakInvalidationListener上将不再使用它的信号,并且可以删除任何引用。调用此方法通常会导致绑定停止,以通过注销其侦听器来观察其依赖性。该实现是可选的。

     

我们的实现中的所有绑定都使用WeakReferences的实例,这意味着通常不需要处理绑定。但是,如果您打算在不支持Binding的环境中使用应用程序,则必须处置未使用的WeakInvalidationListener以避免内存泄漏。

注意:该实现似乎未使用{{1}},但效果是相同的。