diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android/input.h b/ndk/platforms/android-9/arch-arm/usr/include/android/input.h index 75be85abb..014b6a314 100644 --- a/ndk/platforms/android-9/arch-arm/usr/include/android/input.h +++ b/ndk/platforms/android-9/arch-arm/usr/include/android/input.h @@ -534,10 +534,11 @@ struct AInputQueue; typedef struct AInputQueue AInputQueue; /* - * Add this input queue to a looper for processing. + * Add this input queue to a looper for processing. See + * ALooper_addFd() for information on the callback and data params. */ void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, - ALooper_callbackFunc callback, void* data); + ALooper_callbackFunc* callback, void* data); /* * Remove the input queue from the looper it is currently attached to. diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android/looper.h b/ndk/platforms/android-9/arch-arm/usr/include/android/looper.h index 90a89838a..291721624 100644 --- a/ndk/platforms/android-9/arch-arm/usr/include/android/looper.h +++ b/ndk/platforms/android-9/arch-arm/usr/include/android/looper.h @@ -24,25 +24,151 @@ extern "C" { #endif +/** + * ALooper + * + * A looper is the state tracking an event loop for a thread. + * Loopers do not define event structures or other such things; rather + * they are a lower-level facility to attach one or more discrete objects + * listening for an event. An "event" here is simply data available on + * a file descriptor: each attached object has an associated file descriptor, + * and waiting for "events" means (internally) polling on all of these file + * descriptors until one or more of them have data available. + * + * A thread can have only one ALooper associated with it. + */ struct ALooper; typedef struct ALooper ALooper; +/** + * For callback-based event loops, this is the prototype of the function + * that is called. It is given the file descriptor it is associated with, + * a bitmask of the poll events that were triggered (typically POLLIN), and + * the data pointer that was originally supplied. + * + * Implementations should return 1 to continue receiving callbacks, or 0 + * to have this file descriptor and callback unregistered from the looper. + */ typedef int ALooper_callbackFunc(int fd, int events, void* data); +/** + * Return the ALooper associated with the calling thread, or NULL if + * there is not one. + */ ALooper* ALooper_forThread(); -ALooper* ALooper_prepare(); +enum { + /** + * Option for ALooper_prepare: this ALooper will accept calls to + * ALooper_addFd() that do not have a callback (that is provide NULL + * for the callback). In this case the caller of ALooper_pollOnce() + * or ALooper_pollAll() MUST check the return from these functions to + * discover when data is available on such fds and process it. + */ + ALOOPER_PREPARE_ALLOW_NON_CALLBACKS = 1<<0 +}; -int32_t ALooper_pollOnce(int timeoutMillis); +/** + * Prepare an ALooper associated with the calling thread, and return it. + * If the thread already has an ALooper, it is returned. Otherwise, a new + * one is created, associated with the thread, and returned. + * + * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. + */ +ALooper* ALooper_prepare(int32_t opts); +enum { + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): one or + * more callbacks were executed. + */ + ALOOPER_POLL_CALLBACK = -1, + + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): the + * timeout expired. + */ + ALOOPER_POLL_TIMEOUT = -2, + + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): an error + * occurred. + */ + ALOOPER_POLL_ERROR = -3, +}; + +/** + * Wait for events to be available, with optional timeout in milliseconds. + * Invokes callbacks for all file descriptors on which an event occurred. + * + * If the timeout is zero, returns immediately without blocking. + * If the timeout is negative, waits indefinitely until an event appears. + * + * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. + * + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOPER_POLL_ERROR if an error occurred. + * + * Returns a value >= 0 containing a file descriptor if it has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outEvents and outData will contain the poll + * events and data associated with the fd. + * + * This method does not return until it has finished invoking the appropriate callbacks + * for all file descriptors that were signalled. + */ +int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData); + +/** + * Like ALooper_pollOnce(), but performs all pending callbacks until all + * data has been consumed or a file descriptor is available with no callback. + * This function will never return ALOOPER_POLL_CALLBACK. + */ +int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData); + +/** + * Acquire a reference on the given ALooper object. This prevents the object + * from being deleted until the reference is removed. This is only needed + * to safely hand an ALooper from one thread to another. + */ void ALooper_acquire(ALooper* looper); +/** + * Remove a reference that was previously acquired with ALooper_acquire(). + */ void ALooper_release(ALooper* looper); -void ALooper_setCallback(ALooper* looper, int fd, int events, +/** + * Add a new file descriptor to be polled by the looper. If the same file + * descriptor was previously added, it is replaced. + * + * "fd" is the file descriptor to be added. + * "events" are the poll events to wake up on. Typically this is POLLIN. + * "callback" is the function to call when there is an event on the file + * descriptor. + * "id" is an identifier to associated with this file descriptor, or 0. + * "data" is a private data pointer to supply to the callback. + * + * There are two main uses of this function: + * + * (1) If "callback" is non-NULL, then + * this function will be called when there is data on the file descriptor. It + * should execute any events it has pending, appropriately reading from the + * file descriptor. + * + * (2) If "callback" is NULL, the fd will be returned by ALooper_pollOnce + * when it has data available, requiring the caller to take care of processing + * it. + */ +void ALooper_addFd(ALooper* looper, int fd, int events, ALooper_callbackFunc* callback, void* data); -int32_t ALooper_removeCallback(ALooper* looper, int fd); +/** + * Remove a previously added file descriptor from the looper. + */ +int32_t ALooper_removeFd(ALooper* looper, int fd); #ifdef __cplusplus }; diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android_glue/threaded_app.h b/ndk/platforms/android-9/arch-arm/usr/include/android_glue/threaded_app.h new file mode 100644 index 000000000..80de3bf2b --- /dev/null +++ b/ndk/platforms/android-9/arch-arm/usr/include/android_glue/threaded_app.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include +#include + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + int running; + int destroyed; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_EVENT = 2 +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: the ANativeWindow has changed. Upon processing + * this command, android_app->window will be updated to the new window surface + * (or NULL). + */ + APP_CMD_WINDOW_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call if android_app->destroyRequested is non-zero. Upon return, the + * android_app structure is no longer valid and must not be touched. + */ +void android_app_destroy(struct android_app* android_app); + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * default processing of the given command. + */ +void android_app_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); diff --git a/ndk/platforms/android-9/arch-arm/usr/lib/libandroid.so b/ndk/platforms/android-9/arch-arm/usr/lib/libandroid.so index d185a8fb1..002b92946 100644 Binary files a/ndk/platforms/android-9/arch-arm/usr/lib/libandroid.so and b/ndk/platforms/android-9/arch-arm/usr/lib/libandroid.so differ diff --git a/ndk/platforms/android-9/arch-arm/usr/lib/libthreaded_app.a b/ndk/platforms/android-9/arch-arm/usr/lib/libthreaded_app.a new file mode 100644 index 000000000..9b6e7dd8c Binary files /dev/null and b/ndk/platforms/android-9/arch-arm/usr/lib/libthreaded_app.a differ diff --git a/ndk/samples/native-activity/jni/Android.mk b/ndk/samples/native-activity/jni/Android.mk index fa1ab4177..8ce70aae0 100644 --- a/ndk/samples/native-activity/jni/Android.mk +++ b/ndk/samples/native-activity/jni/Android.mk @@ -18,6 +18,6 @@ include $(CLEAR_VARS) LOCAL_MODULE := native-activity LOCAL_SRC_FILES := main.c glutils.c -LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM +LOCAL_LDLIBS := -lthreaded_app -llog -landroid -lEGL -lGLESv1_CM include $(BUILD_SHARED_LIBRARY) diff --git a/ndk/samples/native-activity/jni/main.c b/ndk/samples/native-activity/jni/main.c index 3d2faeef9..6960ff6ab 100644 --- a/ndk/samples/native-activity/jni/main.c +++ b/ndk/samples/native-activity/jni/main.c @@ -18,38 +18,14 @@ #include #include -#include -#include -#include -#include -#include + +#include #include "glutils.h" -// -------------------------------------------------------------------- -// Rendering and input engine thread -// -------------------------------------------------------------------- - struct engine { - pthread_mutex_t mutex; - pthread_cond_t cond; + struct android_app* app; - int msgread; - int msgwrite; - - ANativeActivity* activity; - pthread_t thread; - - int running; - int destroyed; - ALooper* looper; - AInputQueue* inputQueue; - ANativeWindow* window; - AInputQueue* pendingInputQueue; - ANativeWindow* pendingWindow; - - // private to engine thread. - int destroyRequested; int animating; EGLDisplay display; EGLSurface surface; @@ -61,14 +37,6 @@ struct engine { int32_t y; }; -enum { - ENGINE_CMD_INPUT_CHANGED, - ENGINE_CMD_WINDOW_CHANGED, - ENGINE_CMD_GAINED_FOCUS, - ENGINE_CMD_LOST_FOCUS, - ENGINE_CMD_DESTROY, -}; - static int engine_init_display(struct engine* engine) { // initialize opengl and egl const EGLint attribs[] = { @@ -84,8 +52,8 @@ static int engine_init_display(struct engine* engine) { EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, 0, 0); - selectConfigForNativeWindow(display, attribs, engine->window, &config); - surface = eglCreateWindowSurface(display, config, engine->window, NULL); + selectConfigForNativeWindow(display, attribs, engine->app->window, &config); + surface = eglCreateWindowSurface(display, config, engine->app->window, NULL); context = eglCreateContext(display, config, NULL, NULL); eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); @@ -161,19 +129,17 @@ static int engine_term_display(struct engine* engine) { engine->surface = EGL_NO_SURFACE; } -static int engine_process_event(int fd, int events, void* param) { - struct engine* engine = (struct engine*)param; - +static int engine_do_ui_event(struct engine* engine) { AInputEvent* event = NULL; - if (AInputQueue_getEvent(engine->inputQueue, &event) >= 0) { + if (AInputQueue_getEvent(engine->app->inputQueue, &event) >= 0) { LOGI("New input event: type=%d\n", AInputEvent_getType(event)); if (AInputEvent_getType(event) == INPUT_EVENT_TYPE_MOTION) { engine->animating = 1; engine->x = AMotionEvent_getX(event, 0); engine->y = AMotionEvent_getY(event, 0); - AInputQueue_finishEvent(engine->inputQueue, event, 1); + AInputQueue_finishEvent(engine->app->inputQueue, event, 1); } else { - AInputQueue_finishEvent(engine->inputQueue, event, 0); + AInputQueue_finishEvent(engine->app->inputQueue, event, 0); } } else { LOGI("Failure reading next input event: %s\n", strerror(errno)); @@ -182,262 +148,69 @@ static int engine_process_event(int fd, int events, void* param) { return 1; } -static int engine_process_cmd(int fd, int events, void* param) { - struct engine* engine = (struct engine*)param; - - int8_t cmd; - if (read(engine->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { - switch (cmd) { - case ENGINE_CMD_INPUT_CHANGED: - LOGI("Engine: ENGINE_CMD_INPUT_CHANGED\n"); - pthread_mutex_lock(&engine->mutex); - if (engine->inputQueue != NULL) { - AInputQueue_detachLooper(engine->inputQueue); - } - engine->inputQueue = engine->pendingInputQueue; - if (engine->inputQueue != NULL) { - LOGI("Attaching input queue to looper"); - AInputQueue_attachLooper(engine->inputQueue, - engine->looper, engine_process_event, engine); - } - pthread_cond_broadcast(&engine->cond); - pthread_mutex_unlock(&engine->mutex); - break; - - case ENGINE_CMD_WINDOW_CHANGED: - LOGI("Engine: ENGINE_CMD_WINDOW_CHANGED\n"); - engine_term_display(engine); - pthread_mutex_lock(&engine->mutex); - engine->window = engine->pendingWindow; - pthread_cond_broadcast(&engine->cond); - pthread_mutex_unlock(&engine->mutex); - if (engine->window != NULL) { - engine_init_display(engine); - engine_draw_frame(engine); - } - break; - - case ENGINE_CMD_LOST_FOCUS: - engine->animating = 0; +static void engine_do_main_cmd(struct engine* engine) { + int8_t cmd = android_app_read_cmd(engine->app); + switch (cmd) { + case APP_CMD_WINDOW_CHANGED: + engine_term_display(engine); + android_app_exec_cmd(engine->app, cmd); + if (engine->app->window != NULL) { + engine_init_display(engine); engine_draw_frame(engine); - break; - - case ENGINE_CMD_DESTROY: - LOGI("Engine: ENGINE_CMD_DESTROY\n"); - engine->destroyRequested = 1; - break; - } - } else { - LOGW("No data on command pipe!"); + } + break; + case APP_CMD_LOST_FOCUS: + android_app_exec_cmd(engine->app, cmd); + engine->animating = 0; + engine_draw_frame(engine); + break; + default: + android_app_exec_cmd(engine->app, cmd); + break; } - - return 1; } -static void* engine_entry(void* param) { - struct engine* engine = (struct engine*)param; +void android_main(struct android_app* state) { + struct engine engine; - ALooper* looper = ALooper_prepare(); - ALooper_setCallback(looper, engine->msgread, POLLIN, engine_process_cmd, engine); - engine->looper = looper; - - pthread_mutex_lock(&engine->mutex); - engine->running = 1; - pthread_cond_broadcast(&engine->cond); - pthread_mutex_unlock(&engine->mutex); + memset(&engine, 0, sizeof(engine)); + state->userData = &engine; + engine.app = state; // loop waiting for stuff to do. while (1) { // Read all pending events. - while (ALooper_pollOnce(engine->animating ? 0 : -1)) { - ; + int fd; + int events; + void* data; + while ((fd=ALooper_pollAll(engine.animating ? 0 : -1, &events, &data)) >= 0) { + LOGI("Poll returned: %d", (int)data); + switch ((int)data) { + case LOOPER_ID_MAIN: + engine_do_main_cmd(&engine); + break; + case LOOPER_ID_EVENT: + engine_do_ui_event(&engine); + break; + } } - if (engine->destroyRequested) { + if (state->destroyRequested) { LOGI("Engine thread destroy requested!"); - pthread_mutex_lock(&engine->mutex); - engine_term_display(engine); - if (engine->inputQueue != NULL) { - AInputQueue_detachLooper(engine->inputQueue); - } - engine->destroyed = 1; - pthread_cond_broadcast(&engine->cond); - pthread_mutex_unlock(&engine->mutex); - // Can't touch engine object after this. - return NULL; // EXIT THREAD + engine_term_display(&engine); + android_app_destroy(state); + // Can't touch android_app object after this. + return; } - if (engine->animating) { + if (engine.animating) { // Done with events; draw next animation frame. - engine->angle += .01f; - if (engine->angle > 1) { - engine->angle = 0; + engine.angle += .01f; + if (engine.angle > 1) { + engine.angle = 0; } - engine_draw_frame(engine); + engine_draw_frame(&engine); } } } - -static struct engine* engine_create(ANativeActivity* activity) { - struct engine* engine = (struct engine*)malloc(sizeof(struct engine)); - memset(engine, 0, sizeof(struct engine)); - engine->activity = activity; - - pthread_mutex_init(&engine->mutex, NULL); - pthread_cond_init(&engine->cond, NULL); - - int msgpipe[2]; - if (pipe(msgpipe)) { - LOGI("could not create pipe: %s", strerror(errno)); - } - engine->msgread = msgpipe[0]; - engine->msgwrite = msgpipe[1]; - - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&engine->thread, &attr, engine_entry, engine); - - // Wait for thread to start. - pthread_mutex_lock(&engine->mutex); - while (!engine->running) { - pthread_cond_wait(&engine->cond, &engine->mutex); - } - pthread_mutex_unlock(&engine->mutex); - - return engine; -} - -static void engine_write_cmd(struct engine* engine, int8_t cmd) { - if (write(engine->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { - LOGI("Failure writing engine cmd: %s\n", strerror(errno)); - } -} - -static void engine_set_input(struct engine* engine, AInputQueue* inputQueue) { - pthread_mutex_lock(&engine->mutex); - engine->pendingInputQueue = inputQueue; - engine_write_cmd(engine, ENGINE_CMD_INPUT_CHANGED); - while (engine->inputQueue != engine->pendingInputQueue) { - pthread_cond_wait(&engine->cond, &engine->mutex); - } - pthread_mutex_unlock(&engine->mutex); -} - -static void engine_set_window(struct engine* engine, ANativeWindow* window) { - pthread_mutex_lock(&engine->mutex); - engine->pendingWindow = window; - engine_write_cmd(engine, ENGINE_CMD_WINDOW_CHANGED); - while (engine->window != engine->pendingWindow) { - pthread_cond_wait(&engine->cond, &engine->mutex); - } - pthread_mutex_unlock(&engine->mutex); -} - -static void engine_destroy(struct engine* engine) { - pthread_mutex_lock(&engine->mutex); - engine_write_cmd(engine, ENGINE_CMD_DESTROY); - while (!engine->destroyed) { - pthread_cond_wait(&engine->cond, &engine->mutex); - } - pthread_mutex_unlock(&engine->mutex); - - close(engine->msgread); - close(engine->msgwrite); - pthread_cond_destroy(&engine->cond); - pthread_mutex_destroy(&engine->mutex); - free(engine); -} - -// -------------------------------------------------------------------- -// Native activity interaction (called from main thread) -// -------------------------------------------------------------------- - -static void onDestroy(ANativeActivity* activity) -{ - LOGI("Destroy: %p\n", activity); - engine_destroy((struct engine*)activity->instance); -} - -static void onStart(ANativeActivity* activity) -{ - LOGI("Start: %p\n", activity); -} - -static void onResume(ANativeActivity* activity) -{ - LOGI("Resume: %p\n", activity); -} - -static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) -{ - LOGI("SaveInstanceState: %p\n", activity); - return NULL; -} - -static void onPause(ANativeActivity* activity) -{ - LOGI("Pause: %p\n", activity); -} - -static void onStop(ANativeActivity* activity) -{ - LOGI("Stop: %p\n", activity); -} - -static void onLowMemory(ANativeActivity* activity) -{ - LOGI("LowMemory: %p\n", activity); -} - -static void onWindowFocusChanged(ANativeActivity* activity, int focused) -{ - LOGI("WindowFocusChanged: %p -- %d\n", activity, focused); - engine_write_cmd((struct engine*)activity->instance, - focused ? ENGINE_CMD_GAINED_FOCUS : ENGINE_CMD_LOST_FOCUS); -} - -static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) -{ - LOGI("NativeWindowCreated: %p -- %p\n", activity, window); - engine_set_window((struct engine*)activity->instance, window); -} - -static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) -{ - LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window); - engine_set_window((struct engine*)activity->instance, NULL); -} - -static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) -{ - LOGI("InputQueueCreated: %p -- %p\n", activity, queue); - engine_set_input((struct engine*)activity->instance, queue); -} - -static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) -{ - LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue); - engine_set_input((struct engine*)activity->instance, NULL); -} - -void ANativeActivity_onCreate(ANativeActivity* activity, - void* savedState, size_t savedStateSize) -{ - LOGI("Creating: %p\n", activity); - activity->callbacks->onDestroy = onDestroy; - activity->callbacks->onStart = onStart; - activity->callbacks->onResume = onResume; - activity->callbacks->onSaveInstanceState = onSaveInstanceState; - activity->callbacks->onPause = onPause; - activity->callbacks->onStop = onStop; - activity->callbacks->onLowMemory = onLowMemory; - activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; - activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; - activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; - activity->callbacks->onInputQueueCreated = onInputQueueCreated; - activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; - - activity->instance = engine_create(activity); -}