Tuesday, July 17, 2012

Android NDK


The NDK is a toolset that allows you to implement parts of your app using native-code languages such as C and C++ (http://developer.android.com/tools/sdk/ndk/index.html)
You will also need Eclipse CDT Plugin installed to build C/C++ native part from Eclipse. To install it use menu Help -> Install New Software..., then setup CDT Main Features from http://download.eclipse.org/tools/cdt/releases/juno URL.
The Java Native Interface
 Programmer's Guide and Specification
(http://java.sun.com/docs/books/jni/html/start.html)
shows a simple example of using the Java Native Interface:


Let’s go through all these steps.

 

1.     Create a class that declares the native method

Let’s create simple HelloWorld Android application and modify HelloWorldActivity.java in the following way:
public class HelloWorldActivity extends Activity {

    public native String getMyData();

    static {
        System.loadLibrary("mylib");
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hello_world);

        setTitle(getMyData());
    }
}
In Eclipse, create a new directory named jni at the project's root using menu File | New | Folder.
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

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 helloworld.c:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := mylib
LOCAL_SRC_FILES := helloworld.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.

2.     Build project

Execute “Build Project” command to generate *.class files under HelloWorld/bin/classes directory

3.     Generate header file

-       Open project Properties window and go to the Builder section



-       Click on New… and create a new builder of type Program.


In Location select javah.exe file using Browse File System… button.
For Working Directory select your project bin/classes folder using Browse Workspace… button.
In Arguments add:
-o ${workspace_loc:/HelloWorld/jni}/helloworld.h julia.mlinnik.helloworld.HelloWorldActivity

such arguments will result in creating HelloWorld/jni/helloworld.h header.

You can use –d option instead of –o:
-d ${workspace_loc:/HelloWorld/jni} julia.mlinnik.helloworld.HelloWorldActivity

that will generate TestJni/jni/julia_mlinnik_helloworld_HelloWorldActivity.h header.

-       Validate and position it after Java Builder in the list (because JNI files are generated from Java .class files).
helloworld.h should have the following content:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class julia_mlinnik_helloworld_HelloWorldActivity */

#ifndef _Included_julia_mlinnik_helloworld_HelloWorldActivity
#define _Included_julia_mlinnik_helloworld_HelloWorldActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     julia_mlinnik_helloworld_HelloWorldActivity
 * Method:    getMyData
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_julia_mlinnik_helloworld_HelloWorldActivity_getMyData
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

4.    Write the C implementation of the native method
Create helloworld.c file under jni directory:
#include "helloworld.h"
JNIEXPORT jstring Java_julia_mlinnik_helloworld_HelloWorldActivity_getMyData
    (JNIEnv* pEnv, jobject pThis)
{
    return (*pEnv)->NewStringUTF(pEnv,
                                 "My native project talks C++");
}

5.    Compile C code and generate native library
Let’s convert our project to C/C++ project. Open menu File | New | Other....


Check your project, choose MakeFile project and Other Toolchain and finally click on Finish.





Open your project Properties. In the C/C++ Build section, uncheck Use default build command and enter ${NDKROOT}/ndk-build.cmd as a Build command. NDKROOT is environment variable containing the path to Android NDK in your system.
Validate by clicking on OK:

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.

Go to section C/C++ General/Paths and Symbols and then in Includes tab.

Click on Add... and enter the path to the directory containing this include file which is located inside NDK's platforms directory:

${NDKROOT}/platforms/android-9/arch-arm/usr/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/include

${NDKROOT}/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/lib/gcc/arm-linux-androideabi/4.4.3/include
${ProjDirPath}/../../sdk/native/jni/include

 When Eclipse proposes to rebuild its index, say Yes





Finally check that Builders have the following order:
Main idea:
javah after Java Builder
CDT Builder after javah and before Android Package Build
6.    Run application
After all you see the following screen:





4 comments:

  1. Hi Julia, are you able to do step/line debugging of your C/C++ Android app code from eclipse? That would be a great blog post.

    ReplyDelete
    Replies
    1. Hi michels, there is detailed description about debugging Android application from Eclipse under Linux OS in this book - http://www.packtpub.com/android-ndk-beginners-guide/book

      For Windows ndk-gdb script could be run under Cygwin, and I don't know how to debug from Eclipse in this case

      Delete
    2. I'm very surprised still google has not made debugging a priority, but considering the various operating systems for development, differences in eclipse versions, and various ndk versions and toolchains - it's a large set of variables. It is still surprising there is not a "de facto" document describing and showing how to do step debugging, I think a lot of people would read it. I am using Eclipse Juno and Mac OS X by the way.

      Delete
  2. Hi
    any news about native JNI C debug step by step in eclipse juno?
    THANKS to all!

    ReplyDelete