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.
相關文章
相關標籤/搜索