Comment créer une bibliothèque native pour Android

Il est facile de créer et de faire fonctionner l'application avec une petite quantité de code natif. Si vous souhaitez utiliser une bibliothèque native, dans laquelle il y a beaucoup de fichiers, cela devient plus difficile. La difficulté est que les bibliothèques natives sont distribuées sous forme de code source qui doit être compilé pour l'architecture de processeur souhaitée. En utilisant le codec audio Opus comme exemple, je vais vous montrer comment le faire.


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