Build with android ndk

ndk-build

On this page

The ndk-build file is a shell script introduced in Android NDK r4. Its purpose is to invoke the right NDK build script.

Internals

Running the ndk-build script is equivalent to running the following command:

$GNUMAKE points to GNU Make 3.81 or later, and points to your NDK installation directory. You can use this information to invoke ndk-build from other shell scripts, or even your own make files.

Invoking from the Command Line

The ndk-build file lives in the top level the NDK installation directory. To run it from the command line, invoke it while in or under your application project directory. For example:

In this example,

points to your project’s root directory, and is the directory where you installed the NDK.

Options

All parameters to ndk-build are passed directly to the underlying GNU make command that runs the NDK build scripts. Combine ndk-build and options in the form ndk-build . For example:

The following options are available:

clean Remove any previously generated binaries. V=1 Launch build, and display build commands. -B Force a complete rebuild. -B V=1 Force a complete rebuild, and display build commands. NDK_LOG=1 Display internal NDK log messages (used for debugging the NDK itself). NDK_DEBUG=1 Force a debuggable build (see Table 1). NDK_DEBUG=0 Force a release build (see Table 1). NDK_HOST_32BIT=1 Always use the toolchain in 32-bit mode (see 64-bit and 32-bit Toolchains). NDK_APPLICATION_MK= Build, using a specific Application.mk file pointed to by the NDK_APPLICATION_MK variable. -C

Build the native code for the project path located at

. Useful if you don’t want to cd to it in your terminal.

Invoking from Eclipse

To build from Eclipse, make sure that you have configured it as described in Setup. If you wish to build using the default ndk-build command, with no options, you can just build your project just as you would any Android project. To get Eclipse to add any of the options described above, follow these steps:

  1. In the Project Explorer pane, right-click your project name.
  2. Select Properties.
  3. Click C/C++ Build.
  4. Under the Builder Settings tab, uncheck Use default build command.
  5. In the Build command field, enter the entire build string as if you were typing it on the command line.
  6. Click OK.

Figure 1 shows an example of an entered string.

Figure 1. Specifying a debug build from within Eclipse

Debuggable versus Release builds

Use the NDK_DEBUG option and, in certain cases, AndroidManifest.xml to specify debug or release build, optimization-related behavior, and inclusion of symbols. Table 1 shows the results of each possible combination of settings.

Table 1. Results of NDK_DEBUG (command line) and android:debuggable (manifest) combinations.

NDK_DEBUG=0 NDK_DEBUG=1 NDK_DEBUG not specified
android:debuggble=»true» Debug; Symbols; Optimized*1 Debug; Symbols; Not optimized*2 (same as NDK_DEBUG=1)
android:debuggable=»false» Release; Symbols; Optimized Release; Symbols; Not optimized Release; No symbols; Optimized*3

*1: Useful for profiling.
*2: Default for running ndk-gdb .
*3: Default mode.

Note: NDK_DEBUG=0 is the equivalent of APP_OPTIM=release , and complies with the GCC -O2 option. NDK_DEBUG=1 is the equivalent of APP_OPTIM=debug in Application.mk , and complies with the GCC -O0 option. For more information about APP_OPTIM , see Application.mk.

The syntax on the command line is, for example:

If you are using build tools from prior to SDK r8, you must also modify your AndroidManifest.xml file to specify debug mode. The syntax for doing so resembles the following:

From SDK r8 onward, you do not need to touch AndroidManifest.xml . Building a debug package (e.g. with ant debug or the corresponding option of the ADT plugin) causes the tool automatically to pick the native debug files generated with NDK_DEBUG=1 .

Читайте также:  Установка андроид с помощью один

64-Bit and 32-Bit Toolchains

Some toolchains come with both 64-bit and 32-bit versions. For example, directories /toolchain/ /prebuilt/ and /prebuilt/ may contain both linux-x86 and linux-x86_64 folders for Linux tools in 32-bit and 64-bit modes, respectively. The ndk-build script automatically chooses a 64-bit version of the toolchain if the host OS supports it. You can force the use of a 32-bit toolchain by using NDK_HOST_32BIT=1 either in your environment or on the ndk-build command line.

Note that 64-bit tools utilize host resources better (for instance, they are faster, and handle larger programs), and they can still generate 32-bit binaries for Android.

Requirements

You need GNU Make 3.81 or later to use ndk-build or the NDK in general. The build scripts will detect a non-compliant Make tool, and generate an error message.

If you have GNU Make 3.81 installed, but the default make command doesn’t launch it, define GNUMAKE in your environment to point to it before launching ndk-build. For example:

You can override other host prebuilt tools in $NDK/prebuilt/ /bin/ with the following environment variables:

Источник

Использование NDK в Android Studio

В настоящее время среди Android-девелоперов большую популярность имеет среда разработки Android Studio, основанная на IntelliJ IDEA от JetBrains. Однако, при использовании данной IDE, могут возникнуть проблемы при разработке приложений, использующих нативный код, так как Android NDK рассчитан преимущественно на использование IDE Eclipse и ADT.

Цель данной статьи — подробное описание процесса создания Android-приложения, использующего NDK в Android Studio, в частности — предложение достаточно простой и эффективной конфигурации gradle (системы сборки пакетов, используемая в Android Studio), гарантирующей включение нативных библиотек в APK-файл. Также статья включает краткую инструкцию работы с NDK в IDE Eclipse и введение в нативную разработку, достаточное для написания первого приложения.

Данная статья предназначена преимущественно для начинающих разработчиков. Описанное решение не является единственным, но оно достаточно удобно, особенно для тех, кто работал с NDK в Eclipse. Если кому-то из читателей объяснение покажется слишком подробным, то в конце статьи есть краткое резюме, описывающее лишь алгоритм требуемых действий без комментариев к каждому шагу.

Так как существует множество статей, описывающих работу с NDK, я не буду использовать в качестве примера сложные библиотеки, а ограничусь лишь самым простым примером hello-jni. Исходный код данного примера можно найти в каталоге /samples/hello-jni

В среде Eclipse особых проблем с использованием NDK не возникало. Каталог проекта выглядит примерно так:

Рис.1 Главный каталог проекта для Eclipse

Нас интересуют каталоги jni и libs. Каталог jni содержит исходные коды на нативных языках (*.c; *.cpp), заголовочные файлы (*.h), makefiles (*.mk). Долго останавливаться на предназначении данных файлов не буду, так как этому посвящено немало материалов и статей. Упомяну лишь, что jni означает java native interface. Именно через этот интерфейс производится вызов нативных процедур из кода на java. Поэтому не забывайте подключать библиотеку в ваши c/c++ файлы и помните о правильном синтаксисе функций, которые будут вызываться через jni. Например, в моём случае приложение имеет package name:
evi.ntest
поэтому описание функции выглядит так:

где jstring — название типа данных c, соответствующего типу string в java, Java — в данном случае служебный префикс, показывающий язык, из которого будет вызвана функция, evi_ntest — имя пакета, который будет вызывать функцию, MainActivity — имя активити, из которой будет вызываться функция, stringFromJNI — название функции.
В java-коде описание данной функции выглядит гораздо проще:

Не забывайте также указывать используемые файлы кода в .mk файлах. Для начала можете воспользоваться .mk файлами из примеров NDK, модифицируя названия файлов, но в дальнейшем рекомендую изучить их структуру.

Читайте также:  4pda gps test android что это

Каталог libs содержит готовые бинарные библиотеки для различных архитектур процессоров (по умолчанию — armeabi). Динамическая библиотека представляет собой файл с расширением .so, статическая — файл с расширением .a. Для получения данных библиотек требуется компиляция исходных кодов с помощью Android NDK. В Unix-системах (в моём случае — Mac OSX) для этого требуется в терминале ввести следующие строчки:

При этом NDK автоматически компилирует исходные коды из папки jni и помещает полученные библиотеки в libs/armeabi (также можно с помощью параметров командной строки задать компиляцию под x86, mips, arm v7-neon процессоры).
При использовании Windows потребуется воспользоваться дополнительными утилитами, возможно — плагинами для MS Visual Studio.
В любом случае, не имеет значения, каким именно способом получены готовые библиотеки, важен факт, что если в папке с проектом находится подкаталог libs, Eclipse при сборке автоматически включает его содержимое в APK-файл.

Так как данная статья посвящена лишь основам работы с NDK, то на этом введение в программирование на нативных языках под android я закончу. Для более подробного ознакомления с принципами нативной разработки рекомендую изучить примеры из каталога /samples/, а также читать статьи, в том числе и на русском языке. Пример хорошей статьи о Android NDK на русском языке, советую обратить внимание (написана не мной):

Перехожу к основному разделу статьи — настройке IDE Android Studio для работы с нативным кодом.

Среда Android Studio по умолчанию собирает APK с помощью gradle. Данный сборщик имеет широкие возможности кастомизации, но при стандартных настройках gradle не включает нативные библиотеки в APK-файл.

Рассмотрим частичную структуру проекта в Android Studio:

Рис.2 Путь к исходному коду проекта Android Studio.

При работе у меня возникло логичное желание разместить папку jni в каталоге src/main, так как именно там хранятся все остальные файлы с исходным кодом. Разумеется, читатель может размещать каталог jni там, где ему удобно. Главное — не забыть собрать бинарные библиотеки с помощью NDK (повторюсь, в UNIX системах для этого нужно в терминале перейти к каталогу, содержащему jni, затем вызвав исполняемый файл ndk-build, лежащий в папке с NDK, прописав полный путь к нему в этом же терминале, в MS Windows нужно использовать дополнительные утилиты). Проблема же заключается в том, что по умолчанию gradle не будет упаковывать в APK библиотеки.

Однако gradle несложно настроить на включение в сборку java-библиотек (файлов *.jar). Стоит заметить, что jar-файлы представляют собой zip-архивы, содержащие какие-либо ресурсы, а также объектный код. Таким образом, для включения бинарных библиотек *.so и *.a достаточно упаковать их в jar-файл.
Делается это так:

  • Переименовываем папку libs, содержащую наши бинарные библиотеки в lib
  • Сжимаем данную папку любым zip-архиватором
  • Меняем расширение полученного файла на .jar

Полученную библиотеку можно подключить на этапе сборки проекта, при этом полученный APK-файл будет включать двоичные библиотеки, а приложение — вызывать процедуры, написанные на нативном коде.
Данный вопрос не раз обсуждался на различных англоязычных форумах, например Stack Overflow:
stackoverflow.com/questions/16667903/android-studio-gradle-and-ndk
Однако, данная информация достаточно краткая, разрозненная и требует от читателя определённых знаний синтаксиса gradle. Цель моей статьи – предоставить читателям подробное русскоязычное объяснение, доступное даже тем, кто только начал работать с Android Studio и gradle.

Давайте рассмотрим 2 способа упаковки библиотек: ручной и автоматический.

Ручной способ упаковки:

Данный способ весьма неудобен, однако имеет место на существование. Допустимый случай применения на практике: наличие готовой библиотеки и отсутствие необходимости её изменять. В таком случае, описанные ниже операции потребуется выполнить лишь один раз.

Откройте build.gradle, находящийся по адресу » / Project/ /», в моём случае:

Рис.3 Местонахождение конфигурируемого файла build.gradle.

Данный файл изначально выглядит приблизительно так:

В самом низу есть раздел dependencies. Туда следует добавить такую строчку:

Читайте также:  Миррор линк для андроид какие телефоны поддерживают

compile fileTree(dir: ‘src/main/’, include: ‘*.jar’)

Рассмотрим эту команду: gradle в ходе компиляции будет вынужден включить дерево файлов (структуру файлов и папок, соответствующих заданной маске ), расположенное по адресу ‘src/main/’ (т.е. в том каталоге, где расположены исходные коды, а также созданный нами jar файл), при этом в качестве маски использован параметр ‘*.jar’, т.е. включатся будут все файлы с таким расширением. Обратите внимание, что в данном случае путь считается относительно месторасположения файла build.gradle.
В результате выполнения данной команды gradle распакует jar-файл и включит двоичные библиотеки в APK-файл.

Ознакомтесь с модифицированным файлом build.gradle, что бы не перепутать dependencies и buildscript.dependencies.

Читатель скорее всего подметит, что данный способ весьма неудобен при наличии необходимости частых изменений в нативном коде, так как после каждой перекомпиляции необходимо удалять старый jar-файл, переименовывать папку libs в lib, архивировать её, менять расширение архива. Поэтому воспользуемся мощью gradle и автоматизируем процесс.

Сборщик пакетов gradle позволяет создавать задания (функции), также в его возможности входит создание различных типов архивов, в том числе zip. Воспользуемся этим и добавим в build.gradle (расположение данного файла рассмотрено выше) такие строчки:

Данный отрывок кода можно добавить в любую часть файла, кроме существующих разделов, например в конец файла. Я поместил их перед разделом dependencies.
Этот код включает в себя task, который создает в папке build, находящийся по адресу » / Project/ /» подкаталог native-libs, в этой подпапке создается файл native-libs.jar, структура файла соответствует требуемой структуре java-библиотеки, содержащей бинарные библиотеки .so. Если Вы планируете использовать также статические библиотеки .a, то вместо строки:

from fileTree(dir: ‘src/main/libs’, include: ‘**/*.so’)

Вам следует использовать:

from fileTree(dir: ‘src/main/libs’, include: ‘**/*.*’)

Далее остается добавить в раздел dependencies строку:

compile fileTree(dir: «$buildDir/native-libs», include: ‘native-libs.jar’)

В ходе сборки эта команда включит содержимое созданной программно библиотеки native-libs.jar в APK-файл.

Пример build.gradle с данным кодом:

Обратите внимание, что у меня дирректории jni, libs расположены по адресу » / Project/ /src/main». Если в Вашем проекте эти папки лежат в другом месте, то Вам следует учесть это в формировании путей для всех команд.

Если всё сделано правильно, то Android Studio в ходе сборки проекта автоматически создаст в каталоге build правильную библиотеку и включит её в готовую программу. Таким образом, после каждой перекомпиляции нативного кода отпадает необходимость совершения каких-либо дополнительных действий и настроек, gradle сделает всё сам.

Теперь, как и было обещано в начале статьи, краткое резюме, описывающее лишь полный алгоритм без лишних комментариев:

Краткое резюме
  1. Открываем папку » / Project/ /src/main» и создаём там подпапку jni.
  2. Открываем файл » / Project/ /build.gradle», модифицируем раздел dependencies, после чего добавляем туда код:

Для включения также статических библиотек *.a (при их наличии) меняем строку

from fileTree(dir: ‘src/main/libs’, include: ‘**/*.so’)

from fileTree(dir: ‘src/main/libs’, include: ‘**/*.*’)

  • В подпапке jni размещаем файлы *.mk, *.h, *.c, пишем нативный код.
  • Открываем папку » / Project/ /src/main» в терминале.
  • Вводим в терминале команду /ndk-build
  • Запускаем проект.
  • Важно!
    Данная инструкция предназначена для операционных систем Unix (в моём случае — MacOSX). Для операционной системы MS Windows пункты 4 и 5 не актуальны, так как для компиляции нативных библиотек требуются дополнительные утилиты. Также, скорее всего, будет целесообразным изменить пути хранения библиотек на более удобные и учесть это в скрипте сборки.

    На этом я завершаю статью и откланиваюсь. Надеюсь, кому-то данная статья сэкономит время.

    Удачного Вам нативного программирования, главное — каждый раз не забывайте себя спрашивать, стоит ли использовать нативный код. Вполне могут быть java-аналоги, использование которых проще, а в большинстве случаев – лучше, так как сокращается время разработки, улучшается понимаемость кода другими, снижается сложность архитектуры приложения, а мощности современных устройств хватает на выполнение большинства задач даже в Dalvik VM.

    Источник

    Оцените статью