OpenFL Extensions

For the most part, OpenFL does an excellent job of providing you with the features you need in a platform-independent manner. It tells you the screen size, loads your assets, and even maps Android’s back button to the escape key.

Unfortunately, the OpenFL dev team cannot think of everything. Maybe you want to adjust the screen brightness on Android. Or the music volume. Maybe you want access to the camera on iOS. Or maybe you need to integrate a custom library used internally at your company, which the OpenFL dev team could not possibly have integrated for you.

For simplicity, I’ll be using screen brightness as an example. Setting this can be done in only 1-3 lines of code on both iOS and Android. The catch is, neither of those examples are in Haxe, and there’s no way to convert them to Haxe. If only you’d written the app “normally” rather than using OpenFL, you could just copy-paste those few lines of code, and you’d be done! But no, you wanted luxuries like cross-platform compilation, and now you have to somehow use Haxe code to invoke functions in Objective-C and Java.

Fun Fact

Did you know, when you compile for Android, OpenFL creates a complete, self-contained Android project, and then tells the Android SDK to compile that? And when compiling for iOS, it creates an Xcode project, and then has Xcode do the remaining work?

You can see for yourself by checking your output folder (probably either bin or Export; I’ll assume the former). Dig into bin/android/bin, and you’ll find all the files and folders you’d expect to find in a normal Android project. You could even, if you felt bold enough, modify the project, and compile it directly using the Android SDK. (Warning: Don’t actually do this! OpenFL will almost certainly overwrite your changes.)

The same applies to iOS – after compiling, you can check out bin/ios/bin to see the project that OpenFL created. You could try modifying this too, but again, OpenFL is going to revert your changes. There has to be a better way.

Creating an Extension

The OpenFL team is well aware of this problem, and in their infinite wisdom they created the “extension” feature. Also in their infinite wisdom, they wrote no documentation whatsoever before moving on to the next feature.

Extensions are basically mini-projects consisting of native (or Java) code, as well as Haxe bindings. You include them in your project, and then you can call the native (or Java) code by calling the Haxe bindings. Let’s look at an example.

Start by running the following:

I’m calling it “SetBrightness” because that’s all we’ll be doing here. You can call it something else.

Open up the folder, and you’ll find the following structure:

  • haxelib.json – Allows you to submit to Haxelib.
  • include.xml – Like project.xml in your main project, this tells OpenFL what to do with all the other files here.
  • SetBrightness.hx – Contains Haxe bindings for your Java and C++ functions. At first, everything in here will be sample code.
  • dependencies/
    • android/ – An Android library to be included in your Android builds. This is what lets you include Java code. All files in this folder are processed as templates, so you can use that syntax.
      • build.gradle – A project file for this Android library. Leave this alone unless you’re familiar with Gradle.
      • src/ – Despite the name, you can’t just put source files in here. They actually go in a child folder.
        • main/
          • AndroidManifest.xml – The manifest file for your Android library. If your extension requires permissions, this is the place to put them.
          • java/ – Java source files go here.
            • org/haxe/extension/
              • SetBrightness.java – The recommended place for your Java code. It comes with useful callbacks for monitoring the activity lifecycle, or you can ignore all that and write static functions.
  • ndll/ – When you compile your C++ code, the result will go in here.
    • Linux/
    • Linux64/
    • Mac/
    • Mac64/
    • Windows/
  • project/ – The root folder for your C++ project.
    • Build.xml – Build file for your C++ project. Only files named here (or included from those named here) will be compiled.
    • common/ – C++ source files (but not header files) go here.
      • ExternalInterface.cpp – Registers your C++ functions, allowing SetBrightness.hx to access them.
      • SetBrightness.cpp – Put C++ code here, based on the sample code that starts here.
    • include/ – C++ header files go here.
      • Utils.h – Header file for SetBrightness.cpp. Functions must be declared here in order for ExternalInterface.cpp to access them.

Writing Code for Android

Click through all the folders under dependencies/ until you reach SetBrightness.java. Add the following code:

That’s all well and good, but how do you call this function? The answer… is JNI. *Dramatic thunder crashes* Actually, it’s not that bad if you’re only dealing with one function. Climb your way back to the root SetBrightness/ folder, and add this to SetBrightness.hx:

That’s still a little much. Fortunately, shoe[box] came up with an easier way. Start by including the “inthebox-macros” library in your project, change the package in SetBrightness.hx to org.haxe.extension, and add @:build(ShortCuts.mirrors()) just before the class declaration. Now the code above can be replaced with this:

All that’s left is to include the extension in your project (see below), and you can call SetBrightness.setBrightness(0.8); from Haxe.

Debugging

When you try to use this extension on Android, you’ll run into a few errors. First, a compile error:

This happens because you aren’t compiling an ndll for Android, but by default Lime expects you to. To fix the error, go into include.xml and replace <ndll name="SetBrightness" /> with <ndll name="SetBrightness" unless="android" />.

Next, you’ll get a runtime error:

(The only thing that matters about this error message is that it contains the word “thread.” When developing an OpenFL extension, all thread-related errors have the same solution.)

On Android, some tasks have to be done on the main thread. When you use JNI, it runs on a thread other than the main one. Fortunately, Extension.callbackHandler.post() lets you get back to the main thread.

This function takes a Runnable object, so you’ll have to create one of those. Take all the code in your function, and put it inside the run() function:

(Remember, you only need to do this if you get a thread-related error. Usually, it isn’t worth the trouble.)

And that’s it for Android. On to iOS!

Writing Code for iOS

You’ll notice that the extension is set up for C++ code, but to access system properties like brightness, you need to use Objective-C code. Fortunately, this part’s easy: just change the .cpp file extensions to .mm. You’ll also need to update their names in Build.xml. And because Objective-C is specific to iOS, I suggest disabling them for everything else.

If you need C++ code on other platforms, just disable the C++ files on iOS:

Now to write some actual Objective-C code! Put this in SetBrightness.mm:

Now update Utils.h:

Don’t forget to update ExternalInterface.mm:

Last but not least, create the Haxe bindings in SetBrightness.hx:

Phew! That was a lot of updating. [Edit: inthebox-macros can generate the Haxe code, and I wrote a utility to generate ExternalInterface.mm and Build.xml.]

Time to compile!

Including the Extension

Almost done! All that’s left is to include it in your project.

This can actually be done in two ways. The easy way works if you’re only using one machine, or if the relative path is the same on all machines (for example, if the extension project is contained within your main project). All you have to do is add this to your project.xml or application.xml file:

The slightly harder way is to register it with Haxelib by running the following from the root of the extension:

Then include it:

21 thoughts on “OpenFL Extensions”

  1. Wow iOS takes a lot of work for what should be a simple task. Seriously. If I find a way to simplify it, I’ll update the post.

    Also, if you can read this, the comment system works.

  2. It’s great see a documentation about this. I think you could write about the HaxeObject class on android, it seems to be used to call haxe functions in the Java.

    1. I might at some point. Thing is, I got some crashes related to HaxeObject, so I just gave up and worked around it. This was a year ago and I think I know what I did wrong, but the fact remains that I don’t have experience calling Haxe functions from extensions.

  3. I get the “Error: ./common/SetBrightness.mm:8:15: error: use of undeclared identifier ‘UIScreen’
    [[UIScreen mainScreen] setBrightness:brightness];”

    Should I be including something in the .mm?

  4. Great article, thanks ! There is only one thing i can’t understand, how should i call setBrightness from my project ?

  5. Hi player_03, really nice to see that you are still a haxe developer :). I was wonder about why you only called lime rebuild for ios but not for android? Or are the two targets different in that aspect?

    Thanks for reading.

    1. Good question! You would in fact use “lime rebuild” for Android, if you wrote any C++ code you needed to include. However, if all you’re using is Java code, there’s no need for the intermediate “rebuild” step. Just compile your app normally, and the Android SDK will compile the code.

  6. Hello player_03… i’m trying to use this extension in haxe code. Could you please guide that how could we use these extensions in haxe application for iOS?

      1. Thank you for a quick reply.

        I am looking to create an iOS application using Haxe and Objective C. As per my understanding, using this tutorial I will be able to write Objective C code in one of those Cpp files and call them from .hx file.

        This way I will be using native Objective C wherever needed for my application.

        Your guidance will be much appreciated.

        Regards

  7. I have been able to get this working and objective C code is running.
    In this example the Objective C function – setBrightness had void return type.
    What if I need to return an output from Objective C back to Haxe. Say Haxe would call a function in Objective C that would display the default Media player of iOS ?

    Your help will be appreciated.

  8. Hi player_03, nice to see you over here on your site.

    My game requires some fairly heavy-duty geometric computations, which could benefit greatly from conversion to native code. So I’ve been wondering—to what extent could this mechanism be used purely for performance reasons (instead of implementing cross-platform functionality)? Could I create an extension and just fill it with optimized C++ code that I called from Haxe? Is that the best practice for bolting native code into an OpenFL app? Thanks for your advice.

    1. As long as you’re targeting Windows, Mac, Linux, Android, or iOS, your Haxe code is going to be converted to C++ anyway. And if you aren’t, then you probably won’t be able to include C++ code, even using an extension.

      You could probably write better C++ code than Haxe does, and if you think that’s necessary, go for it. (Just remember, premature optimization and all that.) You can do this with an extension, or with untyped __cpp__("raw C++ code goes here"). See this post for more details on using the latter.

      1. My code performs lots of geometric computations on points and matrices, and stores everything in a heavyweight graph data structure. Mainly, I’m assuming that I’m taking a big performance hit because of all the churn in allocating and deallocating heap storage for these little pieces of data. By that measure, it might be sufficient simply to keep around a free list of unused objects instead of always allocating new ones. Of course, I’d probably get even better performance in C++ with stack-allocated points and matrices.

        On top of that, I had to cook up some of the algorithms I need from scratch in Haxe, and it’s virtually guaranteed that I could get faster and more robust execution if I entrust those algorithms to well established C++ libraries.

        I expect that untyped cpp isn’t enough by itself, since I’ll want to call out to big chunks of code in an external library. But perhaps an extension is the way to go. Something to put on the list to investigate later, I guess…

Leave a Reply

Your email address will not be published. Required fields are marked *