• Register
Report article RSS Feed Creating, Compiling, and Deploying Native Projects from the Android NDK

Compile and deploy official sample applications from the Android NDK with Ant build tool and native code compiler ndk-build

Posted by filter-coffee on Feb 21st, 2012
Basic Other.

In this article by Sylvain Ratabouil, author of Android NDK Beginner’s Guide we are going to do the following:

  • Compile and deploy official sample applications from the Android NDK with Ant build tool and native code compiler ndk-build
  • Learn in more detail about ADB, the Android Debug Bridge, to control a development device
  • Discover additional tools like AM to manage activities and AAPT to package applications
  • Create our first own hybrid multi-language project using Eclipse
  • Interface Java to C/C++ through Java Native Interfaces (in short JNI)


Android NDK Beginner’s Guide

Android NDK Beginner’s Guide
(Discover the native side of Android and inject the power of C/C++ in your applications with this book and ebook)
By the end of this article, you should know how to start up a new Android native project on your own.

A man with the most powerful tools in hand is unarmed without the knowledge of their usage. Eclipse, GCC, Ant, Bash, Shell, Linux—any new Android programmer needs to deal with this technologic ecosystem. Depending on your background, some of these names may sound familiar to your ears. Indeed, that is a real strength; Android is based on open source bricks which have matured for years. Theses bricks are cemented by the Android Development Kits (SDK and NDK) and their set of new tools: Android Debug Bridge (ADB), Android Asset Packaging Tool (AAPT), Activity Manager (AM), ndk-build, and so on. So, since our development environment is set up, we can now get our hands dirty and start manipulating all these utilities to create, compile, and deploy projects which include native code.

Compiling and deploying NDK sample applications I guess you cannot wait anymore to test your new development environment. So why not compile and deploy elementary samples provided by the Android NDK first to see it in action? To get started, I propose to run HelloJni, a sample application which retrieves a character string defined inside a native C library into a Java activity (an activity in Android being more or less equivalent to an application screen).

Time for action – compiling and deploying hellojni sample

Let's compile and deploy HelloJni project from command line using Ant:

  1. Open a command-line prompt (or Cygwin prompt on Windows).
  2. Go to hello-jni sample directory inside the Android NDK. All the following steps have to performed from this directory:
    $ cd $ANDROID_NDK/samples/hello-jni
  3. Create Ant build file and all related configuration files automatically using android command (android.bat on Windows). These files describe how to compile and package an Android application:
    android update project –p .
  4. (Move the mouse over the image to enlarge.)

  5. Build libhello-jni native library with ndk-build, which is a wrapper Bash script around Make. Command ndk-build sets up the compilation toolchain for native C/ C++ code and calls automatically GCC version featured with the NDK.
    $ ndk-build
  6. Make sure your Android development device or emulator is connected and running.
  7. Compile, package, and install the final HelloJni APK (an Android application package). All these steps can be performed in one command, thanks to Ant build automation tool. Among other things, Ant runs javac to compile Java code, AAPT to package the application with its resources, and finally ADB to deploy it on the development device. Following is only a partial extract of the output:
    $ ant install
  8. The result should look like the following extract:

  9. Launch a shell session using adb (or adb.exe on Windows). ADB shell is similar to shells that can be found on the Linux systems:
    $ adb shell
  10. From this shell, launch HelloJni application on your device or emulator. To do so, use am, the Android Activity Manager. Command am allows to start Android activities, services or sending intents (that is, inter-activity messages) from command line. Command parameters come from the Android manifest:
    # am start -a android.intent.action.MAIN -n com.example.hellojni/com.example.hellojni.HelloJni

  11. Finally, look at your development device. HelloJni appears on the screen!

What just happened?

We have compiled, packaged, and deployed an official NDK sample application with Ant and SDK command-line tools. We will explore them more in later part. We have also compiled our first native C library (also called module) using the ndk-build command. This library simply returns a character string to the Java part of the application on request. Both sides of the application, the native and the Java one, communicate through Java Native Interface. JNI is a standard framework that allows Java code to explicitly call native C/C++ code with a dedicated API.

Finally, we have launched HelloJni on our device from an Android shell (adb shell) with the am Activity Manager command. Command parameters passed in step 8 come from the Android manifest: com.example.hellojni is the package name and com.example.hellojni. HelloJni is the main Activity class name concatenated to the main package.

<!--?xml version="1.0" encoding="utf-8"?-->

...

...

Automated build
Because Android SDK, NDK, and their open source bricks are not bound to Eclipse or any specific IDE, creating an automated build chain or setting up a continuous integration server becomes possible. A simple bash script with Ant is enough to make it work!

HelloJni sample is a little bit... let's say rustic! So what about trying something fancier? Android NDK provides a sample named San Angeles. San Angeles is a coding demo created in 2004 for the Assembly 2004 competition. It has been later ported to OpenGL ES and reused as a sample demonstration in several languages and systems, including Android. You can find more information by visiting one of the author's page: Jet.ro.

Have a go hero – compiling san angeles OpenGL demo

To test this demo, you need to follow the same steps:

  1. Go to the San Angeles sample directory.
  2. Generate project files.
  3. Compile and install the final San Angeles application.
  4. Finally run it.

As this application uses OpenGL ES 1, AVD emulation will work, but may be somewhat slow!

You may encounter some errors while compiling the application with Ant:

The reason is simple: in res/layout/ directory, main.xml file is defined. This file usually defines the main screen layout in Java application—displayed components and how they are organized. However, when Android 2.2 (API Level 8) was released, the layout_width and layout_height enumerations, which describe the way UI components should be sized, were modified: FILL_PARENT became MATCH_PARENT. But San Angeles uses API Level 4.

There are basically two ways to overcome this problem. The first one is selecting the right Android version as the target. To do so, specify the target when creating Ant project files:

$ android update project –p . -–target android-8

This way, build target is set to API Level 8 and MATCH_PARENT is recognized. You can also change the build target manually by editing default.properties at the project root and replacing:

target=android-4

with the following line:

target=android-8

The second way is more straightforward: erase the main.xml file! Indeed, this file is in fact not used by San Angeles demo, as only an OpenGL screen created programmatically is displayed, without any UI components.

Target right!
When compiling an Android application, always check carefully if you are using the right target platform, as some features are added or updated between Android versions. A target can also dramatically change your audience wideness because of the multiple versions of Android in the wild... Indeed, targets are moving a lot and fast on Android!.

All these efforts are not in vain: it is just a pleasure to see this old-school 3D environment full of flat-shaded polygons running for the first time. So just stop reading and run it!

Exploring android SDK tools Android SDK includes tools which are quite useful for developers and integrators. We have already overlooked some of them including the Android Debug Bridge and android command. Let's explore them deeper.

 

Android debug bridge

You may have not noticed it specifically since the beginning but it has always been there, over your shoulder. The Android Debug Bridge is a multifaceted tool used as an intermediary between development environment and emulators/devices. More specifically, ADB is:

  • A background process running on emulators and devices to receive orders or requests from an external computer.
  • A background server on your development computer communicating with connected devices and emulators. When listing devices, ADB server is involved. When debugging, ADB server is involved. When any communication with a device happens, ADB server is involved!
  • A client running on your development computer and communicating with devices through ADB server. That is what we have done to launch HelloJni: we got connected to our device using adb shell before issuing the required commands.

ADB shell is a real Linux shell embedded in ADB client. Although not all standard commands are available, classical commands, such as ls, cd, pwd, cat, chmod, ps, and so on are executable. A few specific commands are also provided such as:

logcat

To display device log messages

dumpsys

To dump system state

dmesg

To dump kernel messages

ADB shell is a real Swiss Army knife. It also allows manipulating your device in a flexible way, especially with root access. For example, it becomes possible to observe applications deployed in their "sandbox" (see directory /data/data) or to a list and kill currently running processes.

ADB also offers other interesting options; some of them are as follows:

pull

To transfer a file to your computer

push

To transfer a file to your device or emulator

install

To install an application package

install -r

To reinstall an application, if already deployed

devices

To list all Android devices currently connected, including emulators

reboot

To restart an Android device programmatically

wait-for-device

To sleep, until a device or emulator is connected to your computer (for example,. in a script)

start-server

To launch the ADB server communicating with devices and emulators

kill-server

To terminate the ADB server

bugreport

To print the whole device state (like dumpsys)

help

To get an exhaustive help with all options and flags available

To ease the writing of issued command, ADB provides facultative flags to specify before options:

-s To target a specific device

-d To target current physical device, if only one is connected (or an error message is raised)

-e To target currently running emulator, if only one is connected (or an error message is raised)

ADB client and its shell can be used for advanced manipulation on the system, but most of the time, it will not be necessary. ADB itself is generally used transparently. In addition, without root access to your phone, possible actions are limited. For more information, see Developer.android.com.

Root or not root.
If you know the Android ecosystem a bit, you may have heard about rooted phones and non-rooted phones. Rooting a phone means getting root access to it, either "officially" while using development phones or using hacks with an end user phone. The main interest is to upgrade your system before the manufacturer provides updates (if any!) or to use a custom version (optimized or modified, for example, CyanogenMod). You can also do any possible (especially dangerous) manipulations that an Administrator can do (for example, deploying a custom kernel). Rooting is not an illegal operation, as you are modifying YOUR device. But not all manufacturers appreciate this practice and usually void the warranty.

Have a go hero – transferring a file to SD card from command line

Using the information provided, you should be able to connect to your phone like in the good old days of computers (I mean a few years ago!) and execute some basic manipulation using a shell prompt. I propose you to transfer a resource file by hand, like a music clip or a resource that you will be reading from a future program of yours.

To do so, you need to open a command-line prompt and perform the following steps:

  1. Check if your device is available using adb from command line.
  2. Connect to your device using the Android Debug Bridge shell prompt.
  3. Check the content of your SD card using standard Unix ls command. Please note that ls on Android has a specific behavior as it differentiates ls mydir from ls mydir/, when mydir is a symbolic link.
  4. Create a new directory on your SD card using the classic command mkdir
  5. .

  6. Finally, transfer your file by issuing the appropriate adb command.

Project configuration tool

The command named android is the main entry point when manipulating not only projects but also AVDs and SDK updates. There are few options available, which are as follows:

  • create project: This option is used to create a new Android project through command line. A few additional options must be specified to allow proper generation:

    -p   The project path

    -n The project name

    -t The Android API target

    -k The Java package, which contains application's main class

    -a The application's main class name (Activity in Android terms)

    For example:

    $ android create project –p ./MyProjectDir –n MyProject –t
    android-8 –k com.mypackage –a MyActivity

  • update project: This is what we use to create Ant project files from an existing source. It can also be used to upgrade an existing project to a new version. Main parameters are as follows:

    -p The project path

    -n To change the project name

    -l To include an Android library project (that is, reusable code). The path must be relative to the project directory).

    -t To change the Android API target

    There are also options to create library projects (create lib-project, update lib- project) and test projects (create test-project, update test-project). I will not go into details here as this is more related to the Java world.

    As for ADB, android command is your friend and can give you some help:

    $ android create project –help

     

    Command android is a crucial tool to implement a continuous integration toolchain in order to compile, package, deploy, and test a project automatically entirely from command line.

    Have a go hero – towards continuous integration

    With adb, android, and ant commands, you have enough knowledge to build a minimal automatic compilation and deployment script to perform some continuous integration. I assume here that you have a versioning software available and you know how to use it. Subversion (also known as SVN) is a good candidate and can work in local (without a server).

    Perform the following operations:

    1. Create a new project by hand using android command.
    2. Then, create a Unix or Cygwin shell script and assign it the necessary execution rights (chmod command). All the following steps have to be scribbled in it.
    3. In the script, check out sources from your versioning system (for example, using a svn checkout command) on disk. If you do not have a versioning system, you can still copy your own project directory using Unix commands.
    4. Build the application using ant.
    5. Do not forget to check command results using $?. If the returned value is different from 0, it means an error occurred. Additionally, you can use grep or some custom tools to check potential error messages.

    6. If needed, you can deploy resources files using adb
    7. Install it on your device or on the emulator (which you can launch from the script) using ant as shown previously.
    8. You can even try to launch your application automatically and check Android logs (see logcat option in adb). Of course, your application needs to make use of logs!

    A free monkey to test your App!
    In order to automate UI testing on an Android application, an interesting utility that is provided with the Android SDK is MonkeyRunner, which can simulate user actions on a device to perform some automated UI testing. Have a look at Developer.android.com .

    To favor automation, a single Android shell statement can be executed from command-line as follows:

    adb shell ls /sdcard/

     

    To execute a command on an Android device and retrieve its result back on your host shell, execute the following command: adb shell "ls / notexistingdir/ 1> /dev/null 2> &1; echo \$?" Redirection is necessary to avoid polluting the standard output. The escape character before $? is required to avoid early interpretation by the host shell.

    Now you are fully prepared to automate your own build toolchain!

Creating your first android project using eclipse In the first part of the article, we have seen how to use Android command-line tools. But developing with Notepad or VI is not really attractive. Coding should be fun! And to make it so, we need our preferred IDE to perform boring or unpractical tasks. So let's see now how to create an Android project using Eclipse.

Eclipse views and perspectives
Several times in this book, I have asked you to look at an Eclipse View like the Package Explorer View, the Debug View, and so on. Usually, most of them are already visible, but sometimes they are not. In that case, open them through main menu: Window | Show View | Other…. Views in Eclipse are grouped in perspectives, which basically store your workspace layout. They can be opened through main menu: Window | Open Perspective | Other…. Note that some contextual menus are available only in some perspectives.

Time for action – initiating a Java project

  1. Launch Eclipse.
  2. In the main menu, select File | New | Project…
  3. In the project wizard, select Android | Android Project and then Next.
  4. In the next screen, enter project properties:
    • In Project name, enter MyProject.
    • Select Create a new project in workspace.
    • Specify a new location if you want to, or keep the default location (that is, your eclipse workspace location).
    • Set Build Target to Android 2.3.3.
    • In Application name, enter (which can contain spaces): MyProject.
    • In Package name, enter com.myproject.
    • Create a new activity with the name MyActivity.
    • Set Min SDK Version to 10.
  5. Click on Finish. The project is created. Select it in Package Explorer view.
  6. In the main menu, select Run | Debug As | Android Application or click on the Debug button in the toolbar.

  7. Select application type Android Application and click OK:
  8. Your application is launched, as shown in the following screenshot:

What just happened?

We have created our first Android project using Eclipse. In a few screens and clicks, we have been able to launch the application instead of writing long and verbose commands. Working with an IDE like Eclipse really gives a huge productivity boost and makes programming much more comfortable!

ADT plugin has an annoying bug that you may have already encountered: Eclipse complains that your Android project is missing the required source folder gen whereas this folder is clearly present. Most of the time, just recompiling the project makes this error disappear. But sometimes, Eclipse is recalcitrant and refuses to recompile projects. In that case, a little-known trick, which can be applied in many other cases, is to simply open the Problems view, select these irritating messages, delete them without mercy (Delete key or right-click and Delete) and finally recompile the incriminated project.

Android projects created with ADT are always Java projects. But thanks to Eclipse flexibility, we can turn them into C/C++ projects too; we are going to see this at the end of this article.

Avoiding space in file paths
When creating a new project, avoid leaving a space in the path where your project is located. Although Android SDK can handle that without any problem, Android NDK and more specifically GNU Make may not really like it.

Introducing Dalvik

It is not possible to talk about Android without touching a word about Dalvik. Dalvik, which is also the name of an Icelandic village, is a Virtual Machine on which Android bytecode is interpreted (not native code!). It is at the core of any applications running on Android. Dalvik is conceived to fit the constrained requirements of mobile devices. It is specifically optimized to use less memory and CPU. It sits on top of the Android kernel which provides the first layer of abstraction over hardware (process management, memory management, and so on).

Android has been designed with speed in mind. Because most users do not want to wait for their application to be loaded while others are still running, the system is able to instantiate multple Dalvik VMs quickly, thanks to the Zygote process. Zygote, whose name comes from the very first biologic cell of an organism from which daughter cells are reproduced, starts when the system boots up. It preloads (or "warms up") all core libraries shared among applications as well as a Dalvik instance. To launch a new application, Zygote is simply forked and the initial Dalvik instance is copied. Memory consumption is lowered by sharing as many libraries as possible between processes.

Dalvik operates on Android bytecode, which is different from Java bytecode. Bytecode is stored in an optimized format called Dex generated by an Android SDK tool named dx. Dex files are archived in the final APK with the application manifest and any native libraries or additional resources needed. Note that applications can get further optimized during installation on end user's device.

Interfacing Java with C/C++ Keep your Eclipse IDE opened as we are not done with it yet. We have a working project indeed. But wait, that is just a Java project, whereas we want to unleash the power of Android with native code! In this part, we are going to create C/C++ source files, compile them into a native library named mylib and let Java run this code.

Time for action – calling C code from Java

The native library mylib that we are going to create will contain one simple native method getMyData() that returns a basic character string. First, let's write the Java code to declare and run this method.

  1. Open MyActivity.java. Inside main class, declare the native method with the native keyword and no method body:
    public class MyActivity extends Activity {
    public native String getMyData();
    ...

  2. Then, load the native library that contains this method within a static initialization block. This block will be called before Activity instance gets initialized:
    ...
    static {
    System.loadLibrary("mylib");
    }
  3. Finally, when Activity instance is created, call the native method and update the screen content with its return value. You can refer to the source code provided with this book for the final listing:
    ...
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    setTitle(getMyData());
    }
    }

    Now, let's prepare the project files required to build the native code.

  4. In Eclipse, create a new directory named jni at the project's root using menu File | New | Folder.
  5. Inside the jni directory, create a new file named Android.mk using menu File | New | File. If CDT is properly installed, the file should have the following specific icon in the Package Explorer view:

  6. Write the following content into this file. Basically, this describes how to compile our native library named mylib which is composed of one source file the com_ myproject_MyActivity.c:
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := mylib
    LOCAL_SRC_FILES := com_myproject_MyActivity.c
    include $(BUILD_SHARED_LIBRARY)

    As project files for native compilation are ready, we can write the expected native source code. Although the C implementation file must be written by hand, the corresponding header file can be generated with a helper tool provided by the JDK: javah.

  7. In Eclipse, open Run | External Tools | External Tools Configurations….
  8. Create a new program configuration with the following parameters:
    • Name: MyProject javah.
    • Location refers to javah absolute path, which is OS-specific. In Windows, you can enter ${env_var:JAVA_HOME}\bin\javah.exe. In Mac OS X and Linux, it is is usually /usr/bin/javah.
    • Working directory: ${workspace_loc:/MyProject/bin}.
    • Arguments: –d ${workspace_loc:/MyProject/jni} com.myproject. MyActivity}.

    In Mac OS X, Linux, and Cygwin, you can easily find the location of an executable available in $PATH, by using the which command.
    For example,
    $ which javah

  9. On the Refresh tab, check Refresh resources upon completion and select Specific resources. Using the Specify Resources… button, select the jni folder.
  10. Finally, click on Run to save and execute javah. A new file com_myproject_ MyActivity.h is generated in the jni folder. It contains a prototype for the method getMyData() expected on the Java side:
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include
    ...
    JNIEXPORT jstring JNICALL Java_com_myproject_MyActivity_getMyData
      (JNIEnv *, jobject);
    ...

  11. We can now create com_myproject_MyActivity.c implementation inside the jni directory to return a raw character string. Method signature originates from the generated header file:
    #include "com_myproject_MyActivity.h"
    JNIEXPORT jstring Java_com_myproject_MyActivity_getMyData
    (JNIEnv* pEnv, jobject pThis)
    {
    return (*pEnv)->NewStringUTF(pEnv,
    "My native project talks C++");
    }

    Eclipse is not yet configured to compile native code, only Java code. Until we do that in the last part of this article, we can try to build native code by hand.

  12. Open a terminal prompt and go inside the MyProject directory. Launch compilation of the native library with the command ndk-build:
    $ cd /MyProject
    $ ndk-build

    The native library is compiled in the libs/armeabi directory and is named libmylib.so. Temporary files generated during compilation are located in the obj/local directory.

  13. From Eclipse, launch MyProject again. You should obtain following result:

What just happened?

In the previous part, we created an Android Java project. In this second part, we have interfaced Java code to a native library compiled with the Android NDK from a C file. This binding from Java to C allows retrieving through Java Native Interfaces a simple Java string allocated in the native code. The example application shows how Java and C/C++ can cooperate together:

  1. By creating UI components and code on the Java side and defining native calls.
  2. Using javah to generate header file with corresponding C/C++ prototypes.
  3. Writing native code to perform the expected operation.

Native methods are declared on the Java side with the native keyword. These methods have no body (like an abstract method) as they are implemented on the native side. Only their prototype needs to be defined. Native methods can have parameters, a return value, any visibility (private, protected, package protected or public) and can be static, like classic Java methods. Of course, they require the native library with method implementations to be loaded before they are called. A way to do that is to invoke System.loadLibrary() in a static initialization block, which is initialized when the containing class is loaded. Failure to do so results in an exception of type java.lang.UnsatisfiedLinkError, which is raised when the native method is invoked for the first time.

Although it is not compulsory, javah tool provided by the JDK is extremely useful to generate native prototypes. Indeed, JNI convention is tedious and error-prone. With generated headers, you immediately know if a native method expected by the Java side is missing or has an incorrect signature. I encourage you to use javah systematically in your projects, more specifically, each time native method's signature is changed. JNI code is generated from .class files, which means that your Java code must be first compiled before going through javah conversion. Implementation needs to be provided in a separate C/C++ source file.

Remember that a very specific naming convention, which is summarized by the following pattern, must be followed by native side methods:

Java___ (JNIEnv* pEnv,
...)

Native method name is prefixed with Java_ and the packages/class name (separated by _) containing it separated. First argument is always of type JNIEnv and the preceding arguments are the actual parameters given to the Java method.

More on makefiles

Native library building process is orchestrated by a Makefile named Android.mk. By convention, Android.mk is in folder jni, which is located inside the project's root. That way, ndk-build command can find this file automatically when the command is invoked. Therefore, C/C++ code is by convention also located in jni directory (but this can be changed by configuration).

Android Makefiles are an essential piece of the NDK building process. Thus, it is important to understand the way they work to manage a project properly. An Android.mk file is basically a "baking" file, which defines what to compile and how to compile. Configuration is performed using predefined variables, among which are: LOCAL_PATH, LOCAL_MODULE and LOCAL_SRC_FILES.

The Android.mk file presented in MyProject is a very simple Makefile example. Each instruction serves a specific purpose:

LOCAL_PATH := $(call my-dir)

The preceding code indicates native source files location. Instruction $(call < function> ) allows evaluating a function and function my-dir returns the directory path of the last executed Makefile. Thus, as Makefiles usually share their directory with source files, this line is systematically written at the beginning of each Android.mk file to find their location.

include $(CLEAR_VARS)

Makes sure no "parasite" configuration disrupts compilation. When compiling an application, a few LOCAL_XXX variables need to be defined. The problem is that one module may define additional configuration settings (like a compilation MACRO or a flag) through these variables, which may not be needed by another module.

Keep your modules clean
To avoid any disruption, all necessary LOCAL_XXX variables should be cleared before any module is configured and compiled. Note that LOCAL_PATH is an exception to that rule and is never cleared out.

LOCAL_MODULE := mylib

The preceding line of code defines your module name. After compilation, the output library is named according to the LOCAL_MODULE variable flanked by a lib prefix and a .so suffix. This LOCAL_MODULE name is also used when a module depends on another module.

LOCAL_SRC_FILES := com_myproject_MyActivity.c

The preceding line of code indicates which source files to compile. File path is expressed relative to the LOCAL_PATH directory.

nclude $(BUILD_SHARED_LIBRARY)

This last instruction finally launches the compilation process and indicates which type of library to generate.

With Android NDK, it is possible to produce shared libraries (also called dynamic libraries, like DLL on Windows) as well as static libraries:

  • Shared libraries are a piece of executable loaded on demand. These are stored on disk and loaded to memory as a whole. Only shared libraries can be loaded directly from Java code.
  • Static libraries are embedded in a shared library during compilation. Binary code is copied into a final library, without regards to code duplication (if embedded by several different modules).

In contrast with shared libraries, static libraries can be stripped, which means that unnecessary symbols (like a function which is never called from the embedding library) are removed from the final binary. They make shared libraries bigger but "all-inclusive", without dependencies. This avoids the "DLL not found" syndrome well known on Window.

Shared vs. Static modules
Whether you should use a static or shared library depends on the context:

  • If a library is embedded in several other libraries
  • If almost all pieces of code are required to run
  • If a library needs to be selected dynamically at runtime
  • then consider turning it into a shared library because they avoid memory duplication (which is a very sensible issue on mobile devices). On the other hand:

  • If it is used in one or only a few places
  • If only part of its code is necessary to run
  • If loading it at the beginning of your application is not a concern
    then consider turning it into a static library instead. It can be reduced in size at compilation-time at the price of some possible duplication.

 

Compiling native code from eclipse You probably agree with me, writing code in Eclipse but compiling it by hand is not very satisfying. Although the ADT plugin does not provide any C/C++ support, Eclipse does this through CDT. Let's use it to turn our Android project into a hybrid Java-C/C++ project.

Time for action – creating a hybrid Java/C/C++ project

To check whether Eclipse compilation works fine, let's introduce surreptitiously an error inside the com_myproject_MyActivity.c file. For example:

#include "com_myproject_MyActivity.h"
private static final String = "An error here!";
JNIEXPORT jstring Java_com_myproject_MyActivity_getMyData
...

Now, let's compile MyProject with Eclipse:

  1. Open menu File | New | Other....
  2. Under C/C++, select Convert to a C/C++ Project and click on Next.
  3. Check MyProject, choose MakeFile project and Other Toolchain and finally click on Finish.
  4. Open C/C++ perspective when requested.
  5. Right-click on MyProject in Project explorer view and select Properties.
  6. In the C/C++ Build section, uncheck Use default build command and enter ndk- build as a Build command. Validate by clicking on OK:

    And... oops! An error got insidiously inside the code. An error? No we are not dreaming! Our Android project is compiling C/C++ code and parsing errors:

  7. Let's fix it by removing the incriminated line (underlined in red) and saving the file.
  8. Sadly, the error is not gone. This is because auto-build mode does not work. Go back to project properties, inside C/C++ Settings and then the Behaviour tab. Check Build on resource save and leave the value to all.
  9. Go to the Builders section and place CDT Builder right above Android Package Builder. Validate.
  10. Great! Error is gone. If you go to the Console view, you will see the result of ndk- build execution like if it was in command line. But now, we notice that the include statement of jni.h file is underlined in yellow. This is because it was not found by the CDT Indexer for code completion. Note that the compiler itself resolves them since there is no compilation error. Indeed, the indexer is not aware of NDK include paths, contrary to the NDK compiler.
  11. If warnings about the include file which the CDT Indexer could not find do not appear, go to C/C++ perspective, then right-click on the project name in the Project Explorer view and select Index/Search for Unresolved Includes item. The Search view appears with all unresolved inclusions.

  12. Let's go back to project properties one last time. Go to section C/C++ General/Paths and Symbols and then in Includes tab.
  13. Click on Add... and enter the path to the directory containing this include file which is located inside NDK's platforms directory. In our case, we use Android 2.3.3 (API level 9), so the path is ${env_var:ANDROID_NDK}/platforms/android-9/ arch-arm/usr/include. Environment variables are authorized and encouraged! Check Add to all languages and validate:

  14. Because jni.h includes some "core" include files (for example, stdarg.h), also add ${env_var:ANDROID_NDK}/toolchains/arm-linux- androideabi-4.4.3/prebuilt//lib/gcc/arm-linux- androideabi/4.4.3/include path and close the Properties window. When Eclipse proposes to rebuild its index, say Yes.
  15. Yellow lines are now gone. If you press Ctrl and click simultaneously on string.h, the file gets automatically opened. Your project is now fully integrated in Eclipse.

What just happened?

We managed to integrate Eclipse CDT plugin with an Android project using CDT conversion wizard. In a few clicks, we have turned a Java project into a hybrid Java/C/C++ project! By tweaking CDT project properties, we managed to launch ndk-build command to produce the library mylib defined in Android.mk. After getting compiled, this native library is packaged automatically into the final Android application by ADT.

Running javah automatically while building
If you do not want to bother executing manually javah each time native methods changes, you can create an Eclipse builder:

  1. Open your project Properties window and go to the Builder section.
  2. Click on New… and create a new builder of type Program.
  3. Enter configuration like done at step 8 with the External tool configuration.
  4. Validate and position it after Java Builder in the list (because JNI files are generated from Java .class files).
  5. Finally, move CDT Builder right after this new builder (and before Android Package Builder).

JNI header files will now be generated automatically each a time project is compiled.

 

In step 8 and 9, we enabled Building on resource save option. This allows automatic compilation to occur without human intervention, for example, when a save operation is triggered. This feature is really nice but can sometimes cause a build cycle: Eclipse keeps compiling code so we moved CDT Builder just before Android Package Builder, in step 9, to avoid Android Pre Compiler and Java Builder to triggering CDT uselessly. But this is not always enough and you should be prepared to deactivate it temporarily or definitely as soon as you are fed up!>

Automatic building
Build command invocation is performed automatically when a file is saved. This is practical but can be resource and time consuming and can cause some build cycle. That is why it is sometimes appropriate to deactivate the Build automatically option from main menu through Project. A new button: appears in the toolbar to trigger a build manually. You can then re-enable automatic building.

Summary Although setting up, packaging, and deploying an application project are not the most exciting tasks, but they cannot be avoided. Mastering them will allow being productive and focused on the real objective: producing code.

In this artilce, we have seen how to use NDK command tools to compile and deploy Android projects manually. This experience will be useful to make use of continuous integration in your project. We have also seen how to make both Java and C/C++ talk together in a single application using JNI. Finally we have created a hybrid Java/C/C++ project using Eclipse to develop more efficiently.

With this first experiment in mind, you got a good overview of how the NDK works.




Android NDK Beginner’s Guide

Android NDK Beginner’s Guide
Discover the native side of Android and inject the power of C/C++ in your applications with this book and ebook

Post a Comment
click to sign in

You are not logged in, your comment will be anonymous unless you join the community today (totally free - or sign in with your social account on the right) which we encourage all contributors to do.

2000 characters limit; HTML formatting and smileys are not supported - text only

Tutorial
Browse
Tutorials
Report Abuse
Report article
Related Engines
Custom Built
Custom Built Commercial Released Sep 1, 2007
Related Groups
Android Fans
Android Fans Hardware & Tech group with 371 members