Friday 20 May 2016

HB Blog 111: NDK - Hello World Tutorial.

The Native Development Kit (NDK) is a set of tools that allow you to leverage C and C++ code in your Android apps. You can use it either to build from your own source code, or to take advantage of existing prebuilt libraries. However, it can be useful in cases in which you need to,
  • Squeeze extra performance out of a device for computationally intensive applications like games or physics simulations.
  • Reuse your own or other developers' C or C++ libraries.
In this post, I will explain how to setup NDK as well as run a Hello World program. It's pretty simple.
Basically, installing and setting up the NDK is similar to our normal Android SDK. 
To install and configure the NDK, follow these steps,
  1. Get and install the Android SDK.
  2. Download and extract the NDK, making sure to download the correct version for your development platform. You may place the unzipped directory anywhere on your local drive.
  3. Update your PATH environment variable with the location of the directory that contains the NDK.
  4. Set ndk.dir path in local.properties.
  5. Finally, run normal gradle sync to check it all files are installed clearly. 
There are 2 important files that plays a role in building our NDK application namely, Android.mk and Application.mk. They are actually the make files. Android.mk files defines properties specific to individual modules, or libraries. Whereas, Application.mk defines properties for all the modules that you use in your app.

Android.mk: - The Android.mk file resides in a subdirectory of your project's jni/ directory, and describes your sources and shared libraries to the build system. The syntax of the Android.mk allows you to group your sources into modules. A module is either a static library, a shared library, or a standalone executable. Below is a sample file,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Defines the root to all other relative paths
# The macro function my-dir, provided by the build system,
# specifies the path of the current directory (i.e. the
# directory containing the Android.mk file itself)
LOCAL_PATH := $(call my-dir)

# Clear all LOCAL_XXX variables with the exception of
# LOCAL_PATH (this is needed because all variables are global)
include $(CLEAR_VARS)

# List all of our C/C++ files to be compiled (header file
# dependencies are automatically computed)
LOCAL_SRC_FILES := HelloWorldC.c

# The name of our shared module (this name will be prepended
# by lib and postfixed by .so)
LOCAL_MODULE := helloworld

# We need to tell the linker about our use of the liblog.so
LOCAL_LDLIBS += -llog

# Collects all LOCAL_XXX variables since "include $(CLEAR_VARS)"
#  and determines what to build (in this case a shared library)
include $(BUILD_SHARED_LIBRARY)

Application.mk: - The Application.mk file is really a tiny GNU Makefile fragment that defines several variables for compilation. It usually resides under $PROJECT/jni/, where $PROJECT points to your application's project directory. Below is a sample file,
1
2
3
4
5
6
7
8
ANDROID_JPEG_NO_ASSEMBLER := true
APP_PLATFORM := android-9
APP_STL := gnustl_static #gnustl_shared
# ARMv7 is significanly faster due to the use of the hardware FPU
APP_ABI := armeabi armeabi-v7a x86 
APP_OPTIM := release
#APP_CPPFLAGS += -frtti
APP_BUILD_SCRIPT := Android.mk

Now, for creating our Hello World application we need to add this Android.mk and Application.mk make files in jni folder under main folder.We need C or C++ files to be added into the same jni folder. We need header and C main file as per our program functionality. I will show a sample code to display HelloWorld message from C program into java textview. Then, run "javah" utility for compiling this C files, which produces shared output(.so) files in libs folder based on CPUs and Architectures defined in our make files.

Refer the below link for complete sample code:-

Download Sample Code

Have a look on few code snippets,

//HelloWorldC.h
1
2
3
4
5
6
7
8
//
// Created by harshal.benake on 27-04-2016.
//

#ifndef NDKHELLOWORLD_HELLOWORLDC_H
#define NDKHELLOWORLD_HELLOWORLDC_H
JNIEXPORT jstring JNICALL Java_com_example_harshalbenake_ndkhelloworld_MainActivity_getMessage(JNIEnv *, jobject);
#endif //NDKHELLOWORLD_HELLOWORLDC_H

//HelloWorldC.c
1
2
3
4
5
6
7
#include <jni.h>
#include <string.h>
#include "HelloWorldC.h"

JNIEXPORT jstring Java_com_example_harshalbenake_ndkhelloworld_MainActivity_getMessage(JNIEnv *env, jobject thisObj) {
   return (*env)->NewStringUTF(env, "Hello World from native code running successfully");
}

//MainActivity.java 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.harshalbenake.ndkhelloworld;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {
    static {
        System.loadLibrary("helloworld"); // "helloworldjni.dll" in Windows, "libhelloworldjni.so" in Unixes
    }

    // A native method that returns a Java String to be displayed on the
    // TextView
    public native String getMessage();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Create a TextView.
        TextView textView = new TextView(this);
        // Retrieve the text from native method getMessage()
        textView.setText(getMessage());
        setContentView(textView);
    }
}

No comments:

Post a Comment