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.android

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

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.jvm

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

程序代码运行在新的线程中,若是要进行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 对象,建立这个对象后将启动新的线程来处理事件。oop

//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.
相关文章
相关标签/搜索