Remove prebuilt static library "libthreaded_app.a".

Make the "glue library" part of the NDK as an importable module,
this has several benefits:

- no need to distribute a binary here with no easy way to regenerate it

- no need to explicitely list -lthreaded_app in your LOCAL_LDLIBS
  (this is handled automatically by the module import capability)

- allows easier native debugging of what's really happening.

Note that the header is renamed <threaded_native_app.h>

+ Modify the native-activity sample to use and import the new module
+ Start documenting usage in the header file. We probably need something
  better, and will probably put it under development/ndk/docs/ at some
  point.

After this patch, we should be able to get rid of the code under
framework/base/native/{include.glue}

Change-Id: I6e81d70a225a6ca006beabf6e8b42529e8f371b9
This commit is contained in:
David 'Digit' Turner
2010-07-13 17:00:24 -07:00
parent 98b2c359c2
commit 4948c16366
8 changed files with 385 additions and 28 deletions

View File

@@ -18,6 +18,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE := native-activity
LOCAL_SRC_FILES := main.c glutils.c
LOCAL_LDLIBS := -lthreaded_app -llog -landroid -lEGL -lGLESv1_CM
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
LOCAL_STATIC_LIBRARIES := android_native_app_glue
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/native_app_glue)

View File

@@ -19,13 +19,13 @@
#include <errno.h>
#include <android_glue/threaded_app.h>
#include <android_native_app_glue.h>
#include "glutils.h"
struct engine {
struct android_app* app;
int animating;
EGLDisplay display;
EGLSurface surface;
@@ -69,13 +69,13 @@ static int engine_init_display(struct engine* engine) {
engine->width = w;
engine->height = h;
engine->angle = 0;
// Initialize GL state.
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
return 0;
}
@@ -84,7 +84,7 @@ static void engine_draw_frame(struct engine* engine) {
// No display.
return;
}
glClearColor(((float)engine->x)/engine->width, engine->angle,
((float)engine->y)/engine->height, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -108,7 +108,7 @@ static void engine_draw_frame(struct engine* engine) {
#endif
eglSwapBuffers(engine->display, engine->surface);
//engine->angle += 1.2f;
}
@@ -144,7 +144,7 @@ static int engine_do_ui_event(struct engine* engine) {
} else {
LOGI("Failure reading next input event: %s\n", strerror(errno));
}
return 1;
}
@@ -169,19 +169,19 @@ static int32_t engine_do_main_cmd(struct engine* engine) {
res = android_app_exec_cmd(engine->app, cmd);
break;
}
return res;
}
void android_main(struct android_app* state) {
struct engine engine;
memset(&engine, 0, sizeof(engine));
state->userData = &engine;
engine.app = state;
// loop waiting for stuff to do.
while (1) {
// Read all pending events.
int fd;
@@ -201,7 +201,7 @@ void android_main(struct android_app* state) {
break;
}
}
if (engine.animating) {
// Done with events; draw next animation frame.
engine.angle += .01f;

View File

@@ -18,6 +18,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE := native-plasma
LOCAL_SRC_FILES := plasma.c
LOCAL_LDLIBS := -lthreaded_app -lm -llog -landroid
LOCAL_LDLIBS := -lm -llog -landroid
LOCAL_STATIC_LIBRARIES := android_native_app_glue
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/native_app_glue)

View File

@@ -15,7 +15,7 @@
*
*/
#include <android_glue/threaded_app.h>
#include <android_native_app_glue.h>
#include <errno.h>
#include <jni.h>
@@ -374,9 +374,9 @@ stats_endFrame( Stats* s )
struct engine {
struct android_app* app;
Stats stats;
int animating;
};
@@ -385,20 +385,20 @@ static void engine_draw_frame(struct engine* engine) {
// No window.
return;
}
ANativeWindow_Buffer buffer;
if (ANativeWindow_lock(engine->app->window, &buffer, NULL) < 0) {
LOGW("Unable to lock window buffer");
return;
}
stats_startFrame(&engine->stats);
struct timespec t;
t.tv_sec = t.tv_nsec = 0;
clock_gettime(CLOCK_MONOTONIC, &t);
int64_t time_ms = (((int64_t)t.tv_sec)*1000000000LL + t.tv_nsec)/1000000;
/* Now fill the values with a nice little plasma */
fill_plasma(&buffer, time_ms);
@@ -429,7 +429,7 @@ static int engine_do_ui_event(struct engine* engine) {
} else {
LOGI("Failure reading next input event: %s\n", strerror(errno));
}
return 1;
}
@@ -453,7 +453,7 @@ static int32_t engine_do_main_cmd(struct engine* engine) {
res = android_app_exec_cmd(engine->app, cmd);
break;
}
return res;
}
@@ -461,20 +461,20 @@ void android_main(struct android_app* state) {
static int init;
struct engine engine;
memset(&engine, 0, sizeof(engine));
state->userData = &engine;
engine.app = state;
if (!init) {
init_tables();
init = 1;
}
stats_init(&engine.stats);
// loop waiting for stuff to do.
while (1) {
// Read all pending events.
int fd;
@@ -494,7 +494,7 @@ void android_main(struct android_app* state) {
break;
}
}
if (engine.animating) {
engine_draw_frame(&engine);
}

View File

@@ -0,0 +1,10 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= android_native_app_glue
LOCAL_SRC_FILES:= android_native_app_glue.c
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)

View File

@@ -0,0 +1,278 @@
/*
* 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 <jni.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include "android_native_app_glue.h"
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "threaded_app", __VA_ARGS__))
int8_t android_app_read_cmd(struct android_app* android_app) {
int8_t cmd;
if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
return cmd;
} else {
LOGW("No data on command pipe!");
}
return -1;
}
int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd) {
switch (cmd) {
case APP_CMD_INPUT_CHANGED:
LOGI("APP_CMD_INPUT_CHANGED\n");
pthread_mutex_lock(&android_app->mutex);
if (android_app->inputQueue != NULL) {
AInputQueue_detachLooper(android_app->inputQueue);
}
android_app->inputQueue = android_app->pendingInputQueue;
if (android_app->inputQueue != NULL) {
LOGI("Attaching input queue to looper");
AInputQueue_attachLooper(android_app->inputQueue,
android_app->looper, NULL, (void*)LOOPER_ID_EVENT);
}
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_WINDOW_CHANGED:
LOGI("APP_CMD_WINDOW_CHANGED\n");
pthread_mutex_lock(&android_app->mutex);
android_app->window = android_app->pendingWindow;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_START:
case APP_CMD_RESUME:
case APP_CMD_PAUSE:
case APP_CMD_STOP:
LOGI("activityState=%d\n", cmd);
pthread_mutex_lock(&android_app->mutex);
android_app->activityState = cmd;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_DESTROY:
LOGI("APP_CMD_DESTROY\n");
android_app->destroyRequested = 1;
break;
}
return android_app->destroyRequested ? 0 : 1;
}
static void android_app_destroy(struct android_app* android_app) {
LOGI("android_app_destroy!");
pthread_mutex_lock(&android_app->mutex);
if (android_app->inputQueue != NULL) {
AInputQueue_detachLooper(android_app->inputQueue);
}
android_app->destroyed = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
// Can't touch android_app object after this.
}
static void* android_app_entry(void* param) {
struct android_app* android_app = (struct android_app*)param;
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(looper, android_app->msgread, POLLIN, NULL, (void*)LOOPER_ID_MAIN);
android_app->looper = looper;
pthread_mutex_lock(&android_app->mutex);
android_app->running = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
android_main(android_app);
android_app_destroy(android_app);
return NULL;
}
// --------------------------------------------------------------------
// Native activity interaction (called from main thread)
// --------------------------------------------------------------------
static struct android_app* android_app_create(ANativeActivity* activity) {
struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
memset(android_app, 0, sizeof(struct android_app));
android_app->activity = activity;
pthread_mutex_init(&android_app->mutex, NULL);
pthread_cond_init(&android_app->cond, NULL);
int msgpipe[2];
if (pipe(msgpipe)) {
LOGI("could not create pipe: %s", strerror(errno));
}
android_app->msgread = msgpipe[0];
android_app->msgwrite = msgpipe[1];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
// Wait for thread to start.
pthread_mutex_lock(&android_app->mutex);
while (!android_app->running) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
return android_app;
}
static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
LOGI("Failure writing android_app cmd: %s\n", strerror(errno));
}
}
static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
pthread_mutex_lock(&android_app->mutex);
android_app->pendingInputQueue = inputQueue;
android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
while (android_app->inputQueue != android_app->pendingInputQueue) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
pthread_mutex_lock(&android_app->mutex);
android_app->pendingWindow = window;
android_app_write_cmd(android_app, APP_CMD_WINDOW_CHANGED);
while (android_app->window != android_app->pendingWindow) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, cmd);
while (android_app->activityState != cmd) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
static void android_app_free(struct android_app* android_app) {
pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, APP_CMD_DESTROY);
while (!android_app->destroyed) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
close(android_app->msgread);
close(android_app->msgwrite);
pthread_cond_destroy(&android_app->cond);
pthread_mutex_destroy(&android_app->mutex);
free(android_app);
}
static void onDestroy(ANativeActivity* activity) {
LOGI("Destroy: %p\n", activity);
android_app_free((struct android_app*)activity->instance);
}
static void onStart(ANativeActivity* activity) {
LOGI("Start: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
}
static void onResume(ANativeActivity* activity) {
LOGI("Resume: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
}
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);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
}
static void onStop(ANativeActivity* activity) {
LOGI("Stop: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
}
static void onLowMemory(ANativeActivity* activity) {
LOGI("LowMemory: %p\n", activity);
}
static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
android_app_write_cmd((struct android_app*)activity->instance,
focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
}
static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
android_app_set_window((struct android_app*)activity->instance, window);
}
static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
android_app_set_window((struct android_app*)activity->instance, NULL);
}
static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
android_app_set_input((struct android_app*)activity->instance, queue);
}
static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
android_app_set_input((struct android_app*)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 = android_app_create(activity);
}

View File

@@ -15,6 +15,9 @@
*
*/
#ifndef _ANDROID_NATIVE_APP_GLUE_H
#define _ANDROID_NATIVE_APP_GLUE_H
#include <poll.h>
#include <pthread.h>
#include <sched.h>
@@ -22,6 +25,60 @@
#include <android/native_activity.h>
#include <android/looper.h>
#ifdef __cplusplus
extern "C"
#endif
/**
* The native activity interface provided by <android/native_activity.h>
* is based on a set of application-provided callbacks that will be called
* by the Activity's main thread when certain events occur.
*
* This means that each one of this callbacks _should_ _not_ block, or they
* risk having the system force-close the application. This programming
* model is direct, lightweight, but constraining.
*
* The 'threaded_native_app' static library is used to provide a different
* execution model where the application can implement its own main event
* loop in a different thread instead. Here's how it works:
*
* 1/ The application must provide a function named "android_main()" that
* will be called when the activity is created, in a new thread that is
* distinct from the activity's main thread.
*
* 2/ android_main() receives a pointer to a valid "android_app" structure
* that contains references to other important objects, e.g. the
* ANativeActivity obejct instance the application is running in.
*
* 3/ the "android_app" object holds an ALooper instance that already
* listens to two important things:
*
* - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
* declarations below.
*
* - input events coming from the AInputQueue attached to the activity.
*
* Each of these correspond to an ALooper callback that returns a "data"
* value of LOOPER_ID_MAIN and LOOPER_ID_EVENT, respectively.
*
* Your application can use the same ALooper to listen to additionnal
* file-descriptors.
*
* 4/ Whenever you receive a LOOPER_ID_MAIN event from the ALooper, your
* code should call the function android_app_read_cmd() to read the
* command value and act upon it. This is normally done by calling
* android_app_exec_cmd() directly.
*
* XXX: MAKE THIS STUFF MORE CLEAR !!
*
* 5/ Whenever you receive a LOOPER_ID_EVENT event from the ALooper, you
* should read one event from the AInputQueue with AInputQueue_getEvent().
*
* See the sample named "native-activity" that comes with the NDK with a
* full usage example.
*
*/
/**
* This is the interface for the standard glue code of a threaded
* application. In this model, the application's code is running
@@ -195,3 +252,9 @@ int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd);
* the main entry to the app.
*/
extern void android_main(struct android_app* app);
#ifdef __cplusplus
}
#endif
#endif /* _ANDROID_NATIVE_APP_GLUE_H */