Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Mastering the NDK with Android Studio 2.0 and the gradle-experimental plugin

22,033 views

Published on

Android Studio is finally fully supporting the NDK so you can easily integrate C/C++ inside Android applications.

While this support is still experimental, it’s promising and worth already doing the final jump from other IDEs and build systems.

After a short introduction on what is the NDK, learn how to use it from Android Studio while avoiding several classic pitfalls.

Published in: Technology

Mastering the NDK with Android Studio 2.0 and the gradle-experimental plugin

  1. 1. MasteringtheNDKwith Android Studio 2.0+ and the gradle-experimental plugin Xavier Hallade Application Engineer @ Intel
  2. 2. #droidconIT 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. 3. TheAndroidNDK Let’s briefly reexamine what it is and how it works
  4. 4. #droidconIT The Android NDK
  5. 5. Checking .so files https://play.google.com/store/apps/details?id=co m.xh.nativelibsmonitor.app
  6. 6. #droidconIT Java Native Interface (JNI) Java side  System.loadLibrary()  native keyword C/C++ side - .so files  #include <jni.h> – Java primitive types, objects, methods – JNIEnv*, JavaVM*
  7. 7. #droidconIT 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() Mapping C/C++ implementations to Java methods
  8. 8. 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) – the historical tool Build ARM64 libs Build x86_64 libs Build mips64 libs Build ARMv7a libs Build ARMv5 libs Build x86 libs Build mips libs
  9. 9. #droidconIT 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.
  10. 10. AndroidStudioandtheNDK Where are we now?
  11. 11. #droidconIT 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: first availability, with a lot of limitations • Since April 7th (yesterday night!): first stable version of the experimental plugin available, and everything is awesome !!
  12. 12. ASNDKCodeediting Demo
  13. 13. 13
  14. 14. ASNDKDebugging Demo
  15. 15. HowtogetThis?
  16. 16. #droidconIT 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, experimental but stable since yesterday • mixing gradle-stable and –experimental plugins
  17. 17. #droidconIT gradle(-stable) plugin, deprecated Android NDK support - bad APP_PLATFORM handling - it’s deprecated!
  18. 18. #droidconIT 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 ~ C/C++ code editing inside Android Studio - No C/C++ code debugging inside Android Studio
  19. 19. #droidconIT gradle(-experimental) plugin, built-in NDK support + full code editing/debugging within Android Studio + good support for native dependencies since 0.6.0-alpha1 - it’s experimental and the documentation barely exists yet - can’t generate split APKs with proper version codes - Many incompatible plugins.
  20. 20. #droidconIT Mixing gradle-stable and gradle-experimental plugins + full code editing/debugging support within Android Studio + good support for native dependencies since 0.6.0 + can generate split APKs with proper version codes + all the plugins are supported - it’s experimental and the documentation barely exists yet
  21. 21. #droidconIT Using the right versions Android Studio 1.5 2.0 2.1-preview5 gradle 2.8 2.10 2.10 gradle plugin 1.5.0 2.0.0 2.1.0-alpha5 gradle-experimental plugin 0.4.0 0.6.0 0.7.0-alpha5
  22. 22. Migratingtogradle-experimental
  23. 23. #droidconIT Gradle experimental plugin dependencies { classpath 'com.android.tools.build:gradle-experimental:0.6.0' } com.android.model. application / library / native model{} .with{} distributionUrl=https://services.gradle.org/distributions/gradle-2.10-all.zip .add() / .addAll() / .removeAll()
  24. 24. #droidconIT Example of build.gradle update to 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.addAll(['log']) cppFlags.add("-std=c++11“) cppFlags.add("-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 = $("android.signingConfigs.release“) minifyEnabled true proguardFiles.add(file('proguard-rules.txt')) } } }
  25. 25. #droidconIT NDK configurability android.ndk { moduleName platformVersion toolchain toolchainVersion cFlags cppFlags ldLibs ldFlags abiFilters stl renderscriptNdkMode debuggable } android.abis { // 0.7.0+ create("ABI") { //ABI can be any of x86, x86_64, armeabi-v7a, arm64-v8a… cppFlags ldLibs ldFlags } }
  26. 26. #droidconIT Android Studio configuration
  27. 27. Gradleconfigurationexamples
  28. 28. #droidconIT android.ndk { moduleName = "TeapotNativeActivity" platformVersion = 17 cppFlags.add("-I${file("src/main/jni/native_app_glue")}".toString()) cppFlags.add("-I${file("src/main/jni/cpufeatures")}".toString()) cppFlags.add("-I${file("src/main/jni/ndk_helper")}".toString()) ldLibs.addAll(["android", "EGL", "GLESv2", "dl", "log"]) stl = "stlport_static" } build.gradle Native dependencies (with sources)
  29. 29. #droidconIT android.sources { main{ jni { source { srcDir 'src/main/XXX'//folder srcDir 'src/main/YYY'//other folder exclude "**/not_this_one.c" //single file } } } } build.gradle Adding/Removing native sources to be compiled
  30. 30. repositories { libs(PrebuiltLibraries) { myexternallib { headers.srcDir "src/main/jni/prebuilts/include" binaries.withType(SharedLibraryBinary) { sharedLibraryFile = file("src/main/jni/prebuilts/${targetPlatform.getName()}/libmyexternallib.so") } }}} android.ndk { moduleName = "TeapotNativeActivity" platformVersion = 17 ldLibs.addAll(["android", "EGL", "GLESv2", "dl", "log"]) stl = "stlport_static" } android.sources { main { jni { dependencies { library "myexternallib" linkage "dynamic" //dynamic is default / can be static too }}}} build.gradle Native dependencies (prebuilts)
  31. 31. apply plugin: 'com.android.model.application' model { def versionCodeBase = 11; def versionCodePrefixes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]; //... android.productFlavors { create ("armv7") { ndk.abiFilters.add("armeabi-v7a") versionCode = versionCodePrefixes.get("armeabi-v7a", 0) * 1000000 + versionCodeBase } create ("x86") { ndk.abiFilters.add("x86") versionCode = versionCodePrefixes.get("x86", 0) * 1000000 + versionCodeBase } } } build.gradle Multiple APKs (gradle-experimental)
  32. 32. //project’s build.gradle: buildscript { … dependencies { classpath 'com.android.tools.build:gradle-experimental:0.6.0' classpath 'com.android.tools.build:gradle:2.0.0' } } //to keep debugging working, set this in the build.gradle that uses the stable plugin: android{ buildTypes.debug.jniDebuggable true } /! use the latest versions for both plugins, don’t mix older ones. build.gradle Mixing gradle stable and -experimental plugins
  33. 33. Mixing gradle stable and -experimental plugins …and keep debugging working in AS:
  34. 34. #droidconIT splits { abi { enable true reset() include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' universalApk true } } // map for the version code project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9] applicationVariants.all { variant -> // assign different version code for each output variant.outputs.each { output -> output.versionCodeOverride = project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + defaultConfig.versionCode } } build.gradle Multiple APKs (APK Splits – gradle-stable)
  35. 35. Q&A@ph0b – ph0b.com – +XavierHallade
  36. 36. #droidconIT Resources http://ph0b.com/new-android-studio-ndk-support/ https://github.com/googlesamples/android-ndk Watch for this issue to be resolved for fixing editing on Windows versions of AS: http://b.android.com/195483
  37. 37. Additionalslides
  38. 38. 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: x86ARMv7 ARM64 X86_64
  39. 39. #droidconIT 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
  40. 40. #droidconIT 43 Application.mk APP_PLATFORM := android-15 # <= minSDKVersion APP_CFLAGS := -O3 APP_STL := c++_shared APP_ABI := all # or all32, all64… APP_OPTIM := release # default NDK_TOOCLHAIN_VERSION := 4.8 # default
  41. 41. import org.apache.tools.ant.taskdefs.condition.Os android.sources { main { jni { source { srcDirs = ['src/main/none'] } } 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 Using Android.mk/Application.mk from Gradle

×