Android AAR package for native library

igagis picture igagis · Feb 1, 2016 · Viewed 10.4k times · Source

I'm looking for a way to package a native library into an AAR package, so it would be possible to use it via dependencies declaration in gradle script.

By native library I mean set of .cpp files or compiled static library and a set of header files. So, I mean that the app itself will call the library from native code, not from Java. In other words, the library needed to compile app's native code. So that it will be possible to easily manage dependencies of native code.

Is it even possible?

So far I could only find a lot of questions/examples of how to make an AAR of JNI native library with .so file and its Java interface, so the lib just a Java lib with native implementation, but this is not what I need.

Answer

igagis picture igagis · Apr 27, 2017

= UPDATE 2020-06-20 =

Nowadays, there is a nice plugin for that, works pretty well. Thanks to its author and to @Paulo Costa for pointing to it.

= OBSOLETE =

Found the following hacky solution to the problem:

Use Android Experimental Gradle plugin version 0.9.1. The idea is to put library headers and static libraries into .aar. The headers are put to ndkLibs/include and static libs to ndkLibs/<arch> for each architecture. Then, in the app, or another lib which depends on this packed lib we just extract ndkLibs directory from AAR to the build directory in the project. See the example gradle file below.

The build.gradle file for the library with comments:

apply plugin: "com.android.model.library"

model {
    android {
        compileSdkVersion = 25
        buildToolsVersion = '25.0.2'

        defaultConfig {
            minSdkVersion.apiLevel = 9
            targetSdkVersion.apiLevel = 9
            versionCode = 1
            versionName = '1.0'
        }
        ndk {
            platformVersion = 21
            moduleName = "mylib"
            toolchain = 'clang'
            abiFilters.addAll(['armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64']) //this is default
            ldLibs.addAll(['android', 'log'])
            stl = 'c++_static'
            cppFlags.add("-std=c++11")
            cppFlags.add("-fexceptions")
            cppFlags.add("-frtti")

            //Add include path to be able to find headers from other AAR libraries
            cppFlags.add("-I" + projectDir.getAbsolutePath() + "/build/ndkLibs/include")
        }

        //For each ABI add link-time library search path to be able to link against other AAR libraries
        abis {
            create("armeabi") {
                ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/armeabi")
            }
            create("armeabi-v7a") {
                ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/armeabi-v7a")
            }
            create("arm64-v8a") {
                ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/arm64-v8a")
            }
            create("x86") {
                ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/x86")
            }
            create("x86_64") {
                ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/x86_64")
            }
            create("mips") {
                ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/mips")
            }
            create("mips64") {
                ldFlags.add("-L" + projectDir.getAbsolutePath() + "/build/ndkLibs/mips64")
            }
        }
    }

    //Configure this library source files
    android.sources {
        main {
            jni {
                //This does not affect AAR packaging
                exportedHeaders {
                    srcDir "../../src/"
                }

                //This tells which source files to compile
                source {
                    srcDirs '../../src'
                }
            }
        }
    }
}

//Custom Maven repository URLs to download AAR files from
repositories {
    maven {
        url 'https://dl.bintray.com/igagis/android/'
    }
}

//Our custom AAR dependencies, those in turn are also packed to AAR using the same approach
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'io.github.igagis:libutki:+'
    compile 'io.github.igagis:libsvgdom:+'
    compile 'org.cairographics:cairo:+'
}


//===================================
//=== Extract NDK files from AARs ===
//This is to automatically extract ndkLibs directory from AAR to build directory before compiling any sources
task extractNDKLibs {
    doLast {
        configurations.compile.each {
            def file = it.absoluteFile
            copy {
                from zipTree(file)
                into "build/"
                include "ndkLibs/**/*"
            }
        }
    }
}
build.dependsOn('extractNDKLibs')
tasks.whenTaskAdded { task ->
    if (task.name.startsWith('compile')) {
        task.dependsOn('extractNDKLibs')
    }
}



//=================================
//=== pack library files to aar ===
//This stuff re-packs the release AAR file adding headers and static libs to there, but removing all shared (.so) libs, as we don't need them. The resulting AAR is put to the project root directory and can be uploaded to Maven along with POM file (you need to write one by hand).

def aarName = name

task copyNdkLibsToAAR(type: Zip) {
    baseName = aarName
    version = "\$(version)"
    extension = 'aar.in'
    destinationDir = file('..') //put resulting AAR file to upper level directory

    from zipTree("build/outputs/aar/" + aarName + "-release.aar")
    exclude('**/*.so') //do not include shared libraries into final AAR
    from("../../src") {
        exclude('makefile')
        exclude('soname.txt')
        exclude('**/*.cpp')
        exclude('**/*.c')
        into('ndkLibs/include')
    }
    from("build/intermediates/binaries/debug/lib"){
        include('**/*.a')
        into('ndkLibs')
    }
}

build.finalizedBy('copyNdkLibsToAAR')