mastering the ndk with android studio 2.0 and the gradle-experimental plugin
Post on 16-Apr-2017
20.330 Views
Preview:
TRANSCRIPT
Mastering the NDKwith Android Studio 2.0+ and the gradle-experimental plugin
Xavier Hallade
Application Engineer @ Intel
#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
The Android NDKLet’s briefly reexamine what it is and how it works
#droidconIT
The Android NDK
Checking .so files
https://play.google.com/store/apps/details?id=co
m.xh.nativelibsmonitor.app
#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*
#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 usingJNIEnv->RegisterNatives()
Mapping C/C++ implementations to Java methods
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
#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.
Android Studio and the NDKWhere are we now?
#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 !!
AS NDK Code editingDemo
13
AS NDK DebuggingDemo
How to get This?
#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
#droidconIT
gradle(-stable) plugin, deprecated Android NDK support
- bad APP_PLATFORM handling
- it’s deprecated!
#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
#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.
#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
#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
Migrating to gradle-experimental
#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()
#droidconIT
Example of build.gradle update to gradle-experimentalapply 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'))
}
}
}
#droidconIT
NDK configurabilityandroid.ndk {
moduleNameplatformVersiontoolchain toolchainVersioncFlagscppFlagsldLibsldFlagsabiFiltersstlrenderscriptNdkModedebuggable
}
android.abis { // 0.7.0+
create("ABI") { //ABI can be any of x86, x86_64,
armeabi-v7a, arm64-v8a…
cppFlagsldLibsldFlags
}}
#droidconIT
Android Studio configuration
Gradle configuration examples
#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)
#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
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)
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)
//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
Mixing gradle stable and -experimental plugins…and keep debugging working in AS:
#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)
Q&A@ph0b – ph0b.com – +XavierHallade
#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
Additional slides
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
#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_sharedLOCAL_STATIC_LIBRARIES := module_static
Other predefined macros:
BUILD_SHARED_LIBRARY, BUILD_STATIC_LIBRARY,PREBUILT_SHARED_LIBRARY, PREBUILT_STATIC_LIBRARY
#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
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
top related