找遍了全网,没有一个完整的可用的框架。ndk自带的android_native_app_glue确实不太好用,闭关几天,写出了一个框架。完全的消息队列调用,目前测试的主体框架是没有什么问题了,程序入口还是android_main。调用过程:
void android_main(android_app* app)
{
//APP_LOG("main : 程序启动");
//设置消息回调函数,就一个,整体和win32编程差不多了。
app->onEvent = on_app_event;
//循环等待事情以进行处理。
while (app->running){//app处于运行状态
app->process_message();//处理消息主函数
if(!app->pause()){
//draw_frame();//绘制我们的游戏,相当于MainLoop事件
}
else{
//APP_LOG("PAUSE");
}
}
//APP_LOG("main : 程序结束");
}
消息事件结构:
#pragma pack(push, 1)
struct APP_EVENT
{
int id;
union{
void* data;
struct{//input motion
int16_t x, y, z, w;
};
struct{//input_keyboard
int32_t key;
int32_t flag;
};
struct{//ARect
int16_t left, top, right, bottom;
};
};
};
#pragma pack(pop)
主消息处理函数,完全防win32编程设计,当然,事件处理做的还不够全面。现在只是初期框架,但这个框架已经比glue那个清晰多了。
主消息回调函数:
void on_app_event(android_app* app, APP_EVENT& event)
{
switch()
{
case APP_START://activity启动
break;
case APP_STOP://停止
break;
case APP_RESUME://返回
break;
case APP_PAUSE://暂停
break;
case APP_DESTROY://销毁退出
break;
case APP_SAVE://保存状态
break;
case APP_WINDOW_CREATE://窗口创建,app->window有效
break;
case APP_WINDOW_CLOSE://窗口销毁
break;
case APP_WINDOW_ACTIVATE://窗口获得焦点
break;
case APP_WINDOW_DEACTIVATE://窗口失去焦点
break;
case APP_WINDOW_RESIZE://窗口大小改变
break;
case APP_TOUCH_DOWN://触摸按下
//这三个事件,都使用, 作为坐标
//代表pointerid
break;
case APP_TOUCH_MOVE://触摸滑动
break;
case APP_TOUCH_UP:
break;
default:
//APP_LOG("unknow msg:%d", cmd);
break;
}
}
ndk的activity入口函数其实是:
void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize);
这个函数由主进程调用,主进程创建一个activity,通知native库创建环境并处理,如果这个函数不返回,app就会卡死无响应。所以我们native库的过程要在一个新的线程里面处理。直接使用主线程的回调也不是不可以,但无法处理MainLoop这个事件,这个事件主线程没给。如果新建一个线程,如何处理主线程回调的事件,就是一个根本的问题。glue库用了pipe,并大把大把的mutex lock处理。下面这个库,把lock基本都去掉了,不再使用ALooper进行消息处理。主线程和线程之间的通信还是用的pipe,因为简单易用。
方式很简单:(设计执行标准)
一:所有ANativeActivity_onCreate里面指定的回调函数,执行过程都在主线程里面,这个我们要打包。用APP_EVENT把事件封装,通过pipe发送到我们线程的事件队列,主线程不对android_app内部数据进行操作。
二:android_main()过程里面,循环执行process_message(),进行事件处理。process_message()的执行过程就是,从pipe里面读取打包的APP_EVENT,并进行预处理、回调执行、后续处理。
三:所有事件方法,过程尽量保证清晰,可读性高。
目前库封装了部分输入交互操作,后续会继续封装。也欢迎大家评论,有用的着的尽可拿去用,public domain协议!
#ifndef ANDROID_APP_HPP
#define ANDROID_APP_HPP
#include <>
#include <>
#include <>
#include <>
#include <>
#include <>
#include <>
#include <>
#include <sys/>
#include<sys/>
#include <>
#include <android/>
#include <android/>
#include <android/native_activity.h>
#include <android/>
#include ""//封装的pipe操作,之后会发表
#include <cgl/>//#define null NULL
#include <cgl/thread/>//封装的锁操作,之后发表
using namespace cgl;
#define ANDROID_APP_LOG(...) ((void)__android_log_print(ANDROID_LOG_INFO, "android_app", __VA_ARGS__))
#define ANDROID_APP_ERROR(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "android_app", __VA_ARGS__))
#define ANDROID_APP_DEBUG//debug 模式
#ifdef ANDROID_APP_DEBUG
#define ANDROID_APP_LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "android_app", __VA_ARGS__))
#else
#define ANDROID_APP_LOGV(...) ((void)0)
#endif
//android app events
enum ANDROID_APP_EVENT
{
APP_START,
APP_STOP,
APP_RESUME,
APP_PAUSE,
APP_DESTROY,
APP_SAVE,
APP_CONFIG,
APP_LOW_MEMORY,
APP_WINDOW_CREATE, //set
APP_WINDOW_CLOSE, //reset
APP_WINDOW_ACTIVATE,
APP_WINDOW_DEACTIVATE,
APP_WINDOW_RESIZE,
APP_WINDOW_RECT_CHANGED,
APP_WINDOW_PAINT,
APP_INPUT, //set
APP_TOUCH_DOWN,
APP_TOUCH_UP,
APP_TOUCH_MOVE,
APP_KEY_UP,
APP_KEY_DOWN,
APP_KEY_PRESS
};
#pragma pack(push, 1)
struct APP_EVENT
{
int id;
union{
void* data;
struct{//input motion
int16_t x, y, z, w;
};
struct{//input_keyboard
int32_t key;
int32_t flag;
};
struct{//ARect
int16_t left, top, right, bottom;
};
};
};
#pragma pack(pop)
class android_app;
extern void android_main(android_app* app);
void android_app_post_event(android_app* app, const APP_EVENT& event);
void android_app_post_event(android_app* app, int id);
class android_app
{
public:
ANativeActivity* activity;
AConfiguration* config;
ALooper* looper;
ANativeWindow* window;
void* savedState;
size_t savedStateSize;
int stateSaved;
void* userData;
void (*onEvent)(android_app* app, APP_EVENT& event);
int activityState;
bool running;
public:
pthread_t thread;
mutex_t mutex;
cond_t cond;
public:
io::pipefile message;
AInputQueue* inputQueue;
int destroyed;
public:
android_app() : activity(null), config(null), looper(null), window(null),
savedState(null), savedStateSize(0), stateSaved(0),
userData(null),
onEvent(null),
activityState(0),
thread(), mutex(), cond(),
message(),
inputQueue(null),
destroyed(0)
{
running = false;
}
void dispose();
bool pause()const
{
return activityState == APP_PAUSE;// || activityState == APP_STOP;
}
int process_message()
{
APP_EVENT event;
int bytes;
ioctl((), FIONREAD, &bytes);
while(bytes){
if(this->read_event(event)){
//ANDROID_APP_LOGV("process message %d - %p", , );
this->process_begin(event);
if(this->onEvent){
this->onEvent(this, event);
}
this->process_end(event);
}
ioctl((), FIONREAD, &bytes);
}
ALooper_pollAll(0, NULL, NULL, NULL);
//如果传感器有数据,立即处理。ident == USER
//ANDROID_APP_LOG("pollAll %d", ident);
return 0;
}
public://log
void print_config();
public:
void cond_wait()
{
(mutex);
}
public:
static int on_input(int fd, int events, void* data);
int on_input_event(AInputEvent* event);
private:
bool read_event(APP_EVENT& event);
void process_begin(APP_EVENT& event);
void process_end(APP_EVENT& event);
int on_input_motion(AInputEvent* event);
int on_input_keyboard(AInputEvent* event);
};
void free_saved_state(android_app* app)
{
//auto_lock lock(app->mutex);
if (app->savedState != NULL) {
free(app->savedState);
app->savedState = NULL;
app->savedStateSize = 0;
}
}
void android_app::print_config()
{
char lang[2], country[2];
AConfiguration_getLanguage(config, lang);
AConfiguration_getCountry(config, country);
ANDROID_APP_LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
"keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
"modetype=%d modenight=%d",
AConfiguration_getMcc(config),
AConfiguration_getMnc(config),
lang[0], lang[1], country[0], country[1],
AConfiguration_getOrientation(config),
AConfiguration_getTouchscreen(config),
AConfiguration_getDensity(config),
AConfiguration_getKeyboard(config),
AConfiguration_getNavigation(config),
AConfiguration_getKeysHidden(config),
AConfiguration_getNavHidden(config),
AConfiguration_getSdkVersion(config),
AConfiguration_getScreenSize(config),
AConfiguration_getScreenLong(config),
AConfiguration_getUiModeType(config),
AConfiguration_getUiModeNight(config));
}
#ifdef ANDROID_APP_DEBUG
const char* input_motion_name(int action)
{
switch(action){
case AMOTION_EVENT_ACTION_DOWN://触摸按下
return "MOTION_DOWN";
case AMOTION_EVENT_ACTION_UP://触摸弹起
return "MOTION_UP";
case AMOTION_EVENT_ACTION_MOVE://触摸移动
return "MOTION_MOVE";
case AMOTION_EVENT_ACTION_CANCEL:
return "MOTION_CACEL";
case AMOTION_EVENT_ACTION_OUTSIDE:
return "MOTION_OUTSIDE";
default:
break;
}
return null;
}
#endif
int android_app::on_input_motion(AInputEvent* inputEvent)
{
APP_EVENT event;
//motion source
switch(AInputEvent_getSource(inputEvent))
{
case AINPUT_SOURCE_TOUCHSCREEN:
//消息来源于触摸屏
break;
case AINPUT_SOURCE_TRACKBALL:
//消息来源于trackball,轨迹球 or 鼠标?
break;
default:
break;
}
switch(AMotionEvent_getAction(inputEvent))
{
case AMOTION_EVENT_ACTION_DOWN://触摸按下
= APP_TOUCH_DOWN;
break;
case AMOTION_EVENT_ACTION_UP://触摸弹起
= APP_TOUCH_UP;
break;
case AMOTION_EVENT_ACTION_MOVE://触摸移动
= APP_TOUCH_MOVE;
break;
case AMOTION_EVENT_ACTION_CANCEL:
break;
case AMOTION_EVENT_ACTION_OUTSIDE:
break;
default:
break;
}
//getX() 是表示view相对于自身左上角的x坐标,
//getRawX() 是表示相对于物理屏幕左上角的x坐标值
//AMotionEvent_getXPrecision
size_t count = AMotionEvent_getPointerCount(inputEvent);
for(int i=0; i < count; ++i){
= AMotionEvent_getX(inputEvent, i);
= AMotionEvent_getY(inputEvent, i);
= i;
if(this->onEvent){
this->onEvent(this, event);
}
#ifdef ANDROID_APP_DEBUG
ANDROID_APP_LOGV("%s : index=%d, pointer=%d, flag=%d state=%d x=%d, y=%d\n",
input_motion_name(AMotionEvent_getAction(inputEvent)),
i,
AMotionEvent_getPointerId(inputEvent, i),
AMotionEvent_getFlags(inputEvent),
AMotionEvent_getMetaState(inputEvent),
, );
#endif
}
return this->onEvent ? 1 : 0;
}
int android_app::on_input_keyboard(AInputEvent* inputEvent)
{
//键盘控制键管用,字符键不管用
ANDROID_APP_LOGV("keyinput : action=%d flag=%d keycode=%d",
AKeyEvent_getAction(inputEvent),
AKeyEvent_getFlags(inputEvent),
AKeyEvent_getKeyCode(inputEvent));
APP_EVENT event;
switch(AKeyEvent_getAction(inputEvent)){
case AKEY_STATE_UNKNOWN:
break;
case AKEY_STATE_UP:
= APP_KEY_UP;
break;
case AKEY_STATE_DOWN:
= APP_KEY_DOWN;
break;
case AKEY_STATE_VIRTUAL:
= APP_KEY_PRESS;
break;
default:
break;
}
= AKeyEvent_getKeyCode(inputEvent);
= AKeyEvent_getFlags(inputEvent);
if(this->onEvent){//if processed, reutrn 1
this->onEvent(this, event);
return 1;
}
return 0;
}
// input event callback
int android_app::on_input_event(AInputEvent* inputEvent)
{
if(AInputEvent_getType(inputEvent) == AINPUT_EVENT_TYPE_MOTION){
return this->on_input_motion(inputEvent);
}
else if(AInputEvent_getType(inputEvent) == AINPUT_EVENT_TYPE_KEY){
return this->on_input_keyboard(inputEvent);
}
return 0;
}
// inputQueue callback
int android_app::on_input(int fd, int events, void* data)
{
ANDROID_APP_LOGV("on_input %p", data);
android_app* app = (android_app*)data;
AInputEvent* event = NULL;
while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
//ANDROID_APP_LOGV("New input event: type=%d\n", AInputEvent_getType(event));
if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
continue;
}
AInputQueue_finishEvent(app->inputQueue, event, app->on_input_event(event));
}
return 1;
}
//读取事件
bool android_app::read_event(APP_EVENT& event)
{
return (&event, sizeof(event)) == sizeof(event);
}
#ifdef ANDROID_APP_DEBUG
const char* app_event_name(int id)
{
switch (id)
{
case APP_START: return "APP_START";
case APP_STOP: return "APP_STOP";
case APP_RESUME: return "APP_RESUME";
case APP_PAUSE: return "APP_PAUSE";
case APP_DESTROY: return "APP_DESTROY";
case APP_SAVE: return "APP_SAVE";
case APP_CONFIG: return "APP_CONFIG";
case APP_WINDOW_CREATE: return "APP_WINDOW_CREATE";
case APP_WINDOW_CLOSE: return "APP_WINDOW_CLOSE";
case APP_WINDOW_ACTIVATE: return "APP_WINDOW_ACTIVATE";
case APP_WINDOW_DEACTIVATE: return "APP_WINDOW_DEACTIVATE";
case APP_WINDOW_RESIZE: return "APP_WINDOW_RESIZE";
case APP_INPUT: return "APP_INPUT";
default:
break;
}
return null;
}
#endif
//预处理事件
void android_app::process_begin(APP_EVENT& event)
{
switch (){
case APP_START:
case APP_STOP:
case APP_RESUME:
case APP_PAUSE:
ANDROID_APP_LOGV("APP_STATE = %s\n", app_event_name());
this->activityState = ;
break;
case APP_DESTROY:
ANDROID_APP_LOGV("APP_DESTROY\n");
this->running = false;
break;
case APP_SAVE:
//free_saved_state(this);
break;
case APP_CONFIG:
ANDROID_APP_LOGV("APP_CONFIG\n");
AConfiguration_fromAssetManager(this->config, this->activity->assetManager);
this->print_config();
break;
case APP_WINDOW_CREATE:
ANDROID_APP_LOGV("APP_WINDOW_CREATE : %p\n", );
this->window = (ANativeWindow*);
break;
case APP_INPUT:{
ANDROID_APP_LOGV("APP_INPUT : %p\n", );
if(this->inputQueue){
AInputQueue_detachLooper(this->inputQueue);
}
this->inputQueue = (AInputQueue*);
if(this->inputQueue){
AInputQueue_attachLooper(this->inputQueue, this->looper, 0, android_app::on_input, this);
}
break;
}
default:
ANDROID_APP_LOGV("APP_EVENT : %s", app_event_name());
break;
}
}
//后续处理事件
void android_app::process_end(APP_EVENT& event)
{
switch () {
case APP_WINDOW_CLOSE:
ANDROID_APP_LOGV("APP_WINDOW_CLOSE\n");
this->window = NULL;
break;
case APP_SAVE:
ANDROID_APP_LOGV("APP_SAVE\n");
this->stateSaved = 1;
break;
case APP_RESUME:
//free_saved_state(this);
break;
default:
break;
}
}
void android_app::dispose()
{
ANDROID_APP_LOGV("android_app::dispose!");
free_saved_state(this);
//auto_lock lock(app->mutex);
if(this->inputQueue){
AInputQueue_detachLooper(this->inputQueue);
}
AConfiguration_delete(this->config);
this->destroyed = 1;
this->();
// Can't touch android_app object after this.
}
// app main thread
void* android_app_main(void* param)
{
android_app* app = (android_app*)param;
app->config = AConfiguration_new();
AConfiguration_fromAssetManager(app->config, app->activity->assetManager);
app->print_config();
app->looper = ALooper_prepare(0);
app->running = true;
android_main(app); //android_main
app->dispose();
return NULL;
}
// --------------------------------------------------------------------
//
// Native activity interaction (主线程调用的函数)
//
// --------------------------------------------------------------------
android_app* android_app_create(ANativeActivity* activity, void* savedState, size_t savedStateSize)
{
android_app* app = new android_app;
app->activity = activity;
if (savedState != NULL) {
/*
app->savedState = malloc(savedStateSize);
app->savedStateSize = savedStateSize;
memcpy(app->savedState, savedState, savedStateSize);
*/
}
if(app->()){
ANDROID_APP_ERROR("could not create pipe: %s", strerror(errno));
return NULL;
}
//int flag = fcntl(app->(), F_GETFL, 0 );
//fcntl(app->(), F_SETFL, flag | O_NONBLOCK);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&app->thread, &attr, android_app_main, app);
return app;
}
void android_app_post_event(android_app* app, const APP_EVENT& event)
{
//ANDROID_APP_LOGV("write event %d", );
if(app->(&event, sizeof(event)) != sizeof(event)) {
ANDROID_APP_ERROR("Failure writing android_app event: %s\n", strerror(errno));
}
}
void android_app_post_event(android_app* app, int id)
{
APP_EVENT event = {id};
android_app_post_event(app, event);
}
//
// 主线程回调函数
//
//
// app
//
void onStart(ANativeActivity* activity)
{
ANDROID_APP_LOGV("Activity Start: %p\n", activity);
android_app_post_event((android_app*)activity->instance, APP_START);
}
void onStop(ANativeActivity* activity)
{
ANDROID_APP_LOGV("Activity Stop: %p\n", activity);
android_app_post_event((android_app*)activity->instance, APP_STOP);
}
void onResume(ANativeActivity* activity)
{
ANDROID_APP_LOGV("Activity Resume: %p\n", activity);
android_app_post_event((android_app*)activity->instance, APP_RESUME);
}
void onPause(ANativeActivity* activity)
{
ANDROID_APP_LOGV("Activity Pause: %p\n", activity);
android_app_post_event((android_app*)activity->instance, APP_PAUSE);
}
void onDestroy(ANativeActivity* activity)
{
ANDROID_APP_LOGV("Activity Destroy: %p\n", activity);
android_app* app = (android_app*)activity->instance;
//auto_lock lock(app->mutex);
android_app_post_event(app, APP_DESTROY);
while (!app->destroyed) {
//app->cond_wait();
}
app->();
delete app;
}
void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen)
{
android_app* app = (android_app*)activity->instance;
void* savedState = NULL;
ANDROID_APP_LOGV("Activity SaveInstanceState: %p\n", activity);
/*
auto_lock lock(app->mutex);
app->stateSaved = 0;
android_app_post_event(app, APP_SAVE);
while (!app->stateSaved) {
app->cond_wait();
}
if (app->savedState != NULL) {
savedState = app->savedState;
*outLen = app->savedStateSize;
app->savedState = NULL;
app->savedStateSize = 0;
}
*/
return savedState;
}
void onConfigurationChanged(ANativeActivity* activity) {
ANDROID_APP_LOGV("Activity ConfigurationChanged: %p\n", activity);
android_app_post_event((android_app*)activity->instance, APP_CONFIG);
}
void onLowMemory(ANativeActivity* activity)
{
ANDROID_APP_LOGV("Activity LowMemory: %p\n", activity);
android_app_post_event((android_app*)activity->instance, APP_LOW_MEMORY);
}
//
// window
//
void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window)
{
ANDROID_APP_LOGV("NativeWindowCreated: %p -- %p\n", activity, window);
APP_EVENT event;
= APP_WINDOW_CREATE;
= window;
android_app_post_event((android_app*)activity->instance, event);
}
void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window)
{
ANDROID_APP_LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window);
android_app_post_event((android_app*)activity->instance, APP_WINDOW_CLOSE);
}
void onWindowFocusChanged(ANativeActivity* activity, int focused)
{
ANDROID_APP_LOGV("WindowFocusChanged: %p -- %d\n", activity, focused);
android_app_post_event((android_app*)activity->instance, focused ? APP_WINDOW_ACTIVATE : APP_WINDOW_DEACTIVATE);
}
void onNativeWindowResized(ANativeActivity* activity, ANativeWindow* window)
{
ANDROID_APP_LOGV("NativeWindowResized: %p -- %p\n", activity, window);
android_app_post_event((android_app*)activity->instance, APP_WINDOW_RESIZE);
}
void onContentRectChanged(ANativeActivity* activity, const ARect* rect)
{
ANDROID_APP_LOGV("ContentRectChanged: [%d, %d - %d, %d]\n", rect->left, rect->top, rect->right, rect->bottom);
/*
APP_EVENT event;
= APP_WINDOW_RECT_CHANGED;
= rect->left;
= rect->top;
= rect->right;
= rect->bottom;
android_app_post_event((android_app*)activity->instance, event);
*/
}
void onNativeWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow* window)
{
ANDROID_APP_LOGV("NativeWindowRedrawNeeded: %p -- %p\n", activity, window);
android_app_post_event((android_app*)activity->instance, APP_WINDOW_PAINT);
}
//
// input
//
void android_app_set_input(android_app* app, AInputQueue* inputQueue)
{
APP_EVENT event;
= APP_INPUT;
= inputQueue;
android_app_post_event(app, event);
}
void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue)
{
ANDROID_APP_LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
android_app_set_input((android_app*)activity->instance, queue);
}
void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue)
{
ANDROID_APP_LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue);
android_app_set_input((android_app*)activity->instance, NULL);
}
//
// native activity entry
//
JNIEXPORT
void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize)
{
ANDROID_APP_LOGV("NativeActivity create: %p\n", activity);
//app
activity->callbacks->onStart = onStart;
activity->callbacks->onStop = onStop;
activity->callbacks->onResume = onResume;
activity->callbacks->onPause = onPause;
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
//window
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowResized = onNativeWindowResized;
activity->callbacks->onContentRectChanged = onContentRectChanged;
activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
//input
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
//create android app
activity->instance = android_app_create(activity, savedState, savedStateSize);
//ANDROID_APP_LOGV("NativeActivity create successed.");
}
#endif /* ANDROID_APP_HPP */