В этой части мы не будем писать на Java. Напишем программу под Android используя только C++. Это будет просто.
Нам нужна поддержка фич NDK, которые появились только в версии Android 2.3. Поэтому сначала нужно установить SDK с поддержкой Android 2.3:
Если ваша железка не поддерживает такую ОС, то ничего страшного — мой телефон тоже безнадежно устарел, я же купил его целых 6 месяцев назад :) А более новое устройство мне заполучить для тестов не удалось, поэтому я буду запускать примеры на эмуляторе, который входит в состав SDK. Если компьютер, на котором ведется разработка, достаточно быстрый, то неудобств немного. На реальной железке эти примеры также должны работать.
Далее, создаем проект так, как это было описано в статье по установке Eclipese + Android SDK. Только Build Target у нас теперь Android 2.3 и убираем галку с пункта Create Activity, так как мы договорились обойтись в этот раз без Java:
В конце не забываем вызвать Add Native Support (тыкаем правой кнопкой в проект, далее выпадает меню, в котором выбираем Android Tools > Add Native Support). В итоге получим почти пустой проект, но в нем уже есть AndroidManifest.xml, который нужно поправить в соответствии со следующим примером:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.blogspot.jia3ep.test_native"
android:versionCode="1"
android:versionName="1.0" >
<!-- В документации ошибочно указано, что можно писать 8 - это не так.
NativeActivity появилась только в Android 2.3 -->
<uses-sdk android:minSdkVersion="9" />
<!-- Этот .apk не содержит Java кода, поэтому ставим hasCode = false. -->
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:hasCode="false">
<!-- Используем встроенную NativeActivity. -->
<activity android:name="android.app.NativeActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
<!-- Указываем название библиотеки, которая содержит NativeActivity -->
<meta-data android:name="android.app.lib_name"
android:value="test_native" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Вместо activity, которую мы создавали в предыдущих примерах, указываем встроенную в NDK NativeActivity. Её реализация находится в библиотекеandroid_native_app_glue, которую нужно прилинковать. Для этого меняемAndroid.mk, заодно добавим подгрузку библиотек, которые нам пригодятся. После этого файл будет выглядеть так:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test_native### Add all source file names to be included in lib separated by a whitespace
LOCAL_SRC_FILES := test_native.cpp
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
### Load additional libs
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM### Link NativeActivity support
LOCAL_STATIC_LIBRARIES := android_native_app_glue
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/native_app_glue)
В проекте уже есть cpp файл test_native.cpp, который был создан автоматически при конвертации проекта. В него добавим функцию android_main, которая используетandroid_native_app_glue. Она запускается в отдельном потоке со своим циклом обработки сообщений. Все очень похоже на WndProc в Windows. Но для начала в test_native.cpp добавляем всего несколько строк:
#include <android_native_app_glue.h>
void android_main(struct android_app* state) {
// Make sure glue isn't stripped.
app_dummy();
}
Чтобы синтаксический анализатор Eclipse не ругался, добавляем путь к android_native_app_glue.h как это было описано во второй части. У меня получился такой путь:/home/user/Android/android-ndk-r6b/sources/android/native_app_glue.
Получаем минимальный пример, который можно скомпилировать и запустить. Делать он ничего не будет и не будет отвечать на кнопки устройства, так как цикла обработки сообщений тут пока нет:
Теперь добавим примитивный цикл обработки сообщений. Для этого нужно в цикле вызывать функцию ALooper_pollAll, которая вычитывает все сообщения из очереди. Для обработки этих сообщений определим два почти пустых метода: engine_handle_input и engine_handle_cmd. Получаем следующий код:
#include <string.h>
#include <jni.h>
#include <android_native_app_glue.h>
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "test_native", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "test_native", __VA_ARGS__))
/**
* Process the next input event.
*/
static int32_t engine_handle_input(struct android_app* app,
AInputEvent* event) {
return 0;
}
/**
* Process the next main command.
*/
static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
switch (cmd) {
case APP_CMD_SAVE_STATE:
// The system has asked us to save our current state.
break;
case APP_CMD_INIT_WINDOW:
// The window is being shown.
break;
case APP_CMD_TERM_WINDOW:
// The window is being hidden or closed.
break;
case APP_CMD_GAINED_FOCUS:
// Our app gains focus.
break;
case APP_CMD_LOST_FOCUS:
// Our app looses focus.
break;
}
}
void android_main(struct android_app* state) {
// Make sure glue isn't stripped.
app_dummy();
LOGW( "test_native entered main" );
state->userData = NULL;
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
// loop waiting for stuff to do.
while (1) {
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
// We will block forever waiting for events.
while ((ident = ALooper_pollAll(-1, NULL, &events, (void**) &source))
>= 0) {
// Process this event.
if (source != NULL) {
source->process(state, source);
}
// Check if we are exiting.
if (state->destroyRequested != 0) {
LOGW( "test_native exited main" );
return;
}
}
}
}
При отладке можно видеть сообщения о старте и выходе, которые я добавил в android_main:
В следующей части посмотрим как в рамках этого примера можно использовать OpenGL, чтобы увидеть что-то кроме черного экрана.
Скажем спасибо: ему
Комментариев нет:
Отправить комментарий