Android native應用開發簡明教程 (2) - 本地應用的原理

本地應用原理

從NativeActivity說起

本地App,本質上是一個Java App調用了一個JNI的庫,而應用的邏輯通過native代碼來實現。
NativeActivity是繼承自Activity的一個類,代碼在:/frameworks/base/core/java/android/app/NativeActivity.java中:

public class NativeActivity extends Activity implements SurfaceHolder.Callback2,InputQueue.Callback, OnGlobalLayoutListener{
...

然后我們再看NativeActivity的onCreate方法:

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         String libname = "main";
         String funcname = "ANativeActivity_onCreate";
         ActivityInfo ai;
...

這里我們就看到了一個重要的函數:ANativeActivity_onCreate,這個函數將是本地應用的入口函數。

...
         try {
             ai = getPackageManager().getActivityInfo(
                     getIntent().getComponent(), PackageManager.GET_META_DATA);
             if (ai.metaData != null) {
                 String ln = ai.metaData.getString(META_DATA_LIB_NAME);
                 if (ln != null) libname = ln;
                 ln = ai.metaData.getString(META_DATA_FUNC_NAME);
                 if (ln != null) funcname = ln;
             }
         } catch (PackageManager.NameNotFoundException e) {
             throw new RuntimeException("Error getting activity info", e);
         }
 
         BaseDexClassLoader classLoader = (BaseDexClassLoader) getClassLoader();
         String path = classLoader.findLibrary(libname);
...

大家還記得上一講我們貼的AndroidManifest.xml嗎?記得這一行meta data嗎:

      <!-- Tell NativeActivity the name of our .so -->
      <meta-data android:name="android.app.lib_name"
                 android:value="native-activity" />

我們的本地應用生成的so庫的名字,就是通過這里來讀出來的。

最后,通用本地代碼的入口在這里:

         mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(),
                 getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()),
                 getAbsolutePath(getExternalFilesDir(null)),
                 Build.VERSION.SDK_INT, getAssets(), nativeSavedState,
                 classLoader, classLoader.getLdLibraryPath());

入口點函數是funcname,大家還記得它的定義吧:

String funcname = "ANativeActivity_onCreate";
...
                 ln = ai.metaData.getString(META_DATA_FUNC_NAME);
                 if (ln != null) funcname = ln;

默認情況下,這個入口函數就是ANativeActivity_onCreate.

JNI層的實現

我們繼續跟進去看jni中對于loadNativeCode_native的調用:

 loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
         jobject messageQueue, jstring internalDataDir, jstring obbDir,
         jstring externalDataDir, jint sdkVersion, jobject jAssetMgr,
         jbyteArray savedState, jobject classLoader, jstring libraryPath) {
...

下面,根據funcName,就是默認是ANativeActivity_onCreate的這個函數,將變成函數指針,類型為ANativeActivity_createFunc

...
         void* funcPtr = NULL;
         const char* funcStr = env->GetStringUTFChars(funcName, NULL);
         if (needNativeBridge) {
             funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
         } else {
             funcPtr = dlsym(handle, funcStr);
         }
         code.reset(new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr));
         env->ReleaseStringUTFChars(funcName, funcStr);
 
         if (code->createActivityFunc == NULL) {
             ALOGW("ANativeActivity_onCreate not found");
             return 0;
         }
...

android_native_app_glue的實現

我們再來看看NDK中的android_native_app_glue中,入口函數ANativeActivity_onCreate都做了些什么:

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

可以看到,這個封裝中,除了設置了若干個callback函數以外,就是封裝成了android_app_create函數。

我們再來看android_app_create的實現:

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函數。

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

這一層的封裝,將處理AConfiguration。但是最主要的是要處理ALooper,構建一個消息隊列。
最終才會調用android_main,這一層才是android_native_app_glue封裝好的對外接口。

小結

  • Android支持通過本質上是個so的本地代碼來寫應用
  • 骨架代碼是Android framework中的NativeActivity,該類通過裝載so庫的方式來調用本地應用代碼
  • 默認的入口點在ANativeActivity_onCreate,函數名和庫名都可以在AndroidManifest.xml中配置

最后,我們來看一張圖來復習一下:

ANativeActivity procedure
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容