- Учимся работать с Android NDK или как использовать C код в java проектах
- Когда нужно использовать Android NDK?
- Шаг 1: Установка Android NDK и настройка среды разработки
- Шаг 2: Создание нового проекта
- Шаг 3: Добавляем C код в Android проект
- Шаг 4: Вызов нативного кода из Java
- Шаг 5: Создаем Make file для нативного кода
- Шаг 6: компиляция нативного кода
- Шаг 7: Запуск приложения
- Шаг 8: Как вернуть объект из нативной функции
- Замечания
- Заключение
- Android NDK
- Почему стоит использовать Android NDK в вашем следующем проекте
- Для чего и как используется бинарный код
- Использование Android NDK
- Принцип работы Android NDK
- Установка и использование Android NDK в Ubuntu
- Установка с помощью Android Studio
- Создание или импорт проекта с бинарными компонентами
Учимся работать с Android NDK или как использовать C код в java проектах
Android NDK представляет собой набор утилит, позволяющих Вам включать код, написанный на C и C++, в ваше приложение. Такой код называется нативным(native), поскольку он не может быть выполнен на виртуальной машине и компилируется непосредственно в машинный код требуемой процессорной архитектуры.
Данный урок относится к разряду продвинутых. Подразумевается, что читатель уже имеет некоторые навыки, в частности
- Вы умеете программировать на Java и C
- Вы умеете работать с командной строкой
- Вы знаете, как узнать версии Cygwin, awk и других инструментов, которыми нам придется пользоваться
- Вы умеете разрабатывать приложения для Android
- У Вас настроена среда разработки для Android (в момент написания автор использовал Android 2.2)
- Вы используете Eclipse или можете транслировать инструкции по работе с eclipse на свою IDE.
Если какой-либо из указанных пунктов вызывает у вас затруднения, не беда, вы все равно вполне можете усвоить предлагаемый материал, правда некоторые шаги будут казаться Вам довольно сложными. Вообще вопрос использование Android NDK часто вызывает затруднения даже у матерых разработчиков. Скорей всего вам придется приложить значительные усилия, прежде чем вы сможете настроить свою среду разработки и написать работающий проект.
Когда нужно использовать Android NDK?
Обычно разработчики решают использовать нативный код в двух случаях: они хотят увеличить производительность своего приложения, или у них есть готовый C/C++ проект, который требуется с минимальными затратами портировать на Android. Давайте не будем спешить и разберемся, когда же целесообразно использовать NDK, а когда этого не нужно делать.
Наиболее часто программистами высказывается мнение, что NDK стоит использовать, когда приложение сильно нагружает процессор. Существуют алгоритмы, позволяющие полностью загрузить процессор через DalvikVM, в этом случае использование нативного кода действительно с большой вероятностью позволит получить выигрыш в производительности. Однако, не нужно забывать, что использование JIT компилятора также позволяет повысить производительность java кода. Многие думают, что использование в приложении машинного кода автоматически означает увеличение скорости работы приложения. На самом деле это не так. Переключение с выполнения java кода на машинный код и обратно несет с собой накладные расходы, поэтому использовать NDK стоит, только если у вас выполняется какой-нибудь долгий сложный расчет, полностью написанный на C, и в java коде не предполагается частое дерганье нативных функций.
Другой причиной, которая может побудить Вас использовать NDK является необходимость портирования готового приложения. Вполне логично не переписывать уже проверенные и отлаженные куски кода на java, а использовать NDK. Этот подход также позволит Вам в дальнейшем без особых затрат вносить параллельно правки в исходное и портированное на android приложение. В частности, такой подход оправдан в отношении приложений, использующих OpenGL ES.
Шаг 1: Установка Android NDK и настройка среды разработки
Прежде всего, Вам необходимо скачатьAndroid NDK. Для установки и нормальной работы нам также понадобятся утилиты Cygwin 1.7 или старше, awk последней версии, а также GNU Make 3.81 или старше.
После того, как Вы скачали архив с NDK, распакуйте его в какую-нибудь папку. Можно распаковать этот архив туда же, где лежит Android SDK. Путь к этой папке необходимо прописать в системной переменной PATH. В Windows для этих целей лучше настроить конфигурацию Cygwin.
Шаг 2: Создание нового проекта
Создайте новый Android проект. Чтобы избежать проблем в будущем сохраните проект так, чтобы путь к нему не содержал в себе символов пробела. Для примера создайте проект, в качестве названия пакета укажите «com.mamlambo.sample.ndk1», а в качестве Activity — «AndroidNDK1SampleActivity».
В корне проекта создайте папку с названием «jni». Именно здесь будет содержаться файлы с нативным кодом. Если Вы знакомы с JNI, то вам будет приятно узнать, что Android NDK по сути представляет собой JNI с ограниченным набором заголовочных файлов для компиляции C кода.
Шаг 3: Добавляем C код в Android проект
Создайте в папке jni файл с именем native.c и добавьте в него следующий код
Созданная таким образом функция берет параметр String у java объекта, конвертирует его в C-string и записывает в LogCat. Зубодробительное имя функции выбрано не случайно, оно несет в себе важную информацию: сначала указывается название паттерна («Java»), затем идут название пакета, имя класса и название метода. Каждая часть имени отделяется знаком подчеркивания.
Первые два параметра у функции имеют особое значение. Первый параметр определяет JNI среду и часто используется со вспомогательными функциями. Второй параметр является объектом Java, частью которого является функция.
Шаг 4: Вызов нативного кода из Java
Давайте создадим в нашем проекте кнопку, при нажатии на которую будем вызывать следующий код:
Необходимо также объявить функцию helloLog в классе, где она вызывается. Сделать можно с помощью строки
Таким образом, мы сообщаем компилятору и линковщику, что реализацию этой функции стоит искать в папке с нативным кодом.
Наконец, нужно загрузить библиотеку, куда в конечном счете будет скомпилирован код. Добавьте следующую инициализацию в класс Activity.
System.loadLibrary() обеспечивает загрузку библиотеки по имени. Вы можете использовать любое название.
Шаг 5: Создаем Make file для нативного кода
Для компиляции нативного кода в папке jni должен находиться Make file с именем «Android.mk». Ниже приведен код этого фала для нашего примера, то есть когда функция находится в файле native.c и в качестве имени библиотеки указано ndk1
Шаг 6: компиляция нативного кода
После того, как Вы написали код и добавили make файл в папку jni можно приступать к компиляции. Для этого нужно в командной строке (Если вы работаете в windows, запустите Cygwin) запустить ndk-build из папки проекта. Утилита ndk-build входит в состав Android NDK. Если Вы все сделали правильно, то вы должны увидеть что-то вроде этого
Шаг 7: Запуск приложения
Теперь можно запустить проект, нажать на кнопку и посмотреть, как изменится LogCat.
Может произойти одна из двух вещей: 1) ваш проект может запуститься и работать, как Вы того ожидаете. В этом случае примите мои поздравления. 2) Возникнет ошибка, которая в LogCat отобразиться как «Could not execute method of activity.» Ничего страшного. Обычно Eclipse сконфигурирован так, что при запуске проекта автоматически происходит его перекомпиляция. В случае, если эта опция отключена, то нужно вручную заставить Eclips перекомпилировать проект. Для этого перед запуском нужно вызвать менюProject->Clean from the Eclipse toolbar.
Шаг 8: Как вернуть объект из нативной функции
Следующий пример демонстрирует возможность нативных функций возвращать объекты, например String. Добавьте код в файл native.c
С помощью команды malloc мы создали буфер, куда затем с помощью sprintf поместили строку. Чтобы функция возвращала корректный результат, мы использовали JNI helper функцию NewStringUTF(), которая фактически создает Java объект на основании C строки. После этого мы очистили память с помощью команды free().
Для успешной компиляции проекта необходимо в native.c подключить заголовочный файл stdio.h. В классе Activity нужно также объявить новую функцию:
после этого с функцией getString можно работать, например следующим образом:
Замечания
Android NDK для своей работы требует Android SDK 1.5. С помощью NDK можно получить доступ ко многим API, например к OpenGL ES.
Нативный код компилируется в машинный код, соответствующий архитектуре процессора, и поскольку на разных телефонах используются процессоры разной архитектуры, у Вас может возникнуть проблемы с переносимостью программы. По умолчанию NDK производит компиляцию для ARMv5TE. Когда Вы запускаете свое приложение на эмуляторе, а не на реальном устройстве, этот машинный код выполняется не напрямую, а через еще один эмулятор процессора.
Заключение
Ну вот собственно и все. Думаю теперь вам понятно, как работать с нативным кодом. Во многих случаях его применение оправдано, однако бездумное применеие нативного кода в своих проектах может оказаться губительным.
Источник
Android NDK
Android NDK — (расшифровывается как Android Native Development Kit) — набор инструментов для создания приложений для ОС Android с использованием C/C++.
это и позволяет писать нативные приложения, работающие быстрее чем Java. Android использует альтернативный libc — Bionic, а также имеет встроенные библиотеки zlib, OpenGL ES, Vulkan и различные API.
Что такое Android NDK?
22 февраля 2021 (Обновление: 5 мар. 2021)
«Хотя это и позволяет писать нативные приложения, работающие быстрее чем Java, писать только на C/C++ нельзя, точка входа обязательно должна быть написана на Java», — это не правда. Полностью нативные андроид-приложения можно делать чуть ли не со времён 9-й версии API (android 2.3). Proof: https://developer.android.com/ndk/samples/sample_na
General GDA
Пофиксил
Я бы ещё текст прогнал через спелл чекер (пунктуация, опечатки) и согласовал предложение. А то «это . » и начинается с маленькой буквы, и читается как-то не очень. Но это мелочи.
Главное, что «работающие быстрее, чем Java», — это субъективное оценочное суждение. Мало того, что на android нету Java (да, программу на этом языке можно скомпилировать для работы на Android; но на устройствах нет Java VM, модель памяти отличается от Java и т.п.). Так ещё на C/C++ надо постараться написать быстрее. К примеру, в современных андроидах очень сложная среда исполнения, где есть пред-компиляция в нативный код ещё на момент инсталляции приложения. При обновлении ОС может (и делает) перекомпиляция такого кода. Ещё пример: лично я при должном старании определённый класс задач под андроид вполне могу написать на Java/Kotlin так, что будет быстрее, чем на C++. Как в плане скорости выполнения кода, так и в плане скорости его написания.
Раз форум у нас тут гемдеву посвящён. То я могу набросить пример: на Unity так вообще всё на C# пишется. И ничего. Тонны успешных проектов (гемдев проектов!) под мобильные телефоны.
Если и писать про «быстрее», то со ссылками на конкретные исследования или case studies.
General GDA, хотелось бы поподробнее, как вы напишите приложение, которое написано на Java так, чтобы оно работало быстрее внутреннего цикла нативной программы? В плане скорости работы кода.
Про скорость написания — это зависит от того, что уже для этого сделано.
Mirrel
ну, как пример из головы — если работа приложения состоит в обработке строк или структур, которые оно получает с помощью одних вызовов API и отображает с помощью других, то большую часть времени нативное приложение может заниматься перепаковыванием их в свое представление, а java — будет работать с данными «как есть».
Другой пример — оптимизатор java вполне может быть круче оптимизатора fpc и соответственно одни и те же вычисления будут преобразованы в более эффективный машинный код.
kipar, я не об этом спрашивал!
Есть (у абсолютно нативного приложения) свой внутренний цикл (как и в других системах). Из этого цикла и происходят всё вызовы этого самого приложения (Start, Stop, Pause, Resume . ).
Как, человек сможет, используя java-код, обойти скорость работы внутреннего цикла?
И, всеми данными, можно пользоваться из нативного кода. В дополнению к этому, многие данные на Android находятся именно в нативном виде, а внутренние библиотеки соединяют этот код для удобства работы с Java-кодом.
но это больше для информации.
Mirrel
> Start, Stop, Pause, Resume
эти операции делаются достаточно редко, чтобы игнорировать JNI-оверхед при их вызове. Да даже если Update — один JNI-вызов каждый кадр — это копейки.
Что если, я скажу, что можно полностью игнорировать JNI-вызовы?
Mirrel
> Что если, я скажу, что можно полностью игнорировать JNI-вызовы?
и писать при этом на яве?
Зачем? Лично я буду писать на Паскале. Другой народ на C/C++. Кто умеет, тот на каком ещё языке, кроме всех вышеперечисленных.
General GDA
То есть dalvik и art не выполняют (свой) java байт-код? Про скорость C/C++ согласен.
egoros7
Dalvik, вроде как — вполне себе jvm и исполняет байткод. Хотя и не совсем compliant. А art — он AOT.
kkolyan
>а art — он AOT
Ну при включении машинный код всё же компилируется из байт-кода. Или я не прав?
egoros7
Скорее всего да, т.к. это проще чем компилить из сорцов. Но я наверняка не знаю.
Источник
Почему стоит использовать Android NDK в вашем следующем проекте
Оригинал: Why should you need to pick Android NDK for your next project
Автор: Harigovind Thoyakkat
Дата публикации: 28 марта 2017 г.
Перевод: А.Панин
Дата перевода: 28 апреля 2017 г.
Android NDK (Native Development Kit) является очень популярным инструментарием, используемым для разработки приложений для мобильных устройств. Многие приложения из магазина приложений Android Market используют компоненты, разработанные с использованием языков программирования, отличных от Java, для достижения максимальной производительности. Исходя из этого NDK является инструментарием, помогающим разработчикам создавать компоненты для своих приложений с использованием компилируемых языков программирования для различных целей, начиная с достижения оптимальной производительности и заканчивая упрощением используемого кода.
Для чего и как используется бинарный код
Мы все знаем о том, что процесс разработки приложений для Android тесно связан с использованием языка программирования Java, а также о том, что использование данного языка программирования значительно упрощает жизнь разработчиков, ведь они могут использовать элегантную объектно-ориентированную модель Java. Приложения или алгоритмы, реализованные на языке Java, преобразуются в специальный байткод, который выполняется аналогичным образом на всех поддерживаемых платформах. При этом виртуальная машина Java или JVM (Java Virtual Machine) ответственная за JIT-компиляцию и исполнение байткода Java, доступна практически для всех существующих платформ, начиная с мэйнфреймами и заканчивая мобильными телефонами.
Однако, в случае системы Android, которая используется главным образом на смартфонах и планшетных компьютерах, ключевым фактором становится достижение максимальной производительности приложения на используемом аппаратном обеспечении. Исходный код Java, как говорилось выше, предварительно преобразуется в байткод. Это именно тот байткод, который исполняется с небольшими отличиями на платформах, для которых доступна виртуальная машина Java. В конце концов все приложение исполняется в рамках виртуальной машины на устройстве, работающем под управлением Android.
Что касается разработки приложений для Android, описанный выше фактор является незначительным недостатком. Но программирование на языке Java может оказаться достаточно сложным ввиду постоянного усложнения кода и затруднения его понимания. Более того, использование мультиплатформенного байткода и виртуальной машины обуславливает значительные затраты вычислительных ресурсов устройства.
Другим важным фактором, который требует внимания, является мультиплатформенный код. Если нам нужно создать программу для множества аппаратных платформ, мы можем переписать большую часть кода, относящегося к контроллеру и отображению, для каждой из платформ, что является не самым разумным решением. Но весь код, относящийся к контроллеру, должен быть обязательно портирован на языки C и C++, так, как практически все мобильные платформы поддерживают их; таким образом, если нам удастся реализовать логику в рамках библиотек на языках C и C++ и впоследствии использовать ее на нескольких платформах, нам удастся максимально сократить потери производительности приложения. В подобных случаях мы будем использовать код на языках C и C++ вместе с привычным кодом на языке Java или «мультиплатформенным кодом».
При использовании компилируемого языка программирования исходный код компилируется непосредственно в машинный код для центрального процессора, а не в промежуточное представление, такое, как в языке Java. Таким образом разработчики приложений могут создавать приложения с оптимальной производительностью для различных Android-устройств. Фрагменты компилируемого кода могут быть структурированы в рамках одной разделяемой библиотеки, функции из которой могут вызываться из кода на языке Java. Отдельная разделяемая библиотека должна создаваться для каждой из поддерживаемых архитектур центральных процессоров. Большая часть ее исходного кода при этом может оставаться неизменной. Скомпилированные разделяемые библиотеки должны быть добавлены в файл .apk вашего приложения. С учетом всего вышесказанного, фундаментальная модель приложений Android не изменится.
Использование Android NDK
Android NDK является инструментарием, позволяющим реализовывать части приложения для Android на таких компилируемых языках программирования, как C и C++ и содержит библиотеки для управления активностями и доступа к физическим компонентам устройства, таким, как различные сенсоры и дисплей.
Android NDK интегрирован с инструментами из набора компонентов для разработки программного обеспечения (Android SDK), а также с интегрированной средой разработки Android Studio или устаревшей средой разработки Eclipse ADT. Однако, NDK не может использоваться отдельно.
Принцип работы Android NDK
Сердцем пакета Android NDK является сценарий ndk-build , который отвечает за автоматический обход файлов проекта Android (разработка каждого нового приложения для Android с помощью интегрированной среды разработки, такой, как Android Studio или Eclipse начинается с создания файлов нового проекта) и собирает информацию о том, какие компоненты нужно компилировать. Данный сценарий также ответственен за генерацию бинарных файлов и копирование этих бинарных файлов в директорию файлов проекта приложения.
Мы можем использовать ключевое слово native для того, чтобы компилятор знал, что данный фрагмент реализован в рамках компилируемого кода. Например:
Также в процессе сборки проекта создаются разделяемые библиотеки (Native Shared Libraries, с расширением .so ) и статические библиотеки (Native Shared Libraries, с расширением .a ), которые могут связываться с другими библиотеками. Бинарный интерфейс приложения (Application Binary Interface, ABI) использует разделяемые библиотеки с расширением .so для исполнения машинного кода в системе в процессе работы приложения.
Весь компилируемый код исполняется посредством интерфейса под названием Java Native Interface (JNI), который позволяет связать друг с другом компоненты на языках Java и C/C++.
Для сборки проекта с помощью сценария ndk-build нам придется создать два файла: Android.mk и Application.mk . Оба этих файла должны размещаться в директории JNI . Файл Android.mk описывает модуль и его имя, флаги сборки, используемые библиотеки файлы исходного кода, которые должны компилироваться, а файл Application.mk — бинарные модули, необходимые для работы приложения.
Установка и использование Android NDK в Ubuntu
Android NDK поставляется в формате самораспаковывающегося архива. По этой причине нам придется лишь установить бит исполнения и распаковать его:
В результате компоненты NDK будут сохранены в текущей рабочей директории.
Распаковка в ручном режиме
Ввиду того, что файл с расширением .bin является ничем иным, как самораспаковывающимся архивом формата 7-Zip, мы можем извлечь его содержимое вручную с помощью следующей команды:
Пакет с компонентами архиватора 7-Zip доступен из официального репозитория Ubuntu и может быть установлен, к примеру, с помощью команды apt-get:
Установка с помощью Android Studio
Мы можем установить Android NDK с помощью компонента SDK Manager непосредственно из Android Studio.
Для этого после открытия проекта следует осуществить переход в рамках главного меню окна Tools > Android > SDK Manager . После этого нужно установить флажки напротив названий компонентов LLDB , CMake и NDK . Далее нужно просто применить изменения с помощью соответствующей кнопки.
Создание или импорт проекта с бинарными компонентами
После настройки Android Studio мы можем создать новый проект с поддержкой языков программирования C/C++. Однако, если нам понадобится добавить или импортировать существующий код на этих языках в проект Android Studio, мы будем вынуждены выполнить описанные ниже действия.
Для начала следует создать новые файлы исходного кода с использованием упомянутых языков программирования и добавить их в проект, открытый в Android Studio. Мы можем пропустить этот шаг, если в проекте уже имеются подобные файлы или нам нужно импортировать в него предварительно скомпилированную библиотеку.
Сценарий сборки CMake позволяет сообщить одноименной системе сборки о том, как нужно осуществлять компиляцию файлов исходного кода и сборку результирующей бинарной библиотеки. Этот файл также необходим для импорта и связывания с нашей библиотекой существующих или поставляемых в комплекте NDK библиотек. Мы также можем без каких-либо последствий пропустить данный шаг в том случае, если в комплекте поставки нашей существующей бинарной библиотеки уже содержится файл сценария сборки CMakeLists.txt или она использует компонент ndk-build и в ее комплекте поставки содержится файл сценария сборки Android.mk .
Далее следует сообщить Gradle о существовании нашей бинарной библиотеки путем указания пути к файлу сценария сборки CMake или ndk-build . Gradle использует указанный сценарий сборки для импорта исходного кода в проект Android Studio и упаковки результирующей бинарной библиотеки (файла с расширением .so ) в файл пакета формата APK.
Важное замечание: если в рамках проекта используется устаревший инструмент ndkCompile , нам придется открыть файл build.poperties и удалить из него следующую строку кода перед настройкой Gradle с целью использования CMake или ndk-build:
Теперь мы можем собрать и выполнить наше приложение путем нажатия на кнопку Run . Gradle будет рассматривать процесс CMake или ndk-build в качестве зависимости, которая должна быть собрана, осуществлять сборку бинарной библиотеки и упаковывать ее в файл пакета формата APK.
После запуска приложения на устройстве или в эмуляторе мы сможем использовать функции различных интегрированных сред разработки, таких, как Android Studio, для его отладки.
Все это свидетельствует о важности Android NDK для разработчиков приложений для платформы Android. Например, данный набор программных компонентов позволяет создателям игровых движков лучше оптимизировать версии своих продуктов для Android, в результате чего они будут выдавать более впечатляющие графические эффекты, затрачивая на них меньше системных ресурсов.
Процесс создания простого приложения на основе Android NDK не связан с какими-либо сложностями. Однако, каждому разработчику следует уяснить один важный момент: набор программных компонентов Android NDK разрабатывался для использования в определенных случаях и не должен применяться при разработке любых приложений.
Android NDK может как помочь в процессе разработки приложения, так и максимально осложнить его. Не является тайной и то, что использование бинарного кода на платформе Android в некоторых случаях не приводит к заметному повышению производительности приложения (хотя в большинстве случаев его производительность все же повышается), но при этом оно в любом случае усложняет его код. Обычно повышение производительности приложений достигается благодаря задействованию кода со специфичными для используемого центрального процессора инструкциями. Но в общем случае рекомендуется использовать NDK лишь тогда, когда производительность приложения является критически важным параметром, а не тогда, когда разработчику удобнее писать код на языках C/C++.
В качестве заключения следует сказать о том, что не существует незыблемых правил, регламентирующих возможные случаи использования NDK, поэтому вам всегда стоит обращаться к своими знаниям, опыту и интуиции.
Источник