We demonstrate a mobile JavaFX application, running on a Nexus 9 Tablet. It's a pure JavaFX application with our mobile application frame and it was deployed with JavaFXPorts. In the video, we change some styles on our Laptop and the application updates its styles on demand. This is really useful because resolution/screen size can be different. Sometimes the background color looks different than on a Desktop, ... And it's easy to try out new styles without application restarts - same principle as developer tools for Chrome or FireBug for Firefox.
But how did we implement this feature?
It was too easy
Here are the "secrets":
A simple socket server for the application
A custom URL handler for loading remote-retrieved styles
A simple File watcher for the client
That's it!
But to be honest, the custom URL handler was very tricky, because we tried to find a very smart, non-static, solution. And every platform has its specific handling and URL loading mechanism. But one step after another.
I guess you know that a socket server isn't a real problem. We wrote our own "generic" socket server some years ago, for JVx and it's not tricky. In principle, it works like this snippet:
PrintWriter out =newPrintWriter(clientSocket.getOutputStream(), true); BufferedReader in =newBufferedReader(newInputStreamReader(clientSocket.getInputStream()));
The problem is that you need a sort of protocol for the content. We've used our UniversalSerializer and send simple POJOs. The stylesheet POJO contains the css file as byte[]. Here's our server code:
We add custom stylesheets with remotecss as protocol. The resource loading mechanism of Java(FX) tries to load the URL automatically. The protocol isn't a standard protocol and so we had to create a custom URL handler. The problem wasn't the handler itself because it's straight forward:
The problem was that the JVM didn't know how to load remotecss:// URLs because it doesn't have a default handler. The JVM offers different solutions for this problem and the well known is:
URL.setURLStreamHandlerFactory(factory);
But this is a static mechanism and we didn't know if another library uses the same method. There's no getURLStreamHandlerFactory in URL and the solution wasn't good enough for us. For a simple test application this restriction shouldn't be a problem, but we didn't like it because we're framework developers.
There's another solution for the problem, because the source code of URL contains the static method:
This method tries to load protocol handlers automatically, if definded via system property java.protocol.handler.pkgs or from package: sun.net.www.protocol.protocolname.Handler
After reading the source code, we found a blogpost about this feature, read more. It was easy to search with the right keyword after we knew the solution.
...be careful with security manager, but this shouldn't matter.
The URL implementation for Android is a little bit different because but if you set the system property, it'll work as expected.
The hardest parts were done and the last thing was the file watcher client because we planned to send the changed CSS file(s) to the app automatically. Here's our solution (Java8 style):
thWatcher =newThread(()-> { File fi = ResourceUtil.getFileForClass("/live.css").getParentFile();
The source code examples aren't complete but I guess you can implement your own solution based on them. We made tests with iOS, Android and Desktop applications and didn't have any problems. The CSS editing feature would be a very useful extension for ScenicView but it's not available right now.