Platform#runLater Question

Daniel Zwolenski zonski at gmail.com
Tue Apr 2 13:57:53 PDT 2013


Inline responses.

On Tue, Apr 2, 2013 at 12:17 PM, Mark Fortner <phidias51 at gmail.com> wrote:

> Here's the genesis of the original question, this may give you some ideas
> for the future.
>
> I have an app where initially I had a lot of point-to-point event
> listening. I created a generic event helper -- basically a mixin with an
> executor to let me easily add event support to any class.  I then realized
> that what I really wanted was to centralize all event management in a
> single place within my app.  The main reason for this was that it becomes
> very easy to lose track of who's listening to what when that code is
> scattered throughout a project.  So, I created an event manager, added it
> to my application context so that I could easily access it from anywhere in
> my code, and added the event helpers to the event manager.
>

I used to use this architecture a lot a while back but found it messy much
as you describe. One of the (many) problems is error handling - if
something goes wrong it usually goes wrong in a lot of places (like network
connection lost) so you suddenly have 20 independent elements all wanting
to tell the user that they failed and now you have to put logic in around
not showing 20 error dialogs to the user.

I ended up settling on an architecture that is much more inline with the
way webapps work, i.e. page based, where there is basically a presenter for
the page. In this model you really only handle one user request at a time
(in general) and one boss (the presenter) handling that. When the user
clicks a button, the presenter handles that action and blocks the UI (or
the relevant parts of it). When the action is finished, the presenter
updates the relevant parts of the UI, which generally is a case of updating
the current 'page' or redirecting to a new 'page' (i.e. new presenter).

This simplifies the programming model a lot and the EventBus just quietly
disappeared from my apps. The user experience was in general actually
better, as most people prefer the focused, simplified, single-action style
of web pages - less is more. JFX Flow (http://www.zenjava.com/jfx-flow/) is
an early incarnation of this approach, but my later architectures are a lot
cleaner (I just don't have the time/energy to open source this when basic
fundamentals like deployment arent usable and need work first).

This is all subjective and a matter of personal style, but for me this has
worked really well and I now use things like the 'need' for an EventBus as
a bit of an alarm bell for me that I am over complicating the UI for the
user and probably should step back and relook at the User experience.
Again, subjective, and there are exceptions to every rule, but I haven't
hit an app yet that didn't work better with this page based model (just
look at web, iPhone, iPad, Android apps).

Having said all that sometimes it's nice to have multiple windows onto your
data (usually a changing main page area which has a more static side bar or
tool window). Instead of using an EventBus for this I have, in some cases,
introduced a 'shared model'. The Presenter updates the model, which fires
an event, which other components react to. It's sort of a hidden event bus,
but putting the focus on 'data was updated' instead of 'action should be
carried out' really simplifies a lot of things for me (e.g. error handling
is needed in the doAction based evets but not in the dataUpdated based
events).


> Ultimately, the use cases that I'd like to cover cleanly are:
>
> MyContext.fireEvent(event) // lets you fire an event, and notify all
> relevant listeners
> MyContext.addListener(eventHandler, eventType...) // let a listener listen
> to a variety of event types
> MyContext.removeListener(event, eventType...) // remove the listener from
> the event types
>

As Richard says, ye olde event bus. If you want this it is pretty simple to
implement and using Spring's Annotation based autowiring very trivial to
get access to. Give me a shout if you need any help on putting one
together.

There have been calls to have an EventBus in JFX but I'd vote 'no' on this
one as there are as many flavours of EventBus as there are developers (e.g
a JFX based one could not take advantage of Spring). Probably an open
source based library of a simple EventBus would be a good idea to pre-empt
it becoming part of the JFX core libraries and locking us into something.
Maybe if I have some free time when I get home, or if someone else wants to
do it, I'd be happy to input.



> I haven't yet figured out a clean way of removing listeners. It would be
> nice if components had a life cycle with a "destructor" to let them do this
> kind of cleanup.  I don't think *finalize()* is the appropriate place to
> take care of things like that.  Perhaps by making *WeakListeners*, this
> problem might go away on it's own and no specific "removeListener" method
> is necessary.
>

Definitely a challenge in the atomic, floating around small window style UI
of tradtional desktops as everything has its own life cycle and its hard to
know whats going on where. Definitely not a fan of finalize for this (or
much of anything) and I'm always a little distrusting of WeakListeners but
they potentially may work here.

The page based solution side steps this as you pretty much swap in one Page
Presenter for another and so have a very clear load/die moments (Page
Presenters can be aggregates of other re-usable presenters and the event
just perculates down the hierarchy). You decide in your presenter what you
should ditch and what you should keep (and it may be a lot more than just
event listening, such as ditching cached data, stopping video capture,
checking if a save is needed, etc).


>
> I'm also planning on making the event helpers Spring-configurable so that
> it's easy to tune the performance of specific types of events.  I have some
> events that trigger immediate responses in the GUI, and others that happen
> in the background and only require the GUI to update when they complete
> (either onSuccess, or onFailure).  In order to make the GUI performance
> more tuneable, I'll set the executor's thread pool size in the Spring
> config.
>
> When I tried unit testing some of this, the threading got in the way of
> JUnit, and I haven't figured out a good alternative for this yet.  A
> JUnitExecutor might be a useful addition.
>

I ended up creating my own Task framework and not using Richard's directly
so it was JFX agnostic and then could be united tested. Then I wrap my own
framework in Richard's Tasks that can call my framework as appropriate. Way
too much work and code, especially since some of the stuff I needed was
hidden or final in Richard's stuff (hence my previous comment about a
framework that was not tied into JFX threading), but it did work in the
end.


>
> The other thing I'd like to be able to do, is inject a component capable
> of monitoring either a single event helper, or the event manager in
> general.  I have some events that are cached, and only persisted to the
> database when the user decides that he's finished making adjustments.  If I
> attempted to persist each small change the user makes, there would be too
> much chatter going on and performance would suffer.
>
>
In the page based approach, all edits, etc are related to that page
lifecycle and can be queued/cached by that page's presenter until the page
is either saved or ditched. As a bonus you can easily add the old 'are you
sure you want to leave this page?' prompt, etc.


>
> I'd like to simply attach a component that would monitor all tasks and
> give the user feedback when things have updated properly.  If there are any
> problems, the progress bars (one per task) would turn red, and the user
> could click on a specific bar to get detailed error information.
>

If you want this model it is also pretty simple to implement. Just have a
TaskManager, similar to the EventBus, that Tasks register themselves with.
Use Spring's Autowiring to easily pass this task manager around your
presenters (or whatever starts your tasks). Then have your toolbar (or
whatever) monitor the TaskManager (obersvable list of active tasks) and
update the UI accordingly.

In the page based approach, the Presenter does this job really. Since it
has more fine grained information about the task being executed it can
handle it much better. e.g. save failed, then keep the current page and
show a popup dialog allowing a retry; but if a background refresh of some
data failed to load (e.g. scrolling over a map), maybe you show a
placeholder image while you retry to load that bit and only give up if it
fails after a certain number of attempts, etc, etc.

Basically Presenters are totally aware of everything happening in their
context and for me provide a really nice 'controller' for everything that
is going on, on that page, resulting in a better UI exeperience.


>
> In cases where the events are not cached, I'm planning on a creating a
> simple generic Task that shows success or failure dialogs when the task
> completes.  If I take Anthony's suggestion correctly, the *call* method
> is the equivalent of Dan's *doTaskStuff*, and the *onSuccess* or *
> onFailure* is the equivalent of *doThisStuffAfter. *  From the docs, it
> sounds like Services are (aside from being confusingly named) reusable
> Tasks.
>

I've nearly always ended up falling back onto Tasks and very rarely use
Services. Generally I find there's little to reuse - the Presenter is
basically the Task 'factory' so adding a Service within this is, for me at
least, a double-up and doesn't give me much of an advantage for the added
complexity.


A lot of broad stroke responses as this is a hugely complex, and subjective
area (the art of architecture!). Just throwing some other ideas your way
for you to include in the mix of your architecture.


> Hope this helps,
>
> Mark
>
>
>
> On Tue, Apr 2, 2013 at 9:25 AM, Daniel Zwolenski <zonski at gmail.com> wrote:
>
>> Just on the topic, I've had situations where I've wanted Tasks (and
>> Services) to run in my own threading model (e.g. for unit testing). So
>> I've
>> had similar wants to Mark's original request (but not directly with
>> Platform.runLater, though that may also be useful).
>>
>> It would be nice if the Task API was really just a way of saying what I
>> want done at each stage (i.e. doThisStuffBefore, doTaskStuff,
>> doThisStuffAfter), then I could/should be able to call those methods
>> directly myself from my own execution framework that may or may not be FX
>> related or even thread related (in much the same way Runnable doesnt
>> necessairly have anything to do with Threads).
>>
>> >From memory this wasnt (cleanly/easily) achievable because of the tight
>> interdependency between Tasks and the FX threading model and property
>> change events, etc.
>>
>> A couple of use cases:
>>
>>    - unit or integration testing, where I want to make sure certain
>>
>>    things/tasks happen in a certain sequence.
>>    - reusing tasks (particularly the background bit of them) outside of
>> JFX
>>
>>    eg. reuse of REST style calls in an Android, Swing, Command line, GWT,
>> or
>>    AJAX based app (Java compiled to JScript).
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>> On Tue, Apr 2, 2013 at 10:27 AM, Richard Bair <richard.bair at oracle.com
>> >wrote:
>>
>> > Hi Anthony, he's talking about
>> > http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html and
>> > related API in the javafx.concurrent package.
>> >
>> > The answer is yes, the onXXX methods of Task are called on the FX
>> thread,
>> > and as such, you don't need to perform another runLater. There's a large
>> > amount of documentation that should (hopefully) clearly detail which
>> > threads which methods are called on (and which methods can be called
>> from
>> > any thread vs. only the FX thread). But in general on Task only the
>> "call"
>> > method is called on a background thread, and you may call any of the
>> > updateXXX methods from the background thread but all other methods are
>> on
>> > the FX thread.
>> >
>> > Cheers
>> > Richard
>> >
>> > On Apr 2, 2013, at 6:31 AM, Anthony Petrov <anthony.petrov at oracle.com>
>> > wrote:
>> >
>> > > (Please use "Reply All" when replying to a mailing list. Thank you.)
>> > >
>> > > I'm not sure what Task you mean here, and how you started its
>> execution
>> > in the first place. But if I assume you've followed my advice and
>> created
>> > your own executor service and submitted your task to it, then the
>> answer is
>> > no. The executor service doesn't know anything about FX or its event
>> > thread. I suggest to use runLater() in your onSucceeded and onFailed
>> > implementations for your task if you need this code to perform some
>> > GUI-related operations.
>> > >
>> > > --
>> > > best regards,
>> > > Anthony
>> > >
>> > > On 4/2/2013 17:23, Mark Fortner wrote:
>> > >> Thanks, Anthony. The light came on after I saw Richard's reply. Are
>> the
>> > onSucceeded, onFailed methods of Tasks executed on the Application
>> thread
>> > or do I need to call runLater?
>> > >> Mark
>> > >> On Apr 2, 2013 1:18 AM, "Anthony Petrov" <anthony.petrov at oracle.com
>> <mailto:
>> > anthony.petrov at oracle.com>> wrote:
>> > >>    Platform#runLater is not a general purpose mechanism to execute
>> > >>    deferred tasks. Its purpose is to schedule execution of runnables
>> on
>> > >>    the event thread in order to perform GUI-related operations. As
>> > >>    Richard says, the event thread is a native GUI thread. There's
>> only
>> > >>    one such thread per application, hence the design of this
>> machinery.
>> > >>    Note that running code that is unrelated to GUI on the event
>> thread
>> > >>    may only make your application UI more sluggish and jerky, or even
>> > >>    appearing frozen sometimes.
>> > >>    If you need to execute general-purpose tasks asynchronously, you
>> > >>    have to create an executor service instance (such as a
>> ForkJoinPool)
>> > >>    yourself and submit your runnables there.
>> > >>    --
>> > >>    best regards,
>> > >>    Anthony
>> > >>    On 4/1/2013 23:05, Mark Fortner wrote:
>> > >>        In the past, I've found the *Platform#runLater* method to be a
>> > >>        useful way
>> > >>        to run tasks. However, I've noticed that the code that runs my
>> > >>        thread is
>> > >>        not really configurable or accessible.  For example, it would
>> be
>> > >>        nice to be
>> > >>        able to specify and configure a top-level *ThreadPoolExecutor*
>> > >>        to handle my
>> > >>        tasks.  Or be able to switch out *Executor* implementations.
>> > >>        I was wondering if there was some reason for implementing
>> > >>        runLater this
>> > >>        way, or if there are any plans to change it in the future?
>> > >>        Cheers,
>> > >>        Mark
>> >
>> >
>>
>
>


More information about the openjfx-dev mailing list