Работа с Android NDK под Windows
от aNNiMON
Бывают случаи, когда жуть как надо использовать код, написанный на C/C++ в своём приложении под Android. Это может быть что угодно: какая-нибудь библиотека по обработке музыкального формата (тот же FLAC к примеру), игра с использованием OpenGL, проект OpenCV и т.д. Вот я сегодня и расскажу, как же использовать нативный код в своих проектах.
Вообще, когда дело доходит до использования Си, Windows тут же отходит на задний план. Об этом свидетельствует множество туториалов по Android NDK - почти все они ориентированы на Unix или Mac OS. Но не будем отчаиваться, лично мне удалось успешно "скрестить" существующую библиотеку на Си с Java, и делал я это под Windows. Приступим!
Сначала качаем Android NDK с этой страницы. Затем распаковываем в папку, чтобы в пути не было пробелов и кириллицы (например D:\Android\android-ndk). Теперь запускаем Eclipse IDE и в меню Window->Preferences->Android->NDK указываем путь к NDK. Готово!
Создаём новый Android проект.
Структура проекта выглядит так:
Теперь щелкаем правой кнопкой мыши на заголовке проекта и выбираем Android Tools -> Add Native Support. Появится окно, в котором можно задать название библиотеки. Пусть будет "ndkexample". Нажимаем Finish. Eclipse автоматически создаст папку jni, в которой и будет вестись работа с кодом на Си.
* ndkexample.cpp - главный класс библиотеки.
* Android.mk - Make-файл, который указывает компилятору, что и как собирать.
Давайте определим, что будем делать в нативной библиотеке. Выводить строки в лог, как это делается в большинстве туториалов неинтересно. Поэтому давайте-ка подсчитаем сумму одного миллиона рандомных чисел!
Создадим вспомагательный класс NdkLib в пакете com.annimon.ndkexample, в котором будет определён нативный метод:
Теперь на основе этого класса создадим заголовок для C++ класса, используя утилиту javah из JDK. Заходим из командной строки в папку bin\classes нашего проекта и пишем "javah <путь к папке jni> <имя класса с нативными методами с учетом пакета>". В моём случае команда выглядит так: "javah -d C:\Users\aNNiMON\Documents\Eclipse\TestWorkspace\NdkExample\jni com.annimon.ndkexample.NdkLib". Если всё сделали верно, то в папке jni появится файл "com_annimon_ndkexample_NdkLib.h" с таким вот содержимым:
Теперь реализовываем метод Java_com_annimon_ndkexample_NdkLib_getInfo в ndkexample.cpp:
Добавляем в класс NdkLib строки для инициализации библиотеки:
Класс MainActivity приводим к следующему виду:
Компилируем проект. Если выскочила ошибка "Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8", то добавьте в папку jni файл Application.mk с содержимым:
APP_PLATFORM := android-8
Вот такой результат выдал эмулятор, впечатляет?
А вот данные моего Sony Xperia Pro:
А какие у вас результаты?
P.S. Весь проект тут (бинарник тоже): NdkExample.zip
Вообще, когда дело доходит до использования Си, Windows тут же отходит на задний план. Об этом свидетельствует множество туториалов по Android NDK - почти все они ориентированы на Unix или Mac OS. Но не будем отчаиваться, лично мне удалось успешно "скрестить" существующую библиотеку на Си с Java, и делал я это под Windows. Приступим!
Сначала качаем Android NDK с этой страницы. Затем распаковываем в папку, чтобы в пути не было пробелов и кириллицы (например D:\Android\android-ndk). Теперь запускаем Eclipse IDE и в меню Window->Preferences->Android->NDK указываем путь к NDK. Готово!
Создаём новый Android проект.
Структура проекта выглядит так:
Теперь щелкаем правой кнопкой мыши на заголовке проекта и выбираем Android Tools -> Add Native Support. Появится окно, в котором можно задать название библиотеки. Пусть будет "ndkexample". Нажимаем Finish. Eclipse автоматически создаст папку jni, в которой и будет вестись работа с кодом на Си.
* ndkexample.cpp - главный класс библиотеки.
* Android.mk - Make-файл, который указывает компилятору, что и как собирать.
Давайте определим, что будем делать в нативной библиотеке. Выводить строки в лог, как это делается в большинстве туториалов неинтересно. Поэтому давайте-ка подсчитаем сумму одного миллиона рандомных чисел!
Создадим вспомагательный класс NdkLib в пакете com.annimon.ndkexample, в котором будет определён нативный метод:
- package com.annimon.ndkexample;
- public class NdkLib {
- static native String getInfo(int numElements);
- }
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_annimon_ndkexample_NdkLib */
- #ifndef _Included_com_annimon_ndkexample_NdkLib
- #define _Included_com_annimon_ndkexample_NdkLib
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_annimon_ndkexample_NdkLib
- * Method: getInfo
- * Signature: (I)Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_annimon_ndkexample_NdkLib_getInfo
- (JNIEnv *, jclass, jint);
- #ifdef __cplusplus
- }
- #endif
- #endif
Теперь реализовываем метод Java_com_annimon_ndkexample_NdkLib_getInfo в ndkexample.cpp:
- #include "com_annimon_ndkexample_NdkLib.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- JNIEXPORT jstring JNICALL Java_com_annimon_ndkexample_NdkLib_getInfo(
- JNIEnv * env, jclass thiz, jint numElements) {
- long startTime = time(NULL);
- srand(startTime);
- long sum = 0;
- for (int i = 0; i < numElements; i++) {
- sum += (rand() % 20 - 10); // -10..10
- }
- long totalTime = time(NULL) - startTime;
- char info[96] = { 0 };;
- sprintf(info, "Sum: %ld\n Total time: %ld", sum, totalTime);
- return env->NewStringUTF(info);
- }
Добавляем в класс NdkLib строки для инициализации библиотеки:
- static {
- System.loadLibrary("ndkexample");
- }
Класс MainActivity приводим к следующему виду:
- package com.annimon.ndkexample;
- import java.util.Random;
- import android.os.Bundle;
- import android.widget.TextView;
- import android.app.Activity;
- public class MainActivity extends Activity {
- private static final int ELEMENTS = 1000000;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- StringBuilder sb = new StringBuilder();
- sb.append("Native:\n").append(NdkLib.getInfo(ELEMENTS));
- sb.append("\n\nJava:\n").append(getInfo(ELEMENTS));
- TextView tv = new TextView(this);
- tv.setText(sb.toString());
- setContentView(tv);
- }
- private String getInfo(int numElements) {
- long startTime = System.currentTimeMillis();
- Random rnd = new Random(startTime);
- long sum = 0;
- for (int i = 0; i < numElements; i++) {
- sum += (rnd.nextInt(20) - 10);
- }
- long totalTime = System.currentTimeMillis() - startTime;
- return String.format("Sum: %d\n Total time: %d", sum, totalTime);
- }
- }
Компилируем проект. Если выскочила ошибка "Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8", то добавьте в папку jni файл Application.mk с содержимым:
APP_PLATFORM := android-8
Вот такой результат выдал эмулятор, впечатляет?
А вот данные моего Sony Xperia Pro:
А какие у вас результаты?
P.S. Весь проект тут (бинарник тоже): NdkExample.zip