Simple Native Extension for Geolocation (latitude/longitude)

I’ve been doing a bit of research for the last couple of week on how to create a native extension in Haxe 3/OpenFL with the geolocation/gps.

I thought first that maybe at this point in the framework, someone should have created native extensions for the hardware related and basic stuff such as camera, gallery, geolocation and accelerometer (I know that for accelerometer there is already code in NME and OpenFL). But since it seems that it does not exist, I decided to create it.

Hard to find proper documentation on how to create one, I stumbled upon the command openfl create extension TestExtension and it works, it creates a template for you.

The code works and the static method provided returns its int value to the OpenFL side of the app. So my next step was to search for the Android code that returns the latitude and longitude. I tested the code with a plain Android app and it works, copied it into the native extension, and after looking how to, I changed the reference related to the main activity with the one provided in the comment on top of the java class (Extension.mainActivity). And here is the problem, the app crashes, so bad that it exists the app inmediately. I thought it was maybe the way I was handling the interface methods with the macros and JNI, but later on I removed all the geolocation related code from the Java class, and the interface worked.

So I guess, it’s something incredibly simple I’m missing to see, some kind of event such as the onCreate to wait for the main activity to load, but I’ve tried that also and didn’t work.

For the record, I thougth it might be the Android permissions, but they are not, the Manifest has the permissions for ACCESS_FINE_LOCATION and/or ACCESS_COARSE_LOCATION and the app still crashes.

Java code:

	// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
	public void onLocationChanged(Location location) {
		// Called when a new location is found by the network location provider.
		_longitude = String.valueOf(location.getLongitude());
		_latitude = String.valueOf(location.getLatitude());
	}

	public void onStatusChanged(String provider, int status, Bundle extras) {}

	public void onProviderEnabled(String provider) {}

	public void onProviderDisabled(String provider) {}
};

This next part I’ve called it on onCreate or for testing, in a button click. In the original Java code Extension.mainActivity is simply “this”

	locationManager = (LocationManager) Extension.mainActivity.getSystemService(Context.LOCATION_SERVICE);

	locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_REFRESH_TIME,
			LOCATION_REFRESH_DISTANCE, locationListener);

My goal is to publish the extension to Github after I’ve finished,I just need someone to point me into the right direction.

Thanks

Did you check the log for a stack trace?

(Side note: I wrote a tutorial that people find useful, but sadly it hasn’t gotten enough attention to make it to the first page of search results.)

You may also have to be aware of threading, Java runs in a different thread than your C++ code , so if you need to send messages back from Java, you need to put your message into a queue.

Unfortunately, this is more complicated than any other platform, where everything is C++ and single-threaded.

Here’s an example:

As player_03 suggested, I’ve obtained the stack trace with logcat

W/System.err(26090): java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()

W/System.err(26090): at android.os.Handler.(Handler.java:197)
W/System.err(26090): at android.os.Handler.(Handler.java:111)
W/System.err(26090): at android.location.LocationManager$ListenerTransport$1.(LocationManager.java:197)
W/System.err(26090): at android.location.LocationManager$ListenerTransport.(LocationManager.java:197)
W/System.err(26090): at android.location.LocationManager.wrapListener(LocationManager.java:820)
W/System.err(26090): at android.location.LocationManager.requestLocationUpdates(LocationManager.java:833)
W/System.err(26090): at android.location.LocationManager.requestLocationUpdates(LocationManager.java:430)
W/System.err(26090): at org.haxe.extension.Geolocation.updateLocation(Geolocation.java:78)
W/System.err(26090): at org.haxe.lime.Lime.onTouch(Native Method)
W/System.err(26090): at org.haxe.lime.MainView$10.run(MainView.java:501)
W/System.err(26090): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1471)
W/System.err(26090): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1248)

So my guess is I’m loading my code in a wrong thread? I’m using the bold output as a reference in my search on how to create the thread, and I’ve found various examples

It still looks a bit dark to me, but I’ll be trying these examples and others and I’ll keep you guys posted

The Looper.prepare() thing means you aren’t running the code on the main thread. Wrap Geolocation.updateLocation() as Joshua described.

“Unfortunately, gentlemen, the foregoing is not a complete example.”

player_03 no-doubt understood exactly what “Joshua described,” and Joshua, singmajesty, in his turn, probably understood what his “ten, count 'em, ten…” lines of excerpted source-code meant.

Unfortunately … nobody else does. Or, rather, I certainly don’t! :smile:

Therefore, please: a complete example. Please, tell Gentle Reader™ (namely, me!) what the issue is, and how to address it.

I assume you’re getting the same “Can’t create handler inside thread that has not called Looper.prepare()” error?

If you’re not familiar with threads, here’s a very brief explanation, and here’s a more in-depth description.

When you use JNI to run Java code on Android, your Java code is not run on the main thread. At least, not by default. This is sometimes ok, but other times it can cause errors like the “Looper.prepare()” one. In those cases, you’ll have to find a way to run your code on the main thread.

There are multiple ways to tell another thread to run some code, but Handler.post() is the easiest (in OpenFL apps, at least).

Handler.post() tells Android, “Next time you get a chance, run this code on the same thread that the Handler came from.” Now you just need to find a Handler that came from the main thread, which OpenFL provides in the form of Extension.callbackHandler.

You also need to set up a function with code to be run on the main thread. In other languages (like Haxe), you could just pass the function itself, but Java requires some extra code:

new Runnable() {
    public void run() {
        //Your code here.
    }
}

Now post() it to the correct Handler:

Extension.callbackHandler.post(new Runnable() {
    public void run() {
        //Your code here.
    }
});

Wondering what code to put in the middle? Check your stack trace. Find the function that you’re responsible for (updateLocation() in the absulit’s case). Move everything in that function into the Runnable.

1 Like

Thank you! :sunglasses:

Actually, I did know about threads, and I have certainly encountered similar issue with (ick …) Microsoft Windows, where I had to be sure that a worker-thread never talked directly to the GUI. But, thank you for “covering all the bases” with these references to some good topic-introductory pages.

It probably would be good now for me to read some more material on the threading/JNI model on Android. And maybe more about the “perlguts,” so to speak, of OpenFL’s actual implementation.

If you create a sample extension using lime create extension, you should be able to have the basics for a native extension. There is an Extension class in Java (I forgot the full path, org.haxe.lime.Extension or something) that’s designed to give you an interface into things like a reference to the main activity, etc. In essence, to native extension development, it’s sort of like the “stage” variable in Flash/OpenFL. When you dip your toes in native extension development, this inevitably is (probably?) something you work with. Then when you’re talking about threading, the graphics and Java code run on different threads, so the excerpt I shared is an example of wrapping a function call in a way that is supposed to run it on the GPU thread, if I recall