Now that Android Studio is in the stable channel, it is pretty straightforward to get the android-ndk samples running. These samples use the ndk experimental plugin and are newer than the ones linked to from the Android NDK online documentation. Once you know they work you can study the build.gradle, local.properties and gradle-wrapper.properties files and modify your project accordingly. Following are the steps to get them working.
I have successfully built and deployed all projects in the repository this way, except gles3gni, native-codec and builder. I'm using the following:
During Google I/O 2015, end of May, Google announced a new support for the NDK by Android Studio 1.3, integrating Jetbrains CLion capabilities, and the Android gradle plugin. This support has been released only in July, and while it’s really promising, it’s still in heavy development.
The new NDK support requires the use of Android Studio 1.3 RC1+/2.0+ and the android gradle-experimental plugin. This article is for those who are willing to give it a try. If you’re looking into the NDK support while using the gradle(-stable) plugin, you can check this older (but still up-to-date) article on the NDK and Android Studio.
article last updated on 2016/01/26 (gradle-experimental 0.6.0-alpha7 – Android Studio 2.0 preview 7, support for native dependencies!)
Migrating to the gradle-experimental plugin and the new com.android.model.*
The gradle-experimental 0.6.0-alpha7 requires using gradle-2.10. Start by setting it from your project settings:
Or inside gradle/wrapper/gradle-wrapper.properties:
|
distributionUrl
=
https
\
:
//services.gradle.org/distributions/gradle-2.10-all.zip
|
Then, change the reference to the android gradle plugin to the new gradle-experimental plugin, from ./build.gradle:
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript
{
repositories
{
jcenter
(
)
}
dependencies
{
classpath
'com.android.tools.build:gradle-experimental:0.6.0-alpha7'
}
}
|
The gradle-experimental plugin introduces a change in the DSL. The android pluginscom.android.model.application and com.android.model.library are replacing the former com.android.application and com.android.libraryplugins.
You need to migrate your apps and libs build.gradle files to use these new plugins. Here is an example of the same configuration, with the old DSL (top) and the new (bottom):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
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
}
}
}
dependencies
{
compile
'com.android.support:support-v4:23.1.1'
compile
fileTree
(
dir
:
'libs'
,
include
:
[
'*.jar'
]
)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
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_shared'
}
android
.
signingConfigs
{
create
(
"release"
)
{
keyAlias
=
KEY_ALIAS
keyPassword
=
STORE_PASSWORD
storeFile
=
file
(
STORE_FILE
)
storePassword
=
KEY
_PASSWORD
}
}
android
.
buildTypes
{
release
{
shrinkResources
=
true
useProguard
=
true
proguardFiles
.
add
(
file
(
'proguard-rules.txt'
)
)
signingConfig
=
signingConfigs
.
release
}
}
}
dependencies
{
compile
'com.android.support:support-v4:23.1.1'
compile
fileTree
(
dir
:
'libs'
,
include
:
[
'*.jar'
]
)
}
|
To summarize the changes required: all the android declarations are now going under model {}, the various assignments now have to use explicitly ‘=‘, collections must not be overwritten, use .removeAll(),.add(), .addAll() instead. Variants and other new configurations have to be declared using ‘create()‘. Properties like xxxSdkVersion have changed to xxxSdkVersion.apiLevel.
As it’s experimental, you can expect regular changes around the DSL across versions. For example, minifyEnabled has been changed toisMinifyEnabled, then to minifyEnabled again, and now there is also useProguard; jniDebuggable has been changed to isJniDebuggableand then to ndk.debuggable (and is now set by default for debug builds).
You’ll notice that with both DSLs, there is a configuration block for the NDK. This is the place where you’re supposed to set all the NDK configuration when using gradle, as by default Android.mk andApplication.mk files will be simply ignored.
Enjoying the new C++/NDK support in Android Studio
To activate the C++/NDK support inside Android Studio, you only need to have a NDK module declared inside your application or library build.gradle:
|
model
{
//...
android
.
ndk
{
moduleName
=
"mymodule"
}
}
|
Once it’s done, you can go to your Java sources, create a method prefixed with the native keyword, and press ALT+Enter to generate its C or C++ implementation:
Yes, it’s that magical
The implementation will be added under ‘jni‘, inside an existing cpp file if there is one, or inside a new one.
In order to get started with NDK modules, you can have a look at all the samples that have been ported to use the new gradle-experimental plugin: https://github.com/googlesamples/android-ndk
Here is everything you can configure for a ndk module:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
android
.
ndk
{
moduleName
=
"mymodule"
ldLibs
.
addAll
(
[
'log'
]
)
ldFlags
.
add
(
""
)
toolchain
=
"clang"
toolchainVersion
=
"3.9"
abiFilters
.
add
(
"x86"
)
CFlags
.
add
(
""
)
cppFlags
.
add
(
""
)
debuggable
=
false
renderscriptNdkMode
=
false
stl
=
"system"
platformVersion
=
15
}
|
Debugging a NDK project
In order to get the debug capabilities of AS, create and use a new Run/Debug configuration from the “Android Native” default. While it was possible to use GDB in Android Studio 1.3, now only the LLDB backend is available.
Use it with your debug variant, which will have ndk.debuggable flag set to true by default.
Going further with the NDK with Android Studio
Many advanced features, such as the ability to have dependencies between native libraries, reuse prebuilts, tune specific toolchain options and having dynamic version codes while still having a project in a good shape is a bit complex, as the gradle-experimental plugin is still undergoing a lot of improvements across versions.
Getting the APP_PLATFORM right
When you’re building a NDK module, the android platform you’re compiling it against is a quite important setting, as it basically determines the minimum platform your module will be guaranteed to run on.
With earlier versions than gradle-experimental:0.3.0-alpha4, the chosen platform was the one set as compileSdkVersion. Fortunately with subsequent releases, you can now set android.ndk.platformVersionindependently, and you should make it the same as your minSdkVersion.
Using external libraries and separate modules
with sources
If you have access to your 3rd party libraries source code, you can embed it into your project and make it statically compile with your code.
There is an example of this with the native_app_glue library from the NDK, inside the native-activity sample. For example, you can copy the library sources inside a subfolder inside your jni folder and add a reference to its directory so the includes are properly resolved:
|
android
.
ndk
{
//...
cppFlags
+=
"-I${file("
src
/
main
/
jni
/
native_app
_glue
")}"
.
toString
(
)
}
|
with sources in different modules
Now with 0.6.0-alpha7 version, you can finally have clean dependencies between native libraries, by setting the dependency on another module from your model:
|
android
.
sources
{
main
{
jni
{
dependencies
{
project
":yourlib"
buildType
"release"
productFlavor
"flavor1"
linkage
"shared"
}
}
}
}
|
In order to keep debugging working, you may have to edit your app-native run configuration, to add /build/intermediates/binaries/release/obj/[abi] to the symbol directories.
with native prebuilts
This technique works with static and shared prebuilts too! Inside your model, you’ll have to add a “lib repository”:
|
repositories
{
libs
(
PrebuiltLibraries
)
{
yourlib
{
headers
.
srcDir
"src/main/jni/prebuilts/include"
binaries
.
withType
(
SharedLibraryBinary
)
{
sharedLibraryFile
=
file
(
"src/main/jni/prebuilts/${targetPlatform.getName()}/libyourlib.so"
)
}
}
}
}
|
And declare the dependency on this library:
|
android
.
sources
{
main
{
jni
{
dependencies
{
library
|