How to build a native library for Android

It’s easy to build and make the application work with a small amount of native code. If you want to use a native library, in which there are many files, it becomes more difficult. The difficulty is that native libraries are distributed in the form of source code that needs to be compiled for the desired processor architecture. Using the Opus audio codec as an example, I'll show you how to do it.


image


Opus , Android, Opus, . - .so .a, , C/C++ . . Opus , NDK .so .a, Android-.


, C/C++, . Opus (.a).


C , . #include <fileWithHeaders.h>. C , (.a), #include <fileWithHeaders.h> . (.so), . , , , .


.so . , Opus , Android, . libjniopus.so , libopus.a, libopus.so.


NDK c 19 « ». , . , , , . , .


Opus


: (git clone), . Opus Autoconf — , . Autoconf toolchain , ENV-. Autoconf, , Unix . Windows, , , .


4 .a : armeabi-v7a, arm64-v8a, x86, x86-64.


ENV-. NDK, HOST_TAG TOOLCHAIN. Linux, ENV- , export:


export NDK=/home/vital/Android/Sdk/ndk/20.1.5948944
export HOST_TAG=linux-x86_64
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$HOST_TAG

ENV- , , . , . ENV- , .bashrc, (~). .bashrc. , , . ENV-, :


# arm64-v8a
export AR=$TOOLCHAIN/bin/aarch64-linux-android-ar
export AS=$TOOLCHAIN/bin/aarch64-linux-android-as
export CC=$TOOLCHAIN/bin/aarch64-linux-android21-clang
export CXX=$TOOLCHAIN/bin/aarch64-linux-android21-clang++
export LD=$TOOLCHAIN/bin/aarch64-linux-android-ld
export RANLIB=$TOOLCHAIN/bin/aarch64-linux-android-ranlib
export STRIP=$TOOLCHAIN/bin/aarch64-linux-android-strip

# armeabi-v7a
export AR=$TOOLCHAIN/bin/arm-linux-androideabi-ar
export AS=$TOOLCHAIN/bin/arm-linux-androideabi-as
export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi21-clang
export CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi21-clang++
export LD=$TOOLCHAIN/bin/arm-linux-androideabi-ld
export RANLIB=$TOOLCHAIN/bin/arm-linux-androideabi-ranlib
export STRIP=$TOOLCHAIN/bin/arm-linux-androideabi-strip

# x86
export AR=$TOOLCHAIN/bin/i686-linux-android-ar
export AS=$TOOLCHAIN/bin/i686-linux-android-as
export CC=$TOOLCHAIN/bin/i686-linux-android21-clang
export CXX=$TOOLCHAIN/bin/i686-linux-android21-clang++
export LD=$TOOLCHAIN/bin/i686-linux-android-ld
export RANLIB=$TOOLCHAIN/bin/i686-linux-android-ranlib
export STRIP=$TOOLCHAIN/bin/i686-linux-android-strip

# x86-64
export AR=$TOOLCHAIN/bin/x86_64-linux-android-ar
export AS=$TOOLCHAIN/bin/x86_64-linux-android-as
export CC=$TOOLCHAIN/bin/x86_64-linux-android21-clang
export CXX=$TOOLCHAIN/bin/x86_64-linux-android21-clang++
export LD=$TOOLCHAIN/bin/x86_64-linux-android-ld
export RANLIB=$TOOLCHAIN/bin/x86_64-linux-android-ranlib
export STRIP=$TOOLCHAIN/bin/x86_64-linux-android-strip

arm64-v8a, .bashrc ENV- . , CC CXX 21, Android API level. , , minSdkVersion. 21, , . NDK 16 29 32- ABI (armeabi-v7a x86) 21 29 64- ABI (arm64-v8a x86-64).


, ENV-, toolchain , Opus. Readme Opus , - :


$ ./autogen.sh
$ ./configure
$ make

. , 4 .


NDK r19 « » toolchains. , ENV- ( .bashrc), , . configure host. arm64-v8a :


$ source ~/.bashrc
$ ./autogen.sh
$ ./configure --host aarch64-linux-android
$ make

source ~/.bashrc , «» .


, Opus .libs. , libopus.a.


.bashrc /, ENV- armeabi-v7a, host:


$ source ~/.bashrc
$ ./configure --host armv7a-linux-androideabi
$ make

./autogen.sh , configure, .


make . , - . , , , , ( configure host). , , , , , .


. , Opus. , .


- 4 ENV- . , bash-, . :


#!/bin/bash
export NDK=/home/vital/Android/Sdk/ndk/20.1.5948944
export HOST_TAG=linux-x86_64
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$HOST_TAG

if [[ $# -ne 3 && $# -ne 2 ]] 
then
  echo "You should specify at least minSdkVersion and path to Opus directory"
  echo "Example ./buildOpus.sh 21 <pathToOpusDir> <pathToBuildDir>"
  exit
fi

# minSdkVersion that will used for complinig Opus source
minSdk=$1

# Path to directory where Opus sources are located
opusPath=$2

# Path to directory where to place build artifacts
# By default it creates dir where this script is located
if [[ $# -eq 3 ]]
then
  buildPath=$3
else
  buildPath=./opus_android_build
  if [[ -d $buildPath ]]; then
    echo "opus_android_build directory already existed..."
    echo "clearing opus_android_build directory..."
    rm -rfv $buildPath && mkdir $buildPath
  else
    echo "creating opus_android_build directory..."
    mkdir opus_android_build
  fi
fi

echo "Executing autogen.sh"
$opusPath/autogen.sh

if [[ $minSdk -lt 21 ]]
then
  triples=("armv7a-linux-androideabi" "i686-linux-android")
  abis=("armeabi-v7a" "x86")
else
  triples=("aarch64-linux-android" "armv7a-linux-androideabi" "i686-linux-android" "x86_64-linux-android")
  abis=("arm64-v8a" "armeabi-v7a" "x86" "x86-64")  
fi

BIN=$TOOLCHAIN/bin
cd $buildPath 

for i in ${!triples[@]}
do
  triple=${triples[$i]}
  abi=${abis[$i]}
  echo "Building $abi..."

  if [[ $triple == "armv7a-linux-androideabi" ]]
  then
    export AR=$BIN/arm-linux-androideabi-ar
    export AS=$BIN/arm-linux-androideabi-as
    export CC=$BIN/$triple$minSdk-clang
    export CXX=$BIN/$triple$minSdk-clang++
    export LD=$BIN/arm-linux-androideabi-ld
    export RANLIB=$BIN/arm-linux-androideabi-ranlib
    export STRIP=$BIN/arm-linux-androideabi-strip
  else
    export AR=$BIN/$triple-ar
    export AS=$BIN/$triple-as
    export CC=$BIN/$triple$minSdk-clang
    export CXX=$BIN/$triple$minSdk-clang++
    export LD=$BIN/$triple-ld
    export RANLIB=$BIN/$triple-ranlib
    export STRIP=$BIN/$triple-strip
  fi

  mkdir $abi && cd $abi 
  $opusPath/configure --host $triple
  make
  cd ..  
done

echo "Artifacts successfully built for minSdkVersion=$minSdk and ABIs:" 
printf '%s ' "${abis[@]}"
echo ""
echo "Artifacts are located in .libs directory"

, ENV- .bashrc , .


, :


$ sudo chmod +x buildOpus.sh

NDK HOST_TAG, Linux ( linux-x86_64):


  • 32-bit Windows: windows
  • 64-bit Windows: windows-x86_64
  • macOS: darwin-x86_64

:


$ ./buildOpus.sh 21 <pathToOpusDir> <pathToBuildDir>

minSdkVersion Opus. , . opus_android_build , buildOpus.sh.


. opus_android_build , , ABI, . , .libs, .



, 4 libopus.a , . Android- CmakeLists.txt, .so Opus.


app/src/main/cpp, C- (jniopus.c), external Kotlin. includes. include Opus. (.h). Opus C-. , , .


app/src/main/cpp libopus, 4 , ABI, Opus: armeabi-v7a, arm64-v8a, x86, x86-64. libopus.a, .


CmakeLists.txt, , Opus . «» . :


set(NATIVE_SOURCES_PATH "${PROJECT_SOURCE_DIR}/src/main/cpp")
set(OPUS_HEADERS_PATH "${NATIVE_SOURCES_PATH}/includes")
set(OPUS_LIB_PATH "${NATIVE_SOURCES_PATH}/libopus")

Opus :


add_library(
        libopus
        STATIC
        IMPORTED)

add_library . , , STATIC(.a) SHARED(.so) , IMPORTED, . () :


set_target_properties( # Specifies the target library.
        libopus PROPERTIES
        IMPORTED_LOCATION "${OPUS_LIB_PATH}/${ANDROID_ABI}/libopus.a")

ANDROID_ABI NDK toolchain , ABI.


-, external Kotlin, :


add_library( # Sets the name of the library.
        jniopus

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        ${NATIVE_SOURCES_PATH}/jniopus.c)

, « » NDK :


find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

:


target_link_libraries(
        # Specifies the target library.
        jniopus

        # Specifies the libraries that should be linked to our target
        libopus ${log-lib})

, , . , Gradle, Run, . , Start call . , , .



, NDK toolchains ABI . , CmakeLists.txt . , Autoconf , pre-19 NDK, . , , .



« Android»: https://vk.com/@forasoft-kak-ispolzovat-nativnye-biblioteki-v-android


: https://gitlab.com/vitaliybelyaev/opus-android


Opus codec: http://opus-codec.org/, https://github.com/xiph/opus


Using the NDK with other build systems: https://developer.android.com/ndk/guides/other_build_systems


Android- CMake: https://developer.android.com/studio/projects/add-native-code#create-cmake-script


ABI Android: https://developer.android.com/ndk/guides/abis

Source: https://habr.com/ru/post/undefined/


All Articles