Основы Android NDK: работа с C/C++ кодом

Опубликовано: 01.09.2018

видео Основы Android NDK: работа с C/C++ кодом

[UNITY 5] Установка JDK, SDK для сборки на Android.

Использование нативного кода, написанного на C или С++ — это тема, которую многие разработчики не затрагивают вовсе. Но порой использование C++ в приложениях намного упрощает/ускоряет разработку. В этой статье будут рассмотрены основные принципы работы с native кодом.



Предварительная настройка

Если у вас ещё не настроен Eclipse, то читаем как настроить Eclipse для работы с Android . Только помимо того, что в статье сказано, при установке ещё необходимо выбрать и установить NDK плагин.

Так же вам необходимо установить CDT для Eclipse. Под Виндой вам вроде как ещё понадобиться установить Cygwin .


Урок 2. Установка и настройка Android Studio. Установка JDK. Настройка Android SDK | StartAndroid

Теперь необходимо создать проект и прописать пути.

В проекте будет создана папка jni, где вы должны размещать файлы с C++ кодом. В ранних версиях был баг, когда Eclipse не мог верно настроить пути до некоторых хэдеров из NDK. В последней версии всё нормально. Просто очистите проект (Clean project), а затем перестройте его (Build project).


R.id.team: Вебинар 1. Программирование под android. Знакомство

Зачем нужен NDK?

Думаю, необходимо предварительно объяснить, когда вообще стоит (и стоит ли?) использовать ndk. Многие советуют использовать C++, когда требуются какие-то большие/сложные вычисления. Что значит сложно? =/ В общем, лучше назову конкретные случаи, когда использование NDK оправдано:

Возможности NDK огромны. Вы можете из Java вызывать C++ методы. В то же время, вам ничто не машет вызывать Java методы из C++. Даже есть возможность создавать приложение практически без использования Java, используя NativeActivity (API 9 и выше).

Java. Путешествие в Native (или туда и обратно).

Да простит меня профессор за упоминание его работы (: И так, рассмотреть всё в рамках одной статьи невозможно. Поэтому, для начала реализуем лишь вызов native методов из Java.

Перечислю кратко основные моменты при работе с native:

Создание файлов с C++ кодом. Определение C++ методов для экспорта. Создание .mk файлов. Генерация библиотеки. Подключение библиотеки в Java и вызов C++ методов.

Создание файлов с C++ кодом

В native определим всего 3 метода: передача строки, изменение строки, получение строки.

Создадим для начала файл def.h , подключим пару нужных файлов и определим методы для вывода в консоль.

#include <android/log.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #ifdef __ANDROID__ #define LOG_TAG "MyNative" #define STRINGIFY(x) #x #define LOG_TAG __FILE__ ":" STRINGIFY(__MyNative__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) #endif

Создадим файл MyNative.h и определим в нём спецификации методов для экспорта, чтоб вызывать их из Java кода потом.

#include <def.h> #include <jni.h> char MyStr[80]; extern "C" { JNIEXPORT void Java_ru_suvitruf_androidndk_AndroidNDK_SetString(JNIEnv * env, jobject obj, jstring str); JNIEXPORT void Java_ru_suvitruf_androidndk_AndroidNDK_ChangeString(JNIEnv * env, jobject obj); JNIEXPORT jstring Java_ru_suvitruf_androidndk_AndroidNDK_GetString(JNIEnv * env, jobject obj); }

Теперь все три метода можно вызвать из Java кода. Я этот код ручками писал. Но можно заюзать javah , которая будет сама генерить эти заголовки. extern "C" нужен, чтобы компилятор C++ не менял имена объявленных функций.

Стоит немного сказать про наименование методов. Java_ — обязательный префикс. ru_suvitruf_androidndk , так как у нас пакет ru.suvitruf.androidndk , ну а дальше наименование класса и метода на стороне Java. В каждой функции в качестве аргумента имеется JNIEnv* — интерфейс для работы с Java, при помощи него можно вызывать Java-методы, создавать Java-объекты. Второй обязательный параметр — jobject или jclass — в зависимости от того, является ли метод статическим. Если метод статический, то аргумент будет типа jclass (ссылка на класс объекта, в котором объявлен метод), если не статический — jobject — ссылка на объект, у которого был вызван метод.

Ну и создадим MyNative.cpp с реализацией методов.

#include <MyNative.h> JNIEXPORT void Java_ru_suvitruf_androidndk_AndroidNDK_SetString(JNIEnv * env, jobject obj, jstring str){ jboolean isCopy; const char * Str; Str = env->GetStringUTFChars(str, &isCopy); strcpy(MyStr,Str); LOGI("string = \"%s\"",MyStr); } void ChangeStr(){ strcat(MyStr," and bb."); } JNIEXPORT void Java_ru_suvitruf_androidndk_AndroidNDK_ChangeString(JNIEnv * env, jobject obj){ ChangeStr(); LOGI("string after change = \"%s\"",MyStr); } JNIEXPORT jstring Java_ru_suvitruf_androidndk_AndroidNDK_GetString(JNIEnv * env, jobject obj){ LOGI("returned string = \"%s\"",MyStr); return env->NewStringUTF(MyStr); }

В этом файле описаны глобальные настройки для сборки либы.

# Без этой строчки ничего не будет работать (: APP_STL:=stlport_static # Список модулей/либ, которые нужна забилдить. Они будут такие же как в LOCAL_MODULE в Android.mk файле APP_MODULES := AndroidNDK # Указываем под какой arm собирать. Не обязательный параметр. APP_ABI := armeabi armeabi-v7a # Платформа, под которую билдим. Не обязательный параметр. APP_PLATFORM := android-10

Работа с Android.mk

Здесь указываем параметры/настройки по линковке и прочее, чтобы собрать либу.

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # имя нашего модуля, который будет вызываться в Java при помощи System.loadLibrary() LOCAL_MODULE := AndroidNDK LOCAL_DEFAULT_CPP_EXTENSION := cpp #список файлов, который нужно собрать LOCAL_SRC_FILES := MyNative.cpp #список библиотек из ndk, которые надо включить в сборку LOCAL_LDLIBS := -llog -landroid include $(BUILD_SHARED_LIBRARY)

В Android.mk вообще есть не мало всяких флагов и прочего. Можно добавлять в сборку уже готовые библиотеки и т.д. В следующих статьях напишу, как это делается.

После того, как вы создали C++ файлы и .mk сделали, можно забилдить проект, тогда в папке obj появится библиотека libAndroidNDK.so.

Подключение библиотеки в Java и вызов C++ методов.

Теперь остаётся только написать Java код. Сделаем простенькое приложение. Разместим поле для ввода текста, три кнопки (передача текста в native, изменение текста в native и возврат изменённой строки из native) и поле для нового текста, который получили из native кода.

Для того, чтобы использовать native методы создадим класс AndroidNDK .

public class AndroidNDK { // Загрузка модуля «AndroidNDK» — нативной библиотеки, в которой реализованы методы. // Название этого модуля задается в файле Android.mk. static { System.loadLibrary("AndroidNDK"); } public static native void SetString(String str); public static native void ChangeString(); public static native String GetString(); }

Ключевое тут:

static {

System.loadLibrary("AndroidNDK");

}

Этот код выполнит загрузку нашей библиотеки, в которой реализованы методы. Обратите ещё раз внимание на этот класс и методы и вспомните наименование методов при экспорте в native коде: Java_ru_suvitruf_androidndk_AndroidNDK_ChangeString .

Java код по обработке нажатий на кнопки писать не буду, ибо это тривиальная задача. В любом случае, если понадобиться, можете посмотреть в исходниках к статье. В логе будет вот что:

Для знакомства с native достаточно написал. Можете скачать исходники AndroidNDK.rar .

rss