JavaFX and Android

So, the other day I decided to slowly get some focus on a mobile client for my home automation. Not that there is much to do, because it's all automated, but I could at least show some data. Normally I like web interfaces as a GUI for the simple fact, that it can be used on every device that has a browser. This time I decided to try an Android app and to make it a little more fun and challenging, make an app with JavaFX for Desktop and Android. Here I'll ignore IOS devices completely, for the single reason that I prefer to work with Linux and Android systems.

The possibilities

As it is with every completely new adventure, you have no idea what to do and where to start. So it is for me in this case. Luckily, we have Google these days and some good articles about JavaFX, that have already been written. Here are some of them:

Those websites give enough basic information to start developing a JavaFX application.

After starting, I quickly got to the as of yet unanswered question, where to make the cut of platform independent development. The thing is, desktop and Android handheld devices are inherently different in their handling. Users have different assumptions on the behavior of apps on those systems (for example mouse/keyboard handling vs. touch handling) and the technology under the hood differs also to a certain degree, for instance plain Java 8 on the desktop and the Android framework on the Android devices. The possibilities for the basic layout of a mutliplatform app in JavaFX are endless. From everything in JavaFX and Gluon Charm Down plugins to almost everything platform specific, the line can be put anywhere and it's position solely depends on ones preference and decision.

My decision was this one: Everything, that can be done as general business logic, as well as the UI structure (FXML parts) are part of the multiplatform core. The CSS layouts, as well as UI-specific features like notifications and some platform specific data aggregation belong into the variants. The following document contains the hurdles, over which I stumbled, while trying to realize this concept.

Lifecycle

One of the first noticable things is the different lifecycles of JavaFX and Android. JavaFX supports the following three:

  • **init()** This is called during initialization of the application.
  • **start()** This is called when the application is to be shown to the user. start() is intended for setting up the UI part and has to be implemented, because it's also abstract.
  • **stop()** This is called when the application is stopped.

Android, on the other hand, uses a more detailed set of lifecycle methods:

  • **onCreate()** This is called when the application is created / the activity is starting.
  • **onStart()** This is called when the application is started and becomes visible.
  • **onResume()** This is called when the application is resumed, a.k.a. when it becomes active.
  • **onPause()** This is called when the application is paused and going into background.
  • **onStop()** This is called when the application is stopped and is not visible any longer.
  • **onDestroy()** And finally, this is called when the application is destroyed. This method may not be called!

! **Beware:** On Android, stop() appears to be connected to onDestroy() and therefore it might not be called. In my Android-L experience, it's rarely called. During a phase where I checked the lifecycle behavior, this method was executed 3 out of of around 20 times ...

I use stop() to clean up some things myself, including in the Android variant, which didn't work. Well, Android knowns how to clean up after itself (I hope). But sometimes it is necessary to do actions right before the application is shut down. This might be for instance to save some data. Fortunately it is possible to connect to the Android lifecycle with:

FXActivity.getInstance().getApplication().registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)

ActivityLifecycleCallbacks contains the above listed methods, which need to be implemented. Again, onDestroy() is likely not going to be called. But now the methods onStop() or onPause() are available, where things like persistence can be taken care of.

Exiting the application

Speaking of lifecycle, here is another issue fitting right to this topic. What happens in Android, when the back button is pressed? Well, when the application doesn't handle it internally, then it's forwarded to Android. Android pushes the application into the background and shows the home screen. This does not happen with JavaFX applications per default.

Android applications have the method onBackPressed() in their activity. The programmer is free to overwrite it, but the default is to forward the event to the Android system. JavaFX maps the back press to the ESC key. It's the programmers decision, whether or not to handle key events and whether or not to farward them to the OS. There is no default forwarding to Android.

The solution is to implement the key handling by attaching an EventHandler to your Scene:

scene.addEventHandler( KeyEvent.KEY_RELEASED, event ->
{
    // Use back button on touch devices to close app
    if (!Platform.isDesktop() && KeyCode.ESCAPE.equals( event.getCode() ))
    {
        // the exit handling goes here
    }
});

Now there are five ways of closing your application. The straight forward ones are:

  • **Platform.exit()** will close and destroy your application. Unfortunately the standard behaviour is to only push the application to the background, meaning only onStop() should be called in terms of lifecycle methods. With Platform.exit() he application will still be visible in the task view, but it will be destroyed. This is also one of the rare cases, where I got the onDestroy() method call.
  • **primaryStage.hide()** will cause exactly the same as Platform.exit(), at least on Android.
  • **FXActivity.getInstance().finish()** is using native methods. Therefore, the call of this method needs to be in the Android specific code part. This method is used to terminate the activity / application and it will do so, exactly like the others above.
  • **FXActivity.getInstance().onBackPressed()** ... We can forward the ESC in the Android code to onBackPressed(). Nice, right? Now why haven't I told this earlier?! The answer is, it's not really working the way it should and it doesn't make a difference to the above options. Even with using this _'native'_ method, our app will be closed and destroyed.

Ok, why is this so essential anyway? The reason to go through this mess is, that after the application is destroyed, it can't do anything anymore, nothing at all. But sometimes it's useful to have the application being active in the background, for instance to notify the user about certain events by sending notifications. If the application is destroyed, this option is gone.

So what now? Well, thanks to Stacktrace, I found the following, fifth solution. The Android homescreen itself is an Activity, which can be started via an Intent. Hence, we can do this to move to the homescreen (exact copy from Stacktrace):

Intent startMain = new Intent( Intent.ACTION_MAIN );
startMain.addCategory( Intent.CATEGORY_HOME );
startMain.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
FXActivity.getInstance().startActivity( startMain );

This will open the homescreen and close, but not destroy the application.

Notifications and Icons

The next step for me was to show notifications. This should be easy, right. Well, you can use a Gloun charm down plugin for that. It has the message and the date and one or two other parameters, if I remember correctly. For me this was too few settings, to adjust the notification to my needs.

The implementation of an Android notification requires a linking from the main code to the Android code where the notification can be built. My notification had a title and a text. That's enough for the start. Compile, run, done, ... Well, almost. Android doesn't show the notification, but instead complains, that it needs a small icon. Really Android, right now I wan't to test how I can do things, not draw icons?! Ok, so I drew an icon, even had some fun with that, put it into the android.res.drawable directory and ... now it actually gets interesting.

Based on my knownledge, I was used to get a resource ID when developing with Android. In JavaFX development the ID is not generated during delevopment time on the fly for your resources, but setSmallIcon of the Androids notification builder requires a resource-ID. Fortunately, Android provides also a dynamic way to access resources via the activity, which also works in JavaFX:

FXActivity.getInstance().getResources().getIdentifier(name, type, packageName)

So, name is the name of the icon (without file ending), type is the type of the resource as String, in this case "drawable" and package name ... what is the package name? Well, there's a shortcut for that too:

FXActivity.getInstance().getPackageName()

That is all we need to send a notification:

private NotificationManager manager = (NotificationManager) FXActivity.getInstance().getApplicationContext()
        .getSystemService( Context.NOTIFICATION_SERVICE );

Notification n = new Notification.Builder( FXActivity.getInstance().getApplicationContext() )
        .setContentTitle( "Some title" )
        .setContentText( "Some message" )
        .setSmallIcon( FXActivity.getInstance().getResources().getIdentifier(
                "myIcon", "drawable", FXActivity.getInstance().getPackageName() ) )
        .build();

manager.notify( 0, n );

Done.

One more thing, while talking about images: To give the application a nice launcher icon, it's necessary to put an AndroidManifest.xml in the android directory and configure your build script to use this one. A previously generated file can be used here. In this manifest, the icon can be set the usual way with the application arguments android:icon and android:logo.