Android——NativeActivity - C/C++ Apk開發

android基本的四大組件之一Activity,android開發的第一個hello world 建立的就是這個繼承了Activity類的類,擁有對應的生命週期,由AMS維護,只須要重寫父類對應的方法便可,但這都是在Java層面,若是想往C/C++層跑,就須要JNI去訪問,這樣基本能夠知足許多性能有要求的apk開發需求。 
可是那些原生是基於C/C++編寫的大型軟件程序,就並非這麼好抽取JNI接口讓android 的java層調度了。若是想移植實現那就麻煩了, 個人理解 這個NativeActivity 機制至關於一個適配層,將android的那一套組件運行時機制 轉義爲C/C++開發程序所能接收處理的一層進行調度管理,不由想起以前在 《程序員的自我修改-連接.裝載.庫》 中看到那句名言: 計算機科學領域的任何問題均可以經過增長一個間接的中間層來解決java


java-apk層面

NativeActivity 爲Activity的子類,實如今:\frameworks\base\core\java\android\app\NativeActivity.java 
主要是對Activity的一些生命週期函數的封裝,貼出值得關注的:android

* {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all} */ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, InputQueue.Callback, OnGlobalLayoutListener { ... public static final String META_DATA_LIB_NAME = "android.app.lib_name"; ... private native long loadNativeCode(String path, String funcname, MessageQueue queue, String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, AssetManager assetMgr, byte[] savedState); ... private native void onStartNative(long handle); private native void onResumeNative(long handle); ... @Override protected void onCreate(Bundle savedInstanceState) { String libname = "main"; String funcname = "ANativeActivity_onCreate"; ... 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; ... mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(), getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()), getAbsolutePath(getExternalFilesDir(null)), Build.VERSION.SDK_INT, getAssets(), nativeSavedState); ... @Override protected void onStart() { super.onStart(); onStartNative(mNativeHandle); } @Override protected void onStop() { super.onStop(); onStopNative(mNativeHandle); }

封裝的一堆native方法天然一目瞭然,關鍵在 onCreate 中對本地方法 loadNativeCode 的調用,傳遞的參數 libname funcname纔是重點,是去加載對應的code lib 和本地的函數入口。 
能夠看到會讀取apk 的 metaData 中的 android.app.lib_name 來獲取libnamec++

先不去管native 的具體實現,先看下這個NativeActivity中提到的samples/native-activity ,下載個NDK,目錄在: 
android-ndk-r8b\samples\native-activity 
看下這個apk的AndroidManifest.xml:程序員

<?xml version="1.0" encoding="utf-8"?> <!-- BEGIN_INCLUDE(manifest) --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.native_activity" android:versionCode="1" android:versionName="1.0"> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVersion="9" /> <!-- This .apk has no Java code itself, so set hasCode to false. --> <application android:label="@string/app_name" android:hasCode="false"> <!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="android.app.NativeActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden"> <!-- Tell NativeActivity the name of or .so --> <meta-data android:name="android.app.lib_name" android:value="native-activity" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> <!-- END_INCLUDE(manifest) -->

注意兩點便可: 
android:hasCode=」false」 因此 android:name=」android.app.NativeActivity」 這個apk的java源程序就是上面的NativeActivity,本身沒有Java codeapi

 

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := native-activity LOCAL_SRC_FILES := main.c LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM LOCAL_STATIC_LIBRARIES := android_native_app_glue include $(BUILD_SHARED_LIBRARY) $(call import-module,android/native_app_glue)

android_native_app_glue 這個纔是重點,次靜態庫源碼在: 
android-ndk-r8b\sources\android\native_app_glue 
若有興趣可細看android_native_app_glue.c , 代碼也不長,其實也是封了一層,詳細代碼就不貼了ruby

往下簡述一下 從上面的NativeActivity 怎麼調用到main.c 裏面的流程:app


android_native_app_glue 機制封裝層面

  1. loadNativeCode 調用到JNI本地方法:\frameworks\base\core\jni\android_app_NativeActivity.cpp中的:
static jlong
loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
        jobject messageQueue, jstring internalDataDir, jstring obbDir,
        jstring externalDataDir, jint sdkVersion,
        jobject jAssetMgr, jbyteArray savedState)
{
    LOG_TRACE("loadNativeCode_native"); ... void* handle = dlopen(pathStr, RTLD_LAZY); ... if (handle != NULL) { void* funcPtr = NULL; const char* funcStr = env->GetStringUTFChars(funcName, NULL); if (needNativeBridge) { funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0); } else { funcPtr = dlsym(handle, funcStr); } code = new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr);

在lib中找的天然就是java中定義的 ANativeActivity_onCreate 這個函數入口 
這個實現函數在 android_native_app_glue.c 中。ide

  1. android_native_app_glue中的調度到咱們自身的代碼入口android_main過程: 
    看入口代碼:
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); }

這裏開始把C/C++層面上的接口 去 對接上android activity的運行回調,這些個onStart onResume….函數

// --------------------------------------------------------------------
// 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_create(&android_app->thread, &attr, android_app_entry, android_app); ... }

細節省略,能夠看到調進來這裏的是主線程,也就是最開始 NativeActivity.java裏面的onCreate回調函數,比較重要的一個概念:C/C++開發的程序都是運行在一個子線程,這也是必然的,否則那麼大一堆程序代碼的初始化之類的,主UI線程一定卡死,這也帶來一個麻煩事~ 子線程中對UI 的操控,不會有像java子線程同樣方便的handle message 給你調,去更新主線程UI了~ 這裏是C/C++的pthread_create ,並且UI的資源也通常是在C/C++層,怎麼繪製,怎麼管理~ 繼續往下~oop

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

這裏的 looper 機制比較複雜~ 不去深究了,注意process_cmd process_input ,android_app 結構體 
這兩個 process_xxx 是做爲looper中檢測到事件以及命令時觸發的處理邏輯 
往下進入 android_main(android_app) 也就正式進入 咱們本身開發的 C/C++ 代碼了~


c/c++ lib庫實現層面

仍是看例子:\android-ndk-r8b\samples\native-activity\jni\main.c

/** * This is the main entry point of a native application that is using * android_native_app_glue. It runs in its own thread, with its own * event loop for receiving input events and doing other things. */ void android_main(struct android_app* state) { struct engine engine; // Make sure glue isn't stripped. app_dummy(); memset(&engine, 0, sizeof(engine)); state->userData = &engine; state->onAppCmd = engine_handle_cmd; state->onInputEvent = engine_handle_input; engine.app = state; ... // Prepare to monitor accelerometer engine.sensorManager = ASensorManager_getInstance(); engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, ASENSOR_TYPE_ACCELEROMETER); engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, state->looper, LOOPER_ID_USER, NULL, NULL); ... // loop waiting for stuff to do. while (1) { ... struct android_poll_source* source; // If not animating, we will block forever waiting for events. // If animating, we loop until all events are read, then continue // to draw the next frame of animation. while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, (void**)&source)) >= 0) { // Process this event. if (source != NULL) { source->process(state, source); } ... } }

這個例子 sensor部分的不作關注~ 只關注機制,能夠看到 :

state->onAppCmd = engine_handle_cmd;
    state->onInputEvent = engine_handle_input;

這兩行是重點,實現對上面說到過的 process_xxx 的處理了,怎麼對應上的可查看 android_native_app_glue.c 的代碼~ 
往下進入while ,ALooper_pollAll 去等待事件,調用source->process(state, source) 去處理 
先轉到android_native_app_glue ,再轉到這裏的engine_handle_cmd 或者 engine_handle_input 
詳細的處理就不說了~

還有上面說到過的在這個子線程裏面~UI 怎麼辦呢~ 
這個例子裏面是這麼作的,調EGL的api繪圖~因此前面的Android.mk 須要加入lib庫 -lEGL -lGLESv1_CM,繪製顯示的初始化函數engine_init_display

總結

主要是從上往下,理清層次關係,調用邏輯,弄清楚運行的機制,仍是頗有意思的~ 這個例子比較簡單,後面有機會分析C/C++大型軟件用這種方式適配成apk的例子,除了Activity的一些生命週期,事件處理,還能夠傳遞廣播以及監聽

相關文章
相關標籤/搜索