Objective
This article is a beginner’s introduction to creating native (NDK based) Android* applications for Intel® architecture (IA) based devices. We will also discuss porting NDK Android apps to IA based devices that were created for other architecture based devices. We will walk through two scenarios, one to demonstrate the process of creating basic NDK based Android* App from start to finish, and another to showcase a simple process of porting an existing NDK based Android App to use IA based devices.
Table of Contents
1. Introduction
2. Creating NDK based Android App for IA based devices - Walkthrough of a simple App
a. Creating Default Android Project
b. Invoking native code from Java Sources
c. Using javah to generate JNI header stubs for native code
d. Building native code with NDK for IA
e. Rebuilding, Installing and Running the Android NDK App for IA
3. Using x86 NDK toolset to port existing NDK Apps to IA based devices
4. Summary
Introduction
Android applications can incorporate native code using the Native Development Kit (NDK) toolset. It allows developers to reuse legacy code, code to low-level hardware, or differentiate their apps by taking advantage of features otherwise not optimal or possible.
This article is a basic introduction on how to create NDK based apps for IA from start to finish, and also on simple use cases for porting existing NDK based apps to IA based devices. We will walk through a simple step-by-step app development scenario to demonstrate the process.
We assume you have already installed the Android development environment including the Android SDK, Android NDK and configured emulator-x86 to test the apps. Please refer to the Android Community section on Intel website for more information. To keep our development environment simple, we will use Linux* command line tools for the most part.
Creating NDK based Android App for IA based devices – Walkthrough of a simple App
Let’s assume we have some legacy code that uses C and assembly language to parse CPUID (please refer to http://en.wikipedia.org/wiki/CPUID* for more on CPUID). Below is source listing for our "legacy" sample C code cpuid.c (demo purposes only).
We would like call cpuid_parse from our Android App (demo purpose only - cpuid_parse function expects pre-allocated buffer) and display the output inside the App.
Following below is a step-by-step walk-through to create an Android App from start to finish, and to use the above legacy native code.
1. Creating Default Android Project
Android SDK has command line tools to generate default project structure for a typical hello world app. We will first create a default project, and then modify the java sources to add JNI calls and native code.
In the above screenshot, we first created a directory called labs/lab2 and used "android" command line tool to generate the default project. We specified android-15 as the API level, and named our app as "CPUIdApp" with package com.example.cpuid.
We then used the "ant" command line tool to build the project in debug mode and install using "adb" (or reinstall if exists on emulator or target). We assume you have already have an emulator or a device attached and it is the only listed device in "adb devices" command output.
Below is a screenshot of the Android x86 emulator with ICS after completing the above process.
Clicking on the App, you can see the default hello world output of the app. We will now modify the app to use native code.
2. Invoking Native code from java sources
The default android project generates the java sources for a typical hello world project with give package namespace (eg: com.example.cpuid). The screenshot below shows the source code generated for the main java source file.
To use native c/c++ code in our java source file, we first need to declare JNI call and load the native library, as highlighted yellow box in the screenshot below.
As seen in the declaration, the native call returns a java string which we can use anywhere in our java source. As shown in above screenshot, we modified the TextView to display the string we get from the native call. This is highlighted in red in the box.
This is a very simple case for declaring and using JNI native calls in Android App Java sources. Next, we will use "javah" tool to generate the JNI header stubs for native code, and add or modify native code to comply with JNI native headers.
3. Using "javah" to generate JNI header stubs for native code
We now have to modify our native code to comply with JNI call specification. "javah" too helps us automatically generate the appropriate JNI header stubs based on the Java source files. The "javah" tool requires the compiled Java class file for generating the headers. So we use the "ant" tool to quickly generate java class files as shown in below screenshot ("ant debug").
Use "javah" to generate the jni header as shown in the screenshot (second yellow highlight box). It will create a directory "jni" and the header stub based on Java class. The screenshot below shows the generated JNI native header stub.
Create corresponding C source file ("com_example_cpuid_CPUIdApp.c") for the above generated header. Following below is the source listing :
We call the native code cpuid_parse and return the parsed buffer as JNI string. We are now ready to compile the native code using the x86 NDK toolset.
4. Building native code with NDK for x86
Please refer to the Android community section on Intel website (http://software.intel.com/en-us/android/articles/ndk-for-ia) for more information on NDK for IA installation and usage.
Android NDK toolset uses a build system, which requires a custom android specific make file "Android.mk" to be present in the "jni" folder of the project to compile native code. Android.mk specifies all the native C/C++ source files to be compiled, the headers and the type of build (eg: shared_library).
Below is the native code Android make listing for our project ("jni/Android.mk")
This is a simple scenario with 2 C source files and specifies to build a shared library.
We can now issue "ndk-build APP_ABI=x86" to build our native code, and generate the shared lib. Android build system also provides another supplementary make file "Application.mk", which we can use to specify additional configuration options. For example, we can specify all the supported ABIs in the Application.mk file, and the ndk-build will generate native shared libraries to target all architectures.
The screenshot above shows the successful compile of native code for x86, and also shows shared library being generated and installed. We are now ready to rebuild our Android App, and install/run on x86 emulator or target device.
5. Rebuilding, Installing and Running the Android NDK App for IA
We can use "ant debug clean" to clear our old build files, and issue "ant debug" again to start off full rebuild of android project. Use "adb" to reinstall the app on target device or x86 emulator as shown in the screenshot below.
The screenshot below shows the App icon inside the x86 emulator, and the result of running the app inside the x86 emulator.
We have successfully built NDK based android app start to finish.
Using x86 NDK toolset to port existing NDK Apps to IA based devices
Android Apps with native code typically have a standard project structure, with "jni" folder containing the native sources, and the corresponding Android.mk/Application.mk build files. In the previous section, we saw a simple example with native source code and corresponding Android.mk file.
Android NDK toolset lets us specify all the target ABIs in Application.mk at once, and automatically generates native shared libraries for all targets. The android build system will automatically package all the target native libraries inside the APK, and at install time the Android package manager will install only the appropriate native library based on the target architecture.
We can invoke "ndk-build" or specify in Application.mk
APP_ABI := all
OR
APP_ABI := armeabi armeabi-v7a x86
Please refer to http://developer.android.com/sdk/ndk/index.html for more information.
To port an existing Android App with native code, which currently does not target x86, the process of modifying the App to support IA is straightforward for most cases (as discussed above) - Unless the app uses architecture specific assembly language or constructs. There may be other issues like memory alignment or platform specific instructions usage. Please refer to http://software.intel.com/en-us/android/articles/ndk-android-application-porting-methodologies for more details.
Summary
This article discussed creating and porting NDK based Android Apps to target IA. We walked-through step-by-step process to demonstrate the process of creating the NDK based App to use IA start-to-finish. We also discussed simple process made available by NDK toolset to port existing NDK based Android Apps to target IA as well.
Notices
Intel is a trademark of Intel Corporation in the U.S. and other countries
INFORMATION IN THIS DOCUMENT IS PROVIDED IN CONNECTION WITH INTEL PRODUCTS. NO LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY INTELLECTUAL PROPERTY RIGHTS IS GRANTED BY THIS DOCUMENT. EXCEPT AS PROVIDED IN INTEL'S TERMS AND CONDITIONS OF SALE FOR SUCH PRODUCTS, INTEL ASSUMES NO LIABILITY WHATSOEVER AND INTEL DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY, RELATING TO SALE AND/OR USE OF INTEL PRODUCTS INCLUDING LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR INFRINGEMENT OF ANY PATENT, COPYRIGHT OR OTHER INTELLECTUAL PROPERTY RIGHT.
A "Mission Critical Application" is any application in which failure of the Intel Product could result, directly or indirectly, in personal injury or death. SHOULD YOU PURCHASE OR USE INTEL'S PRODUCTS FOR ANY SUCH MISSION CRITICAL APPLICATION, YOU SHALL INDEMNIFY AND HOLD INTEL AND ITS SUBSIDIARIES, SUBCONTRACTORS AND AFFILIATES, AND THE DIRECTORS, OFFICERS, AND EMPLOYEES OF EACH, HARMLESS AGAINST ALL CLAIMS COSTS, DAMAGES, AND EXPENSES AND REASONABLE ATTORNEYS' FEES ARISING OUT OF, DIRECTLY OR INDIRECTLY, ANY CLAIM OF PRODUCT LIABILITY, PERSONAL INJURY, OR DEATH ARISING IN ANY WAY OUT OF SUCH MISSION CRITICAL APPLICATION, WHETHER OR NOT INTEL OR ITS SUBCONTRACTOR WAS NEGLIGENT IN THE DESIGN, MANUFACTURE, OR WARNING OF THE INTEL PRODUCT OR ANY OF ITS PARTS.
Intel may make changes to specifications and product descriptions at any time, without notice. Designers must not rely on the absence or characteristics of any features or instructions marked "reserved" or "undefined". Intel reserves these for future definition and shall have no responsibility whatsoever for conflicts or incompatibilities arising from future changes to them. The information here is subject to change without notice. Do not finalize a design with this information.
The products described in this document may contain design defects or errors known as errata which may cause the product to deviate from published specifications. Current characterized errata are available on request.
Contact your local Intel sales office or your distributor to obtain the latest specifications and before placing your product order.
Copies of documents which have an order number and are referenced in this document, or other Intel literature, may be obtained by calling 1-800-548-4725, or go to: http://www.intel.com/design/literature.htm.
* Other names and brands may be claimed as the property of others.
Copyright © 2012 Intel Corporation. All rights reserved