Getting started with the NDK

KirillKounik 701 views 30 slides Nov 09, 2016
Slide 1
Slide 1 of 30
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30

About This Presentation

Presentation that I did at Tikal's FullStack developers meetup


Slide Content

Getting Started with the NDK Native Development Kit (NDK) Kirill Kounik

Agenda Introduction: What/Why NDK ABIs - Architectures and CPUs NDK and Project setup Libraries Debugging

Native Development for Android Natural/native language of Android development is Java Android runs Linux kernel at its core C/C++ development is possible, actually an Android application can be written entirely in C or C++ Leverages standard Java Native Interface (JNI) However it will not benefit most apps

Benefits of Native Development for Android Mostly useful for computation intensive apps where performance becomes an issue May help to port or share code with other platforms, like iOS Use external native libraries Sometimes allows access to APIs not exposed in Java layer

NDK development Cons: Requires compilation for every CPU architecture and, possibly, a separate APK for each architecture. May significantly increase APK size JNI development is cumbersome for Java developers

Native Development Kit or NDK for Android NDK is a set of tools or toolchain used for for native development for Android platform. http://developer.android.com/ndk/index.html Samples* https://github.com/googlesamples/android-ndk

ABI is Application Binary Interface Different Android handsets use different CPUs, which in turn support different instruction sets. Each combination of CPU and instruction sets has its own Application Binary Interface, or ABI . armeabi armeabi-v7a arm64-v8a x86 x86_64 m ips mips64

APK structure with native code $ unzip -l build/outputs/apk/app-all-debug.apk Archive: build/outputs/apk/app-all-debug.apk Length Date Time Name --------- ---------- ----- ---- 1896 03-31-2016 14:10 AndroidManifest.xml ... 2724 03-31-2016 14:10 classes.dex 5592 03-31-2016 14:10 lib/arm64-v8a/libhello-jni.so 13552 03-31-2016 14:10 lib/armeabi/libhello-jni.so 13560 03-31-2016 14:10 lib/armeabi-v7a/libhello-jni.so 5460 03-31-2016 14:10 lib/mips/libhello-jni.so 6048 03-31-2016 14:10 lib/mips64/libhello-jni.so 5264 03-31-2016 14:10 lib/x86/libhello-jni.so 5784 03-31-2016 14:10 lib/x86_64/libhello-jni.so ... --------- ------- 81286 17 files

NDK and project setup

NDK setup NDK setup is very straight-forward: [Install Android Studio] Download and unzip NDK into a convenient location or let Android Studio do it for you Optionally get the samples from Github, they are not included in the NDK https://github.com/googlesamples/android-ndk * * Look for various branches corresponding to build methods

Several ways to build native code Using ndk -build or cmake and externalNativeBuild { } block in build.gradle (starting with Android Studio 2.2) Use if you are porting older or very complex project or familiar with GNU make or cmake Using experimental Gradle plugin – new build system Recommended for new projects, doesn’t require external build tool knowledge Use toolchain directly Use you favorite build tools

External Native Build – ndk -build Define Android.mk file location Pass optional ndk -build parameters Define required ABIs externalNativeBuild { ndkBuild { path “Android.mk” abiFilters 'armeabi-v7a‘, 'arm64-v8a', 'x86', 'x86_64' arguments 'NDK_DEBUG=0', '-j4', 'ANDROID_TOOLCHAIN=clang' } }

Experimental plugin - Project structure Starting from version 1.3 Android Studio supports NDK development with the relatively new 'com.android.model.application' gradle plugin .C, .CPP files under jni folder at the same level as java *Old toolchain based on makefiles and ndk-build is not covered by this training.

Project structure Build settings defined in android.ndk section of the build.gradle file, for example model { ... android.ndk { moduleName = "native-codec-jni" cppFlags.add( "-UNDEBUG" ) // for native multimedia ldLibs.addAll([ "OpenMAXAL" , "mediandk" ]) // for logging ldLibs.add( "log" ) // for native windows ldLibs.add( "android" ) stl = "stlport_static" } ... }

Everything you can configure in android.ndk android.ndk { // All configurations that can be changed in android.ndk. moduleName = "mymodule" ldLibs.addAll(['log', 'android']) ldLibs.add("log") ldFlags.add("-L/custom/lib/path") ... }

Everything you can configure in android.ndk 2 android.ndk { // All configurations that can be changed in android.ndk. ... abiFilters.add("x86") // List of target ABIs CFlags.add("-DCUSTOM_DEFINE") cppFlags.add("-DCUSTOM_DEFINE") debuggable = false renderscriptNdkMode = false stl = "stlport_static" // choice of c++ runtimes provided platformVersion = 15 } http://tools.android.com/tech-docs/new-build-system/gradle-experimental#TOC-Ndk-Integration

Define what ABIs to package in the APK

Or Split APKs In native code causes an APK too large. To build separate APK for each architecture android { ... splits { abi { enable true reset() include 'x86', 'armeabi-v7a', 'mips' // ABIs to include universalApk true // build “fat” version } } }

Split APKs - version code support Every APK in Play store must have unique versionCode // map for the version code project.ext.versionCodes = ['armeabi-v7a':1, 'x86':2, 'mips':3] applicationVariants.all { variant -> // assign different version code for each output variant.outputs.each { output -> output.versionCodeOverride = project.ext.versionCodes.get(output.getFilter(OutputFile. ABI )) * 10000 + variant.versionCode } } * http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits

C++ runtimes ( stl = "stlport_static" ) the build system automatically links the standard C libraries, real-time extensions, and pthread By default NDK provides a very minimal standard C++ runtime support library (libstdc++). This minimal support does not include, for example: Standard C++ Library support (except a few trivial headers). C++ exceptions support RTTI support Available runtimes: GAbi++, STLport, GNU STL, LLVM libc++ Detailed information about various available runtimes and supplied headers can be found in the docs: developer.android.com/ndk/guides/cpp-support.html

Stable NDK libraries ( ldLibs.addAll(['log', 'android']) ) The Android NDK provides a set of native headers for prebuilt libraries that allow access various system features without the need to go through JNI. Library Description Header files log Android log support log.h z ZLib compression library zlib.h, zconf.h dl Dynamic linker library dlfcn.h GLESv1_CM, GLESv2, OpenSLES, EGL, GLESv3, 3.1 Open GL ES, EGL, libraries for various versions many android For writing pure native apps many OpenMAXAL Android native multimedia handling is based on Khronos Group OpenMAX AL OMXAL/OpenMAXAL.h, OMXAL/OpenMAXAL_Platform.h, OpenMAXAL_Android.h jnigraphics Native interface to the pixel buffers of bitmaps bitmap.h

Pure Native Activity android.app.NativeActivity is a glue between Android and and pure native Activity No need to subclass it, only properly define in the manifest Android framework APIs can be accessed through the JNI Native code should adhere to certain structure defined by NDK in native_activity.h Alternatively use helper library defined in android_native_app_glue.h Implement native function ANativeActivity_onCreate Implement ANativeActivity->callbacks to manage activity lifecycle

Pure native activity manifest <application android:label="@string/app_name" android:hasCode="false" > <!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="android.app.NativeActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden"> <!-- Tell NativeActivity the name of or .so --> < meta-data android:name="android.app.lib_name" android:value="native-activity" /> ... </activity> </application> Sample: https://github.com/googlesamples/android-ndk/tree/master/native-activity

Debugging JNI code In the latest releases of Android Studio native debugger is nicely integrated and can be used out of the box

Debugging JNI crashes Tombstone files are crash dumps with some extra information Up to 10 last tombstone files saved by the system and replaced cyclically $ adb shell ls /data/tombstones $ adb pull /data/tombstones/tombstone_00

Examining tombstone files with ndk-stack Identify crash address in your library $ ndk-stack -sym <root symbols dir> [-dump <dump file>] $ ndk-stack -sym .\app\build\intermediates\symbols -dump .\tombstone_00

Finding file location with addr2line addr2line command that you need to use depends on the ABI of your crashed file. alias addr2line= \ ’$NDK/toolchains/x86_64-4.9/prebuilt/windows/bin/i686-linux-android-addr2line.exe’ Find the crashed line $ addr2line -f -e <input file> <address>

Summary JNIEnv *, Local/global refs, method signatures, javah , javap , tombstones, ndk-stack, addr2line Q & A