Capers - Android - Meetup

estrapadetubacityMobile - Wireless

Dec 10, 2013 (3 years and 6 months ago)

119 views

Developing performance critical components using the Android NDK
ANDROID
HELLO WORLD
C/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
PHP
File 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
PHP
File 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 EXAMPLE
A 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?
l
ibsnorkel32.so
C

libServer.so
stop
JNI NAMING CONVENTIONS
JNIEXPORT
return_type
JNICALL
Java_
package_ClassName_MethodName
(
JNIEnv
*
env
,
jobject
obj
, arg
0
, arg
1
, arg
2
,…,
arg
n
)
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
l
ibsnorkel32.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)
l
ibsnorkel32.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
C
ygwin 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
l
ibsnorkel32.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

n
dk
-
gdb

DDMS from within Eclipse and arm
-
eabi
-
addr2line.exe
USING DDMS AND ADDR2LINE TO IDENTIFY THE
LOCATION OF A CRASH

T
rap 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
Snorkel
EMBEDDED
project:
www.snorkelembedded.webs.com