пятница, 24 февраля 2012 г.

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

При разработке приложений под Android можно использовать C/C++. Это даёт возможность использовать существующие наработки и библиотеки написанные на C. Создавать свой, более быстрый чем Java, код. Далее рассмотрим использование C++ кода в Android проекте, на примере простейшего приложения, с применением Eclipse под Windows.
Для использования C в Android проекте требуется NDK. Забрать пакет можно здесь http://developer.android.com/sdk/ndk/index.html Пакет распространяется свободно и бесплатен для использования.
Скачиваем пакет и распаковываем его в подходящую директорию. Например в c:\Android\android-ndk-r7\ Лучше использовать путь не содержащий пробелов. (Разработка под Windows с использованием NDK несколько неудобна. Если есть возможность, лучше использовать Linux или MacOS, создание программ будет проще. Весь необходимый софт под Linux и MacOS в наличии, включая Eclipse.)

Теперь приступаем к написанию приложения с использованием C++.
Создаём в Eclipse новый Android-проект old.cpp.test с классом CppTestActivity.
Добавляем  в класс загрузку будущей библиотеки в которой будет находится наш код на C++:
static { System.loadLibrary("CppTest"); }

И объявляем метод из библиотеки который мы будем использовать:
private native String getTextFromCpp();

Для начала это будет простейшая функция возвращающая строку.
Теперь можем использовать этот метод в Java-коде программы:
setTitle( getTextFromCpp() );

Устанавливаем титул приложения значением возвращаемым библиотечной функцией.
В результате должен получится такой код:
package old.cpp.test;

import android.app.Activity;
import android.os.Bundle;

public class CppTestActivity extends Activity {
 
 static { System.loadLibrary("CppTest"); }
 
 private native String getTextFromCpp();
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        setTitle( getTextFromCpp() );
    }
}

Теперь приступаем к созданию библиотеки из которой будет импортирован метод getTextFromCpp().
Создаём в проекте новую директорию jni/
Сначала нужно создать заголовочный файл, в котором будет объявлен импортируемый метод. Файл можно создать автоматически, при помощи утилиты javah, из установленного в системе пакета Java. Тут у меня возникли некоторые сложности. Если под MacOS достаточно зайти в директорию содержащую компилированные Java-классы проекта
cd <project_path>/bin/classes

И запустить утилиту javah
javah -jni old.cpp.test.CppTestActivity

В результате получим готовый заголовочный файл, который можно скопировать в папку с CPP файлом. То, под Windows javah.exe постоянно не мог найти необходимых для работы классов. Видимо, нужно ещё прописать какие-то пути в PATH. Пришлось создавать файл в ручную.
Содержимое файла CppTest.h:
#include <jni.h>

#ifndef _Included_old_cpp_test_CppTestActivity

#define _Included_old_cpp_test_CppTestActivity

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jstring JNICALL Java_old_cpp_test_CppTestActivity_getTextFromCpp (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif

#endif
Имена классов и функции выглядят несколько замысловато. Общий принцип такой: Java_[PACKAGE_NAME]_[CLASS_NAME]_[FUNCTION_NAME] Если в названии класса или методов присутствуют символы '_', то создание имени усложнится (описание).

Имея заголовочный файл, уже легко можно написать реализацию самого метода.
Содержимое файла CppTest.cpp:
#include <string.h>
#include <jni.h>
#include <cpptest.h>

JNIEXPORT jstring JNICALL Java_old_cpp_test_CppTestActivity_getTextFromCpp(JNIEnv * env, jobject obj) {
    return env->NewStringUTF( "Hello from C++!" );
}

Теперь отправляемся в командную строку. Заходим в директорию, где находится наш проект.
cd <path_to_project>

И запускаем в ней командный файл идущий в комплекте с NDK.
<path_to_ndk>\ndk-build.cmd

Получаем библиотеку готовую для использования в Java.Запускаем проект из Eclipse и радуемся результату.

Что можно сделать, чтоб упростить сборку проекта?

Добавляем в Eclipse поддержку NDK.
Help -> Install new software...
Выбираем в Work with - Indigo - http://download.eclipse.org/releases/indigo
И устанавливаем Sequoyah Android Native Code Support из раздела Mobile and  Device Development.
И в свойствах проекта добавляем поддержку нативного кода:
Теперь в свойствах проекта указываем какой командой нужно компилировать нативный код.
c:\Android\android-ndk-r7\ndk-build.cmd
По умолчанию там будет вызов bash скрипта ndk-buld.
Теперь Eclipse, при сборке проекта, сам будет вызывать скрипт сборки библиотеки. Это удобно если работа идёт в основном с C-кодом.

Так же для того чтоб IDE не ругался на ошибки в C-коде, нужно указать путь до нативных библиотек.
C:\Android\android-ndk-r7\platforms\android-14\arch-arm\usr\include

Источники:
http://developer.android.com/sdk/ndk/index.html
http://jia3ep.blogspot.com/2011/10/c-android-1.html
http://mindtherobot.com/blog/452/android-beginners-ndk-setup-step-by-step/
http://www.gliffer.ru/articles/android--hello-ndk/
http://marakana.com/forums/android/examples/49.html

1 комментарий:

  1. Утилиту javah нужно запускать из \src!!!
    чтобы она работала, достаточно добавить в SYSTEM PATH "C:\Program Files\Java\jdk1.7.0_51\bin", или другую папку, в которой находится javah.exe

    ОтветитьУдалить