java - Swing application initialization and loading screen approach -
i have made quite lot of various swing apps , loading time vary between few seconds , minutes depending on application ui/data size. in cases application info loading mixed ui loading.
a few seconds load time not issue, when longer let's 10 seconds - obvious kind of loading screen should displayed until ui/data initialized.
what - create kind of loading screen first (for illustration window logo , label beingness updated while application loading) , update various loading "points" in application.
the problem - application loaded within single phone call queued edt , hard separate multiply calls edt without complicating application's code. since application loading performed in single phone call queued edt cannot update loading screen - update won't displayed until application initialized since edt busy loading application.
so implement loading screen in cases have moved application ui initialization outside of edt , have designed them in way won't ui update stuff while loading performed. application's frame display , application ui actions still performed in edt. not generally, after lots of testing , looking through swing code know sure doesn't cause issues on big applications. still, isn't if doesn't cause issues.
so question is: what approaches can used display , update application loading screen while keeping application initialization in edt?
hopefully isn't broad.
here "dummy" application showcases "bad" approach:
import javax.swing.*; import java.awt.*; public class dummyapplication extends jframe { private static jdialog loadingdialog; private static jlabel loadingprogress; public dummyapplication () { super ( "dummy application" ); dummyprogressupdate ( "loading content...", 3000 ); final jlabel label = new jlabel ( "custom content" ); label.setborder ( borderfactory.createemptyborder ( 100, 100, 100, 100 ) ); getcontentpane ().add ( label ); dummyprogressupdate ( "loading settings...", 3000 ); setdefaultcloseoperation ( windowconstants.exit_on_close ); pack (); setlocationrelativeto ( null ); dummyprogressupdate ( "opening application...", 1000 ); } private static void dummyprogressupdate ( final string status, final int time ) { swingutilities.invokelater ( () -> loadingprogress.settext ( status ) ); dummyloadtime ( time ); } private static void dummyloadtime ( final long time ) { seek { thread.sleep ( time ); } grab ( final interruptedexception e ) { e.printstacktrace (); } } public static void main ( final string[] args ) throws exception { // displaying loading screen edt first swingutilities.invokeandwait ( () -> { loadingdialog = new jdialog ( ( window ) null, "loading screen" ); loadingprogress = new jlabel ( "initializing application...", jlabel.center ); loadingprogress.setborder ( borderfactory.createlineborder ( color.light_gray ) ); loadingdialog.getcontentpane ().setlayout ( new borderlayout () ); loadingdialog.getcontentpane ().add ( loadingprogress ); loadingdialog.setundecorated ( true ); loadingdialog.setalwaysontop ( true ); loadingdialog.setmodal ( false ); loadingdialog.setsize ( 400, 100 ); loadingdialog.setlocationrelativeto ( null ); loadingdialog.setvisible ( true ); } ); // initializing application outside of edt final dummyapplication applicationframe = new dummyapplication (); // displaying application edt swingutilities.invokelater ( () -> { loadingdialog.setvisible ( false ); applicationframe.setvisible ( true ); } ); } }
also time ago found interesting stuff implemented in jdk7: http://sellmic.com/blog/2012/02/29/hidden-java-7-features-secondaryloop/
that secondaryloop
feature allows block futher code execution in edt thread without causing ui stuck. same modal jdialog when opened within edt.
so feature found improve way solve case:
import javax.swing.*; import java.awt.*; public class dummyapplication extends jframe { private static jdialog loadingdialog; private static jlabel loadingprogress; public dummyapplication () { super ( "dummy application" ); dummyprogressupdate ( "loading content...", 3000 ); final jlabel label = new jlabel ( "custom content" ); label.setborder ( borderfactory.createemptyborder ( 100, 100, 100, 100 ) ); getcontentpane ().add ( label ); dummyprogressupdate ( "loading settings...", 3000 ); setdefaultcloseoperation ( windowconstants.exit_on_close ); pack (); setlocationrelativeto ( null ); dummyprogressupdate ( "displaying application...", 1000 ); } private static void dummyprogressupdate ( final string status, final int time ) { // utilize secondaryloop block execution , forcefulness loading screen update final secondaryloop loop = toolkit.getdefaulttoolkit ().getsystemeventqueue ().createsecondaryloop (); swingutilities.invokelater ( () -> { loadingprogress.settext ( status ); loop.exit (); } ); loop.enter (); // perform dummy heavy operation dummyloadtime ( time ); } private static void dummyloadtime ( final long time ) { seek { thread.sleep ( time ); } grab ( final interruptedexception e ) { e.printstacktrace (); } } public static void main ( final string[] args ) throws exception { // displaying loading screen edt first swingutilities.invokeandwait ( () -> { loadingdialog = new jdialog ( ( window ) null, "loading screen" ); loadingprogress = new jlabel ( "initializing application...", jlabel.center ); loadingprogress.setborder ( borderfactory.createlineborder ( color.light_gray ) ); loadingdialog.getcontentpane ().setlayout ( new borderlayout () ); loadingdialog.getcontentpane ().add ( loadingprogress ); loadingdialog.setundecorated ( true ); loadingdialog.setalwaysontop ( true ); loadingdialog.setmodal ( false ); loadingdialog.setsize ( 400, 100 ); loadingdialog.setlocationrelativeto ( null ); loadingdialog.setvisible ( true ); } ); // initializing , displaying application edt swingutilities.invokelater ( () -> { final dummyapplication applicationframe = new dummyapplication (); loadingdialog.setvisible ( false ); applicationframe.setvisible ( true ); } ); } }
as can see - in dummyprogressupdate
method i've done tricky workaround case - blocking execution performed in edt , waiting separate phone call queued edt update loading screen.
and works - though not sure thing , whether might cause side-effects on larger scale. , loading screen in case updated when forcefulness update made means if loading screen has (for example) animation running - won't display , updated text.
if you're loading info or having connect remote services or other lengthy applications, swingworker
simpler of available approches, provides progress back upwards via setprogress
method , propertychangelistener
support.
it provides ui synchronisation via it's process
/publish
methods, allowing safely update ui , done
method and/or propertychangelistener
can used determine when worker done
if you're doing trying loading ui elements should consider lazy loading approach.
so rather loading ui elements @ once, when user may not @ sections of ui, much improve thought load elements when absolutely have , save time.
when using things cardlayout
or jtabbedpane
, much improve initialise ui , info of views when become visible.
if want, have unload when view no longer visible, disconnecting listeners info source, stopping form responding changes info source user not see...
java swing event-dispatch-thread
No comments:
Post a Comment