Channels ▼

Al Williams

Dr. Dobb's Bloggers

Going Android Native

September 22, 2014

I mentioned a few weeks ago that if you know Java and Linux, you might feel like you know Android. That's almost true, but the devil, of course, is in the details.

I was a little disappointed that Android Studio doesn't support the NDK (Native Development Kit) yet, although I am sure it will eventually. Being mostly an embedded guy, I'm not real fond of doing GUI development, but I often get asked to write something lean to go into an Android app (or a Java program).

Google warns you that you shouldn't use the NDK as an excuse to just write Android programs in C and I agree it probably isn't very effective to do that. The NDK allows you to either add native methods to a class or you can create an entire activity using C/C++ code by using the NativeActivity class and implementing android_main. The main reasons you'd use these features would be for performance (highly CPU-intensive operations), access to special hardware or libraries, or in the case where you have legacy C/C++ algorithms you need to use.

Effectively, this is all JNI but, as usual, the details are important. Android has its own peculiar mappings. It also has a restricted list of C libraries you can assume are present and available. You can read the documentation on the Android developer site.

The problem is, I'm not a big fan of JNI. It does work, of course, and it is efficient. However, if your actual calls into your C code make up a bit part of your application, you probably have the wrong architecture. Generally speaking, the time spent in the C code should dwarf the call overhead of the JNI call.

That's why I often turn to JNA when I need to get to a native library. JNA loads system standard libraries (that is, a DLL or .so file) and can make calls into them with very little effort on your part. JNA is available on Android, as well as other Java platforms. The type conversions are mostly automatic, although you can customize them where you want to. There's a whole list of features you can look up if you want, but suffice it to say it works for most common use cases (function pointers, nested structures or arrays, and even variable numbers of arguments). Usually it just works, and that's a great convenience.

The only downside is performance. If you take the easiest way out and let JNA do all the mapping magically, your call overhead can be as much as 10 times what conventional JNI achieves. However, using direct mapping in JNA, the call overhead is only two or three times worse than JNI. On a modern machine, that's not much time and, again, if your call overhead to a C function is a significant part of your runtime, you probably ought to evaluate how you've decomposed the problem.

I borrowed this example from the JNA documentation, to show you how easy it is to use the tool:

package com.sun.jna.examples;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

/** Simple example of JNA interface mapping and usage. */
public class HelloWorld {

    // This is the standard, stable way of mapping, which supports extensive
    // customization and mapping of Java to native types.

    public interface CLibrary extends Library {
        CLibrary INSTANCE = (CLibrary)
            Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
                               CLibrary.class);

        void printf(String format, Object... args);
    }

    public static void main(String[] args) {
        CLibrary.INSTANCE.printf("Hello, World\n");
        for (int i=0;i < args.length;i++) {
            CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
        }
    }
}

The real heart of the class is the Native.loadLibrary line. It loads msvcr.dll on Windows or libc.so on other platforms. In other words, it loads the C library and makes the printf function available. Note that the function has a varying number of arguments of different types, but JNA will handle that for you invisibly. You can see examples of calling printf in main(). Very simple. Of course, you could just as easily provide your own .so file and load it to get to your own functions. An Android program wouldn't have to even worry about DLL files at all.

Direct mapping performs better and is, in some ways, even easier. Here's the official JNA example for direct mapping:

import com.sun.jna.*;

public class HelloWorld {

    public static native double cos(double x);
    public static native double sin(double x);

    static {
        Native.register(Platform.C_LIBRARY_NAME);
    }

    public static void main(String[] args) {
        System.out.println("cos(0)=" + cos(0));
        System.out.println("sin(0)=" + sin(0));
    }
}

The difference here is that the declared methods are static and the Native.register call actually patches up calls to directly call the corresponding C code. This can interfere with some profilers, by the way, that do the same thing, but the JNA documentation talks about a work around. It also prohibits you from using function arguments that are arrays of pointers, structures, or strings.

If you've ever programmed JNI for any platform, you'll recognize that this is a lot simpler and quite possibly worth a little extra runtime overhead. The implementation is clean and pure Java. There is a JNI-type calls in the JNA library that make it work, but these are presumably well tested and not going to create errors in your code.

To further sweeten the pot, the JNAerator can automatically generate JNA definitions for JNA (much as SWIG can do for JNI).

Speaking of Android, I'll be talking about Android at the ARMTechCon 2014 along with fellow Dr. Dobbs'er Eric Bruno. The conference is October 1 to October 3 in Santa Clara, California. If you get a chance, you should definitely come by and tell us hello.

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.