android - files.meetup.comfiles.meetup.com/1698110/capers - android.pdf• many android developers...
TRANSCRIPT
Developing performance critical components using the Android NDK
ANDROID
HELLO WORLDC/C++
Java Native Interface
WHAT IS SNORKEL-EMBEDDED?
Web application development API written in C/C++
TODAYS WEB APPLICATIONS
• Broken into logical chunks called tiers
• Presentation tier
• Client browser
• Explorer, Chrome, Firefox, Safari, etc….
• Application tier (application logic)
• Web Server
• Apache, Tomcat, IIS, etc…
• Server side dynamic content engine
• ASP, ASP.NET, CGI, ColdFusion, JSP/Java, PHP, Perl, Python, Ruby on Rails or Struts2, etc…
• Database tier
• SQL, Oracle, proprietary,…
THE APPLICATION TIER CONSISTS OF LOOSELY
COBBLED TOGETHER BLOCKS OF STUFF
Web Server
Servlet Container
CGI
PHPFile System
TODAYS WEB APPLICATIONS ARE COMPLEX
• Over componentized
• To many moving parts
• To many things that can break
• Have obfuscated away the underlying hardware
• Difficult to debug and develop
• Suffer from code bloat – trying to be everything to everyone
• Require beefy systems to run
• Employ complex frameworks that are equally complex to configure for the average user
• Not conducive to smaller devices with limited resources
• Printers, Cameras, toasters,…, appliances, Android devices, etc…
• More resources equals shorter battery life and less resources to run other things
• Layered with prerequisites and third party dependencies
• Are not easily implemented in native solutions
• C/C++ based applications
Todays
Applications
“Everything should be made as simple as possible,
but not simpler”
Albert Einstein
ONE SOLUTION, ONE EXECUTABLE EQUALS
SNORKEL-EMBEDDED
Web Application
File System
Web Server
Servlet Container
CGI
PHPFile System
ADVANTAGES OF SNORKEL-EMBEDDED
• Written in C
• Extremely portable
• Compile everywhere, debug once
• Written using cross platform standard APIs
• Shorter development cycles
• Existing native code can be reused.
• The Snorkel API is easy to learn and the average developer can begin implementing
Snorkel with in a couple of days.
• Web applications are written within weeks instead of months.
• No third party dependencies = easier configuration and installation
ADVANTAGES OF SNORKEL -- CONTINUED
• Snorkel based web applications require far fewer resources to run
• Leverages the latest system architectures such as NUMA
• Reduced context switching
• Less page faulting
• Does more with far fewer threads (4 threads = 200 concurrent users)
• Lockless memory management
• Employs memory segregation which provides efficient garbage collection
• Outperforms Apache, IIS, and Tomcat for both static and dynamic content delivery.
• Supports not only HTTP/HTTPS but also proprietary protocol development
GOING NATIVE ON ANDROID
WHY NATIVE?
• In many cases well written native components in C are faster, leaner, and more efficient
than their Java based counterparts.
• Porting existing C/C++ code to Android instead of converting it to JAVA means getting to
market faster.
• Going forward beginning with Android (Gingerbread) Google has beefed up the NDK and
added a NativeActivity class as well as a new helper class for native development to
attract more developers.
• Soon, developers will be able to choose to work with JAVA mixed with C/C++, pure
JAVA, or pure C/C++.
WHAT’S THE BEST APPROACH FOR
IMPLEMENTING NATIVE CODE
• Gingerbread is not widely supported yet
• Nexus S
• JNI is still the practiced approach
• Allows you to keep your core code in C/C++
• Allows you to quickly migrate to other non-JAVA or JAVA based platforms that are
also based on a Linux Kernel
• Many Android developers have taken this approach – writing a small JNI wrapper to
handle lifecycle management.
• Example: Angry Birds
A CLOSER LOOK AT JNI
C Component
Java Libraries
Your Java Class
Java Virtual
Machine
Your class
methods
A CLOSER LOOK AT JNI
Java Program
C Routine
C++ Class
C Debugger
A CLOSER LOOK AT JNI
Functions
Libraries
Exceptions
Classes
JVM
C/C++ Java
JNI
PREREQUISITES FOR NATIVE DEVELOPMENT
USING WINDOWS
• Windows XP (32-bit) or Vista (32 or 64-bit)
• JDK 1.6 or higher
• Cygwin 1.7 or higher
• GNU Awk or Nawk
• GNU Make 3.81 or higher
• Eclipse (Galileo) or higher
• Helios
• http://developer.android.com/sdk/ndk/overview.html
LEARNING BY EXAMPLEA BETTER HELLO WORLD
HELLO
WORLD
CREATING AN ANDROID PROJECT
API LEVEL
Platform Version API Level
Android 3.0 11
Android 2.3.3 10
Android 2.3 9
Android 2.2 8
Android 2.1 7
Android 2.0.1 6
Android 2.0 5
Android 1.6 4
Android 1.5 3
Android 1.1 2
Android 1.0 1
MAIN.XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
STRINGS.XML
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, MywebActivity!</string>
<string name="app_name">Myweb</string>
</resources>
MYWEBACTIVITY.JAVA
package com.mycompany.myweb;
import android.app.Activity;
import android.os.Bundle;
public class MywebActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
DESIGNING A UI
TOOLS FOR UI DESIGN
• The Eclipse IDE – graphical layout tool
• Droid draw http://www.droiddraw.org/
WRITING THE JAVA WRAPPER CLASS
Start
Stop
Status
Error
Messages
WRITING THE JAVA WRAPPER CLASS
package com.mycompany.myweb;
public class Server {
static final int RUNNING=1;
static final int DOWN=0;
static final int SNORKEL_SUCCESS=0;
static final int SNORKEL_ERROR=-1;
static {
System.loadLibrary("snorkel32");
System.loadLibrary("Server");
}
public native String lastError ();
public native int isRunning ();
public native int start (int port,
String rootDir,
int showDir);
public native int stop ();
}
INTERFACING WITH C
lastError
isRunning
start
Server Class
JNI
Functions?
libsnorkel32.so
C – libServer.so
stop
JNI NAMING CONVENTIONS
JNIEXPORT return_type JNICALL Java_package_ClassName_MethodName
(JNIEnv *env, jobject obj, arg0, arg1, arg2,…, argn)
Java_com_mycompany_myweb_Server_MethodName where
com_mycompany_myweb_Server identifies the class com.mycompany.myweb.Server
Must include <jni.h>
THE LASTERROR METHOD
Server.lastError = Java_com_mycompany_myweb_Server_lastError
JNIEXPORT jstring JNICALL
Java_com_mycompany_myweb_Server_lastError (
JNIEnv *env,
jobject javaThis)
{
return (*env)->NewStringUTF (env, snrkl_lerr ());
}
• JNIEnv allows access to all JAVA classes from C/C++
• jobect allows C/C++ to access our JAVA class methods
THE ISRUNNING METHOD
JNIEXPORT jint JNICALL
Java_com_mycompany_myweb_Server_isRunning (
JNIEnv *env,
jobject javaThis)
{
if (g_this_server)
return RUNNING;
return DOWN;
}
THE STOP METHOD
JNIEXPORT void JNICALL
Java_com_mycompany_myweb_Server_stop (
JNIEnv *env,
jobject javaThis)
{
if (g_this_server)
snrkl_destroy (g_this_server);
g_this_server = 0;
}
THE START METHOD
JNIEXPORT jint JNICALL
Java_com_mycompany_myweb_Server_start (
JNIEnv *env,
jobject javaThis,
jint port,
jstring rootString,
jint showDir)
{
const char *pszroot =
(*env)->GetStringUTFChars (env, rootString, 0);
if (g_this_server)
return SNORKEL_SUCCESS;
THE START METHOD – CONTINUED
if (snrkl_init () != SNORKEL_SUCCESS)
return SNORKEL_ERROR;
g_this_server = srnkl_server (2, pszroot);
(*env)->ReleaseStringChars (env, pszroot,
(const jchar *)pszroot);
if ( !g_this_server)
return SNORKEL_ERROR;
if (showDir)
snrkl_srvset_show_dir (g_this_server, 1);
THE START METHOD – CONTINUED
if (snrkl_srvadd_listener (g_this_server, port, 0)
== SNORKEL_ERROR)
{
snrkl_destroy (g_this_server);
g_this_server = 0;
return SNORKEL_ERROR;
}
/*
* disable IPV6 - not supported on Android yet
*/
snorkel_obj_set (g_this_server, snorkel_attrib_ipvers,
IPVERS_IPV4, SOCK_SET);
THE START METHOD – CONTINUED
if (snrkl_start (g_this_server) != SNORKEL_SUCCESS)
{
snrkl_destroy (g_this_server);
g_this_server = 0;
return SNORKEL_ERROR;
}
return SNORKEL_SUCCESS;
}
TYING IN THE SERVER CLASS TO THE MAIN
ACTIVITY CLASS
public class MywebActivity extends Activity {
Server m_server;
.
.
public void onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
m_server = new Server();
.
.
}
BUILDING THE NATIVE COMPONENT
lastError
isRunning
start
Server Class
JNI
Java..lastError
libsnorkel32.so
C – libServer.so
stop
Java..isRunning
Java..start
Java..stop
SETTING UP FOR NDK-BUILD
• NDK-BUILD is a build system for building Android native code
• Script that wraps the GCC compiler
• The easiest way to build native code
• Requires C/C++ source files to be located in the “jni” subdirectory of the current project.
• Operates on NDK build make files located in the “jni” subdirectory
• Android.mk
• Describes native sources to the NDK build system
• Application.mk
• Allows you to specify build type (release, debug)
• Targeted chipsets
• Usually run from within a Cgywin command shell
• Can be configured as part of the build process in Eclipse http://mobilepearls.com/labs/ndk-builder-in-eclipse/
ANDROID.MK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := snorkel32
LOCAL_SRC_FILES := snorkel/libsnorkel32.so
LOCAL_EXPORT_C_INCLUDES:=snorkel
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := Server
LOCAL_SRC_FILES := Server.c
LOCAL_SHARED_LIBRARIES := snorkel32
include $(BUILD_SHARED_LIBRARY)
libsnorkel32.so
libServer.so
TARGETING CHIPSETS/TOOLCHAINS FOR
OPTIMIZATION
• Default tool-chain is armeabi
• To date all released phones are based on ARM architecture
• Should be avoided unless there are significant performance gains on targeted devices
• Increases deployment size, since you have to include basic arm support along with
the optimized implementation
• Additional toolchains are specified in the Application.mk file
APPLICATION.MK
APP_OPTIM := release
APP_ABI := armeabi armeabi-v7a
TARGET_CPU_ABI := armeabi-v7a
TARGET_CPU_ABI2 := armeab
USING NDK-BUILD
• Run from Cygwin command prompt in “jni” directory
• When run without command line options builds a release build
• Located in the NDK directory “installation_directory/android-ndk-r5”
$ /home/android-ndk-r5/ndk-build
Compile thumb : Server <= Server.c
Prebuilt : libsnorkel32.so <= jni/snorkel/
SharedLibrary : libServer.so
Install : libServer.so => libs/armeabi/libServer.so
Install : libsnorkel32.so => libs/armeabi/libsnorkel32.so
REUSING NATIVE RUNTIMES OR THIRD PARTY
LIBRARIES
MyWeb
libsnorkel32.so
libServer.so
DEBUGGING NATIVE CODE
• Compile with debug option
• Change or add “APP_OPTIM := debug” to Application.mk file
• Set debuggable to true in manifest.xml
• Run NDK-BUILD with debug option “NDK-DEBUG=1”
• No easy way to debug from within IDE
• Sequoyah http://www.eclipse.org/sequoyah/documentation/native_debug.php
• DDD, GDB
• ndk-gdb
• DDMS from within Eclipse and arm-eabi-addr2line.exe
USING DDMS AND ADDR2LINE TO IDENTIFY THE
LOCATION OF A CRASH
• Trap the crash using logcat from within adb by issuing the command:
adb logcat
• Trap crash from within the DDMS view in Eclipse which has a logcat window opened by
default.
OUTPUT FROM LOGCAT
I/ServiceManager( 417): Executing: /android/bin/app_process (link=/android/bin/app_process, wrapper=/android/bin/app_process)
I/DEBUG: -- observer of pid 417 starting --
I/appproc ( 417): App process is starting with pid=417, class=android/activity/ActivityThread.
I/DEBUG: -- observer of pid 417 exiting --
I/DEBUG: -- observer of pid 420 starting --
I/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG: pid: 373, tid: 401 >>> android.content.providers.pim <<<
I/DEBUG: signal 11 (SIGSEGV), fault addr 00000000
I/DEBUG: r0 ffffffff r1 00000000 r2 00000454 r3 002136d4
I/DEBUG: r4 002136c0 r5 40804810 r6 0022dc70 r7 00000010
I/DEBUG: r8 0020a258 r9 00000014 10 6b039074 fp 109ffcf8
I/DEBUG: ip 6b039e90 sp 109ffc0c lr 580239f0 pc 6b0156a0
I/DEBUG: #01 pc 6b0156a0 /android/lib/libjamvm.so
I/DEBUG: #01 lr 580239f0
ADDR2LINE SCRIPT
@ECHO OFF
C:\cygwin\home\android-ndk-r5\toolchains\arm-eabi-4.4.0\prebuilt\windows\bin\arm-eabi-
addr2line.exe -f -e obj\local\armeabi\lib%1.so 0x%2
IDENTIFYING THE LINE AND FILE THAT CRASH
OCCURS
>> addr2line snorkel32 0000acdc
buffer_vprintf
C:/cygwin/home/PFHWEC0/android_workspace/sailfish/jni/snorkel.c:6068
QUESTIONS
• More about JNI http://java.sun.com/docs/books/jni/
• WINGDB plugin for debugging native Android and other mobile type native components in
Visual Studio
• http://www.wingdb.com/wgMobileEdition.htm
• http://ian-ni-lewis.blogspot.com/2011/01/its-like-coming-home-again.html
• The SnorkelEMBEDDED project: www.snorkelembedded.webs.com