native_app_glue 分析

natvie_activity

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.

说的很明白,native_activity 提供的回调接口默认都是运行在主线程的,所以这些回调的实现不能 阻塞(block), 否则有可能被kill掉, 这种编程模型直接、轻量,但是要求回调不能阻塞,这是一个很大的限制,因此android_native_app_glue 设计了新的编程模型.

app_glue

The 'android_native_app_glue' 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 identifier_ returned by **_ALooper_pollOnce_** with values of _LOOPER_ID_MAIN_ and _LOOPER_ID_INPUT_, respectively.

Your application can use the same ALooper to listen to additional file-descriptors. They can either be callback based, or with return identifiers starting with LOOPER_ID_USER.

  • 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, the returned data will point to an android_poll_source structure. You can call the process() function on it, and fill in android_app->onAppCmd and android_app->onInputEvent to be called for your own processing of the event.

Alternatively, you can call the low-level functions to read and process the data directly... look at the process_cmd() and process_input() implementations in the glue to see how to do this.

app_glue 的编程模型提供一个新的线程处理事件, app_glue 的数据结构 android_app, 要求用户实现 onAppCmdonInputEvent 两个接口,分别处理Activity 的生命周期事件和用户输入事件(触摸、传感器等);

程序代码运行在新的线程中,如果要进行jni 调用,还需要把此线程attach 到 jvm

app_glue 的核心数据结构 android_app

//file: android_native_app_glue.h

/**
 * 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;

    // Fill this in with the function to process main app commands (APP_CMD_*)
    void (*onAppCmd)(struct android_app* app, int32_t cmd);

    // Fill this in with the function to process input events.  At this point
    // the event has already been pre-dispatched, and it will be finished upon
    // return.  Return 1 if you have handled the event, 0 for any default
    // dispatching.
    int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);

    // The ANativeActivity object instance that this app is running in.
    ANativeActivity* activity;

    // The current configuration the app is running in.
    AConfiguration* config;

    // This is the last instance's saved state, as provided at creation time.
    // It is NULL if there was no state.  You can use this as you need; the
    // memory will remain around until you call android_app_exec_cmd() for
    // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL.
    // These variables should only be changed when processing a APP_CMD_SAVE_STATE,
    // at which point they will be initialized to NULL and you can malloc your
    // state and place the information here.  In that case the memory will be
    // freed for you later.
    void* savedState;
    size_t savedStateSize;

    // 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 content rectangle of the window; this is the area where the
    // window's content should be placed to be seen by the user.
    ARect contentRect;

    // 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;

    struct android_poll_source cmdPollSource;
    struct android_poll_source inputPollSource;

    int running;
    int stateSaved;
    int destroyed;
    int redrawNeeded;
    AInputQueue* pendingInputQueue;
    ANativeWindow* pendingWindow;
    ARect pendingContentRect;
};

另一个核心数据结构是 android_poll_source

//file: android_native_app_glue.h

/**
 * 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);
};

接口定义了一个 extern 方法(相当于接口),需要我们去实现:

//file: android_native_app_glue.h

extern void android_main(struct android_app* app);

ANativeActivity 的入口:

//file: android_native_app_glue.c

void ANativeActivity_onCreate(ANativeActivity* activity,
        void* savedState, size_t savedStateSize) {
    LOGV("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->onConfigurationChanged = onConfigurationChanged;
    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, savedState, savedStateSize);
}

最后的activity->instance 可以保存自定义的android_app 对象,创建这个对象后将启动新的线程来处理事件。

//file:native_activity.h

 /**
     * This is the native instance of the application.  It is not used by
     * the framework, but can be set by the application to its own instance
     * state.
     */
    void* instance;

#事件处理(事件读取)

事件处理线程创建过程

android_app 初始化完毕后会启动新的线程(事件处理线程 tt),主线程将等待线程 tt 启动,然后返回 android_app 对象。

android_app->msgreadandroid_app->msgwrite 是一对管道文件,事件处理线程从管道读取数据(事件),主线程将向改管道写入数据(事件)

//file: android_native_app_glue.c

// --------------------------------------------------------------------
// Native activity interaction (called from main thread)
// --------------------------------------------------------------------

static struct android_app* android_app_create(ANativeActivity* activity,
        void* savedState, size_t savedStateSize) {
    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);

    if (savedState != NULL) {
        android_app->savedState = malloc(savedStateSize);
        android_app->savedStateSize = savedStateSize;
        memcpy(android_app->savedState, savedState, savedStateSize);
    }

    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];

    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;
}

事件处理线程的入口方法 android_app_entry

//file: android_native_app_glue.c

static void* android_app_entry(void* param) {
    struct android_app* android_app = (struct android_app*)param;

    android_app->config = AConfiguration_new();
    AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);

    print_cur_config(android_app);

    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;

    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);
    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;
}

configuration 对象

app 的 configuration 对象是由 assetmanager 创建的:

    android_app->config = AConfiguration_new();
    AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);

设置事件源

cmdPollSourceinputPollSource 的处理方法分别是 process_cmdprocess_input

    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;

创建Looper

创建tt 的looper,该looper 监听(读取) android_app->msgread 管道文件,管道的写是由主线程的生命周期函数及输入事件触发的。

    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);
    android_app->looper = looper;

然后通知主线程main_thread, 事件处理线程tt 已经启动成功:

    pthread_mutex_lock(&android_app->mutex);
    android_app->running = 1;
    pthread_cond_broadcast(&android_app->cond);
    pthread_mutex_unlock(&android_app->mutex);

最后调用app_glue框架的业务逻辑逻辑android_main,android_main 将不断从looper中取出事件int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData); )并处理;

outData 就是 android_poll_source, 它有一个process 函数指针。

    android_main(android_app);

从主线程获取事件传递给事件处理线程(事件写入)

从主线程获取的事件有两类:生命周期事件(cmd 事件)和 用户输入事件(input 事件,比如触摸屏幕)

cmd 事件

在native_activity 启动时注册了一些回调函数,比如:

    activity->callbacks->onStart = onStart;

在这些方法中会对管道文件**android_app->msgwrite**执行写操作:

    static void onStart(ANativeActivity* activity) {
        LOGV("Start: %p\n", activity);
        android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
    }

    ...


    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_write_cmd(struct android_app* android_app, int8_t cmd) {
        if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
            LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
        }
    }

需要注意的是, 以 android_app_set_activity_state 为例,acitivty的生命周期以及window 等事件虽然通过管道通知 事件处理线程异步处理,但是同时使用了

while (android_app->activityState != cmd) {
            pthread_cond_wait(&android_app->cond, &android_app->mutex);
}

进行条件等待,所以如果事件时间过长仍然会导致ANR

input 事件

主线程是通过 InputQueue 接受input 事件的,当InputQueue创建后 native_activity 会收到 onInputQueueCreated 通知, 此时要先触发一个cmd 事件: APP_CMD_INPUT_CHANGED

    static void onInputQueueCreated(ANativeActivity* activity,AInputQueue* queue) {
        LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
        android_app_set_input((struct android_app*)activity->instance, queue);
    }


    ...
    
    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);
    }

事件处理线程收到 APP_CMD_INPUT_CHANGED 事件后把 inputQueue attach 到事件处理线程的looper,对应的事件源为android_app->inputPollSource, 这样事件处理线程就可以通过其looper 收到inputQueue 的事件了。


static void process_cmd(struct android_app* app, struct android_poll_source* source) {
    int8_t cmd = android_app_read_cmd(app);
    android_app_pre_exec_cmd(app, cmd);
    if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
    android_app_post_exec_cmd(app, cmd);
}

...

void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
    switch (cmd) {
        case APP_CMD_INPUT_CHANGED:
            LOGV("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) {
                LOGV("Attaching input queue to looper");
                AInputQueue_attachLooper(android_app->inputQueue,
                        android_app->looper, LOOPER_ID_INPUT, NULL,
                        &android_app->inputPollSource);
            }
            pthread_cond_broadcast(&android_app->cond);
            pthread_mutex_unlock(&android_app->mutex);
            break;

        ...
    }
}

总结

事件处理线程的looper 监听两个事件源 :

  • 一个是管道文件,有主线程写入生命周期等事件,此事件源由android_app->cmdPollSource 标识;
  • 另一个是inputQueue,接收input 事件,此事件源由android_app->inputPollSource标识。

#ps: 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.

转载于:https://my.oschina.net/u/255456/blog/702253

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这些都是 Android NDK 内部的 `Android.mk` 文件。其中,`./android-ndk-r25c/sources/android/native_app_glue/Android.mk` 是用于编译 Native Activity 示例应用程序的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/support/Android.mk` 是包含一些 Android 支持库的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/ndk_helper/Android.mk` 是包含一些辅助函数和类的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/cpufeatures/Android.mk` 是用于编译 `cpufeatures` 库的 `Android.mk` 文件,该库提供了一些 CPU 相关的信息和功能;`./android-ndk-r25c/sources/cxx-stl/llvm-libc++abi/Android.mk` 和 `./android-ndk-r25c/sources/cxx-stl/llvm-libc++/Android.mk` 是用于编译 C++ STL 库的 `Android.mk` 文件,分别对应 libc++abi 和 libc++ 两个 STL 库;`./android-ndk-r25c/sources/third_party/googletest/Android.mk` 是用于编译 Google Test 测试框架的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/Android.mk` 是用于编译 Shaderc 编译器的 `Android.mk` 文件,该编译器可以将 GLSL 代码编译成 SPIR-V 代码;`./android-ndk-r25c/sources/third_party/shaderc/libshaderc/Android.mk` 是用于编译 Shaderc 库的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/libshaderc_util/Android.mk` 是用于编译 Shaderc Util 库的 `Android.mk` 文件,该库提供了一些辅助函数和类;`./android-ndk-r25c/sources/third_party/shaderc/third_party/Android.mk` 是用于编译 Shaderc 编译器依赖的第三方库的 `Android.mk` 文件,包括 glslang 和 spirv-tools 两个库;`./android-ndk-r25c/sources/third_party/shaderc/third_party/glslang/Android.mk` 是用于编译 glslang 库的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/third_party/spirv-tools/Android.mk` 是用于编译 spirv-tools 库的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/vulkan/src/build-android/jni/Android.mk` 是用于编译 Vulkan 库的 `Android.mk` 文件。 如果您要在 Android NDK 中编写自己的 `Android.mk` 文件,可以参考这些示例文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值