3

When a view is destroyed (stage closed, another view taken its place, etc), are there any methods that will get called on the controller? That is, is there a counterpart to:

@FXML
private void initialize()

Update: The reason why I'm doing this is that my application is receiving messages from PubNub and then those messages need to be relayed to these controllers so they can chose whether to act on them or not (whether they do depends on internal controller information, such as the size of the view).

My plan was to have these controllers register themselves in a global list and when destroyed, unregister themselves.

Pablo Fernandez
  • 279,434
  • 135
  • 377
  • 622
  • No, there are no such methods. – James_D Dec 20 '17 at 20:00
  • I don't think so... – zlakad Dec 20 '17 at 20:00
  • 1
    The real question would be "why I should use the controller of some UI part when that part is not available any more?". @Pablo -can you give some answer to this question? – zlakad Dec 20 '17 at 20:10
  • 1
    It's not really clear that there is a good definition of a view being "destroyed", which could be used to trigger such a method anyway. This sounds like an [X-Y question](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – James_D Dec 20 '17 at 20:15
  • If I may add, this is a question where only two parts of MVC approach are the issues (VC parts). However, the full MVC architecture must be applied. Am I wrong? – zlakad Dec 20 '17 at 20:38
  • Yes, this could be an X-Y question. My apologies. I updated the question with more details about what I wanted this. – Pablo Fernandez Dec 20 '17 at 21:23
  • 1
    The use case makes sense. However, ultimately, the FX toolkit is not managing the life cycle of the controllers in any way, other than creating them for you under some circumstances. So there's no real way for the toolkit to know when you are no longer using a controller; first if a window is closed, for example, the toolkit really has no way of knowing if the UI displayed in it was loaded from FXML, and even if so, there's no way to determine if you might want to display that UI again. So you really just have to manage this yourself. – James_D Dec 20 '17 at 21:32
  • Maybe there's a solution if you just store weak references to the controllers? – James_D Dec 20 '17 at 21:37

1 Answers1

2

Most of the comments have told you there is no such equivalent feature in JavaFX. James_D has also mentioned using WeakReference, which I believe is a good solution for your problem.

public final class PubNubManager {
    private final List<WeakReference<PubNubListener>> listeners = new ArrayList<>();

    // Other standard singleton stuff

    public void informListeners(PubNubData data) {
        listeners.stream().forEach(listenerRef -> {
            if (listenerRef.get() != null)
                listenerRef.get().receiveData(data);
        });

        // You can remove weakreference that are no longer valid, or stop getting for data when there are no more listener.
    }

    public void registerListener(PubNubListener listener) {
        if (listener != null)
            listeners.add(new WeakReference<>(listener));
    }

    // You can make one to unregister too if you want.
}

public interface PubNubListener {
    void receiveData(PubNubData data);
}

public class ControllerA implements PubNubListener {
    @Override
    public void receiveData(PubNubData data) {
        // What you need to do
    }
}
Jai
  • 8,165
  • 2
  • 21
  • 52
  • Nice demo. A couple of comments. 1. This relies on the view retaining a reference to the controller. This happens naturally if the controller has an event listener of some kind registered with one or more UI elements defined in the FXML file; but if the controller exists solely to update the UI with notifications, it could go out of scope prematurely and notifications would stop. 2. Conversely, there is no guarantee as to when, or even if, the weak reference becomes invalid after the controller is out of scope. So this works to prevent memory leaks, but notifications may continue indefinitely. – James_D Dec 21 '17 at 05:52
  • @James_D true. It seems like this solution works well if it's in an ideal world where the View and Controller are garbage collected together, and as soon as they are out of scope. I guess the safest way is still to get a reference of the `Stage` in controller, and listen for window hidden event, then manually unregister from the manager. – Jai Dec 21 '17 at 06:01
  • I think it depends on the motivation for unregistering the listener. If it's solely to make sure that no unnecessary references are kept to controllers that are not otherwise used (i.e. it's to prevent memory leaks), this approach is sufficient. If the OP is concerned about executing code (notifications) that have no effect from a CPU-performance perspective, then this won't do what's needed. (I find it hard to envision the latter being a real problem in practice, though.) – James_D Dec 21 '17 at 06:05