haxe to java to nme to android! (part 1)

NME is a great solution for writing applications for the Android platform, but sometimes it’s nice to escape the confines of GameActivity.java. This is fairly easy to do, but unfortunately until now it meant writing Java.

Which is fine for some people, but given the option I’d avoid it. (For one thing, it’s lead me to using two separate editors simultaneously: FlashDevelop and Notepad++.)

The convenience of using NME, along with the power and freedom that comes from using Java for Android, without ever having to leave HaXe, is one of the benefits of HaXe’s upcoming Java target. And for me, it’s a pretty major one.

There are three main steps. The first is writing the code, and the other two are the two-stage compilation process. I imagine most of the stuff I discuss will end up being automated nicely, but for now this is how I went about it.

I start by making an NME project with FlashDevelop, which sets up a lot of stuff for you. Not a whole lot of thought went into the directory structure I use, but so far it’s seemed to work.

Setting up the directories

  • java-src is where I for the haxe files that will be compiled to Java.
  • assets/res is what will become the res directory for the APK.
  • src/java/src is where the compiled Java will end up. (It’s placed into output_dir/src, so I set the output_dir to src/java.)

Updating files in the project directory

Once the directories are set up, I edit application.nmml. The only real differences are:

<assets path="assets" include="*" exclude="nme.svg|res" />
<java path="src/java/src" />
<template path="assets/res" rename="res" />
<template path="MainActivity.java" rename="src/com/gigglingcorpse/test/haxejavatoast/MainActivity.java" if="android" />

Where MainActivity.java should be renamed to depends on the package you’re using. In this case, my package is com.gigglingcorpse.test.haxejavatoast.

The next step is making MainActivity.java. It’s a Java file that we won’t be generating from haxe. We could, I just think it’d be excessive.

Since it doesn’t require modification, I copy this file from project to project.

package ::APP_PACKAGE::;
public class MainActivity extends ::APP_PACKAGE::.MyActivity {}

MainActivity.java goes in the project directory, as should be apparent from the changes to application.nmml. This is because NME will replace our MainActivity.java with its own unless we tell it to use ours instead.

Since we’re telling it to extend (in my case) com.gigglingcorpse.test.haxejavatoast.MyActivity, the next step is to create that file.

Writing the haxe that will become java

MyActivity.java should be placed in a directory according to your package name. In my case that’s java-src/com/gigglingcorpse/test/haxejavatoast. It should also extend android.app.Activity.

This is because of the default AndroidManifest.xml (you can use your own if you want.. say if you’re making a service); MainActivity needs to be an Activity..eventually.

package com.gigglingcorpse.test.haxejavatoast;
import android.os.Bundle;
import android.app.Activity;
class MyActivity extends Activity {
    override public function onCreate( state:Bundle ) {
	haxe.Log.trace = function(msg:Dynamic, ?pos:haxe.PosInfos) {
		android.util.Log.i( "trace", pos.className + "@"
				+ pos.lineNumber + ": " + msg) ; }
	trace("starting android haxe-java toast");

We reference android.app.Activity, android.os.Bundle, and android.util.Log, so we need to import them.

Since the java target hasn’t even been released yet, it’s no surprise that it lacks a number of (let’s say all of) android’s libraries.

Surprisingly, these external libraries can easily be added using extern.

It can be a bit of a hassle, depending on how many you need to use, but it seems like the sort of thing that could be fairly easily automated.
I mentioned this to @cwaneck, and he described two different ways he’d thought of doing it.

For now, I just added them myself. Here’s an example: (android/util/Log.hx)

package android.util;
extern class Log {
	public static function i( tag:String, message:String ) : Void ;

In case you’re wondering about that neat line in MyActivity.java

haxe.Log.trace = function(msg:Dynamic, ?pos:haxe.PosInfos) {
		android.util.Log.i( "trace", pos.className + "@"
				+ pos.lineNumber + ": " + msg) ; }

It sets the trace() function! So I can call trace(“hihi”); instead of android.util.Log.i( “trace”, “hihi” );. Mostly I put that in there so I’d remember it.

Compiling the haxe to java

I wrote a batch file to handle the compiling and running nme for me, but it’s only two lines so who even needs one, right?

To compile the haxe to java, run this command from the project root:

haxe-byte -cp java-src com.gigglingcorpse.test.haxejavatoast.MyActivity -java src/java

That should create a src directory into src/java, which will be filled with more folders and eventually a wondrous array of Java files.

Running nme

This is just the same as always.

me test application.nmml android

And that should be it!

Next time I’d like to talk about some of the specifics of the haxe code that is compiled to java. (For example, you may have noticed the superfluous references to the res directory in application.nmml and otherwise).