Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[Xavier Hallade] Mastering the NDK with Android...

[Xavier Hallade] Mastering the NDK with Android Studio 1.3+ and the gradle-experimental plugin

Presentation from GDG DevFest Ukraine 2015 - the biggest Google related event in the country. October 23-24, Lviv. Learn more at http://devfest.gdg.org.ua/

Google Developers Group Lviv

October 24, 2015
Tweet

More Decks by Google Developers Group Lviv

Other Decks in Programming

Transcript

  1. Mastering the NDK with Android Studio 1.3+ and the gradle-experimental

    plugin Xavier Hallade Developer Evangelist @ Intel
  2. #dfua Mastering the NDK - Agenda The Android NDK Brief

    history of Android Studio support of the NDK What we can do now with Android Studio Migrating to the gradle-experimental plugin Configuring your projects Q&A
  3. #dfua Java Native Interface (JNI) Java side • System.loadLibrary() •

    native keyword C/C++ side - #include <jni.h> • Java primitive types, objects, methods • JNIEnv*, JavaVM*
  4. #dfua 6 Mapping C/C++ implementations to Java methods use and

    return Java primitives and objects jint xxx(JNIEnv* env, jclass cls, …) use a specific function name: Java_com_example_hellojni_MainActivity_method or do a manual registration using JNIEnv->RegisterNatives()
  5. #dfua Uses Android.mk and Application.mk Makefiles. The NDK will generate

    optimized code for all target ABIs You can also pass APP_ABI variable to ndk-build, and specify each ABI: ndk-build APP_ABI=x86 all32 and all64 are also possible values. ndk-build(.cmd) Build ARM64 libs Build x86_64 libs Build mips64 libs Build ARMv7a libs Build ARMv5 libs Build x86 libs Build mips libs
  6. #dfua Classic* execution flow 1. .so files loaded in memory

    by System.loadLibrary() 1. C/C++ functions ó Java native methods 2. DVM/ART encounters a call to a native method 1. its C/C++ implementation is executed 2. then, Java code execution goes on * android.app.NativeActivity/ native_activity.h allow to develop without having Java code inside the app.
  7. #dfua Android Studio and the NDK – a brief history

    • December 2013: gradle 0.7.3 - sort of support for the NDK • December 2014: Eclipse ADT no longer in development • May 2015: Integration of CLion announced at Google I/O • July 2015: beta available! • October 2015: still quite WIP (-experimental), but usable!
  8. #dfua Solutions to use the NDK with gradle/AS gradle(-stable) plugin,

    deprecated Android NDK support gradle(-stable) plugin, manual call to NDK build gradle(-experimental) plugin, WIP-but-already-great Android NDK support gradle(-experimental) plugin, manual call to NDK build
  9. #dfua gradle(-stable) plugin, manual call to NDK build + stable

    + most configurable solution (using Android.mk/Application.mk) + supports generating split APKs with proper version codes - No C/C++ code editing/debugging within Android Studio
  10. #dfua gradle(-experimental) plugin, built-in NDK support + full code editing/debugging

    within Android Studio - it’s experimental - can’t generate split APKs with proper version codes - support for native dependencies is almost there
  11. #dfua gradle(-experimental) plugin, manual call to NDK build + full

    code editing within Android Studio + usual Android.mk/Application.mk support (with full native dependencies support) - it’s experimental - can’t generate split APKs with proper version codes - can’t debug from Android Studio
  12. #dfua Solutions to use the NDK with gradle/AS gradle(-stable) plugin,

    deprecated Android NDK support gradle(-stable) plugin, manual call to NDK build KJ -> http://ph0b.com/android-studio-gradle-and-ndk-integration/ gradle(-experimental) plugin, WIP-but-already-great Android NDK support J gradle(-experimental) plugin, manual call to NDK build J
  13. #dfua Gradle experimental plugin dependencies { classpath 'com.android.tools.build:gradle-experimental:0.3.0-alpha7' } com.android.model.application

    / com.android.model.library model{} .with{} = distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-all.zip
  14. #dfua Example of build.gradle update for gradle-experimental apply plugin: 'com.android.application'

    android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { applicationId "com.ph0b.example" minSdkVersion 15 targetSdkVersion 23 versionCode 4 versionName "1.0.1" ndk { moduleName "mymodule" ldLibs "log" stl "gnustl_static" cFlags "-std=c++11 -fexceptions" } } signingConfigs { release { storeFile file(STORE_FILE) storePassword STORE_PASSWORD keyAlias KEY_ALIAS keyPassword KEY_PASSWORD } } buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.txt' signingConfig signingConfigs.release } debug { jniDebuggable true } } } apply plugin: 'com.android.model.application' model { android { compileSdkVersion = rootProject.ext.compileSdkVersion buildToolsVersion = rootProject.ext.buildToolsVersion defaultConfig.with { applicationId = "com.ph0b.example" minSdkVersion.apiLevel = 15 targetSdkVersion.apiLevel = 23 versionCode = 4 versionName = "1.0.1" } } android.ndk { moduleName = "mymodule" ldLibs += ['log'] cppFlags += "-std=c++11" cppFlags += "-fexceptions" stl = 'gnustl_static' } android.signingConfigs { create("release") { keyAlias = KEY_ALIAS keyPassword = STORE_PASSWORD storeFile = file(STORE_FILE) storePassword = KEY_PASSWORD } } android.buildTypes { release { // signingConfig = signingConfigs.release // buggy, look for a workaround here: issue 182249 minifyEnabled = true proguardFiles += file('proguard-rules.txt') } } }
  15. #dfua NDK configurability android.ndk { moduleName platformVersion toolchain toolchainVersion cFlags

    cppFlags ldLibs ldFlags abiFilters stl renderscriptNdkMode debuggable } In the model, but not yet in the DSL: cIncludeDirs cppIncludeDirs cDefines cppDefines
  16. #dfua Native dependencies (with sources) android.ndk { moduleName = "TeapotNativeActivity"

    platformVersion = 17 cppFlags += "-I${file("src/main/jni/native_app_glue")}".toString() cppFlags += "-I${file("src/main/jni/cpufeatures")}".toString() cppFlags += "-I${file("src/main/jni/ndk_helper")}".toString() ldLibs += ["android", "EGL", "GLESv2", "dl", "log"] stl = "stlport_static" } build.gradle
  17. #dfua Adding a folder of native sources to be compiled

    android.sources { main.jni { source { srcDirs += ['src/main/XXX'] } } } build.gradle
  18. #dfua android.sources { main.jni { dependencies { project ":ndkLib" buildType

    "debug" linkage "static" } } } build.gradle Other solution (really experimental right now): Native dependencies (with sources)
  19. #dfua Using good old Android.mk/Application.mk import org.apache.tools.ant.taskdefs.condition.Os android.ndk { …

    } android.sources { main.jni { source { srcDirs = ['src/main/none'] } } main.jniLibs { source { srcDirs = ['src/main/libs'] } } } task ndkBuild(type: Exec) { def ndkBuildExt = Os.isFamily(Os.FAMILY_WINDOWS) ? ".cmd" : "" commandLine "ndk-build${ndkBuildExt}", '-C', file('src/main').absolutePath } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn ndkBuild } build.gradle
  20. #dfua Native Dependencies android.productFlavors { create ("x86") { ndk.abiFilters +=

    "x86" ndk.ldFlags += "-L${file("src/main/jni/prebuilts/x86")}".toString() } } build.gradle Please don’t do this, as version codes can’t be properly handled.
  21. #dfua Android.mk LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE :=

    hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY) • Other useful variables: LOCAL_C_INCLUDES := ./headers/ LOCAL_EXPORT_C_INCLUDES := ./headers/ LOCAL_SHARED_LIBRARIES := module_shared LOCAL_STATIC_LIBRARIES := module_static Other predefined macros: BUILD_SHARED_LIBRARY, BUILD_STATIC_LIBRARY, PREBUILT_SHARED_LIBRARY, PREBUILT_STATIC_LIBRARY
  22. #dfua 3 Application.mk APP_PLATFORM := android-15 # <= minSDKVersion APP_CFLAGS

    := -O3 APP_STL := gnustl_shared APP_ABI := all # or all32, all64… APP_OPTIM := release # default NDK_TOOCLHAIN_VERSION := 4.8 # default
  23. #dfua Multiple APKs and version codes handling Google Play* supports

    multiple APKs for the same application. What compatible APK will be chosen for a device entirely depends on the android:versionCode If you have multiple APKs for multiple ABIs, best is to simply prefix your current version code with a digit representing the ABI: 2310 3310 6310 7310 You can have more options for multiple APKs, here is a convention that will work if you’re using all of these: x86 ARMv7 ARM64 X86_64