使用NativeActivity可以完全不使用java代码,全部使用native code来开发android程序。NativeActivity 为我们定制了native代码的各种接口回调,在ndk的samples里面,提供了一个例子如何使用NativeActivity。我们会发现,demo中使用了一个胶水层android_native_app_glue.h封装了native层面的ANativeActivity的事件回调。
在native层面ANativeActivity对应的就是,java层面的NativeActivity。通过NativeActivity的java会代码会看到,ANativeActivity的回调函数都是在NativeActivity中被调用的。android_native_app_glue.c 主要做了以下几件事情。
- 实现ANativeActivity启动函数,挂载ANativeActivity的事件回调函数。包括了事件处理,窗口生命周期等等。
- 启动一个独立的线程,给使用者传递事件,和实现绘制。
- 使用ALooper重新定义和实现了事件类型,让使用者实现回调处理。
在整个android_native_app_glue代码的核心,就是如何使用ALooper串联起主线程事件,输入输出事件的。主线程事件,就是指在NativeActivity的回调事件。输入输出事件,就是input事件。
简单介绍一个下ALooper,通过阅读java和c的定义代码。我的理解是,Looper可以把其它线程的事件放到队列里面,以后再一个线程里面独立处理。比如,OpenGL的调用就需要和Context在一个线程里面,就是典型的使用Looper的场景。我们从主线程拿到事件,然后在openGL的绘制线程里,处理looper收集的事件,这样事件处理和绘制就会在同一个线程里面了。
从android/input.h和android/sensor.h我们可以了解到,要使用系统的输入输出和重力感应事件,我们需要传入Looper对象指针。在android/looper.h中可以了解到Looper的使用方式,每个线程可以附加一个Looper。我们在线程代码里看到下面的代码。
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
&android_app->cmdPollSource);
int msgpipe[2];
if (pipe(msgpipe)) {
LOGE("could not create pipe: %s", strerror(errno));
return NULL;
}
android_app->msgread = msgpipe[0];
android_app->msgwrite = msgpipe[1];
就是利用管道的读写句柄来进行数据通信的。当ANativeActivity的事件在主线程触发的时候,向msgwrite写入了一个自定义的数据,然后调用ALooper_pollAll来处理Looper中事件,这时候利用msgwrite去读取写入的数据。由于是非回调Looper处理,所以android_native_app_glue使用一个结构在存放通用的事件处理,作为最后一个自定义参数传递给ALooper_addFd。如下:
/**
* Data associated with an ALooper fd that will be returned as the "outData"
* when that source has data ready.
*/
struct android_poll_source {
// The identifier of this source. May be LOOPER_ID_MAIN or
// LOOPER_ID_INPUT.
int32_t id;
// The android_app this ident is associated with.
struct android_app* app;
// Function to call to perform the standard processing of data from
// this source.
void (*process)(struct android_app* app, struct android_poll_source* source);
};
我们继续查看process的实现和赋值的代码如下:
android_app->cmdPollSource.id = LOOPER_ID_MAIN;
android_app->cmdPollSource.app = android_app;
android_app->cmdPollSource.process = process_cmd;
android_app->inputPollSource.id = LOOPER_ID_INPUT;
android_app->inputPollSource.app = android_app;
android_app->inputPollSource.process = process_input;
static void process_input(struct android_app* app, struct android_poll_source* source) {
AInputEvent* even