文件位置: frameworks\base\services\jni 動態注冊文件:onload.cpp [cpp] #include "JNIHelp.h" #include "jni.h" #include "utils/Log.h" #include "utils/misc.h" namespace android { int register_android_server_AlarmManagerService(JNIEnv* env); int register_android_server_BatteryService(JNIEnv* env); int register_android_server_InputManager(JNIEnv* env); int register_android_server_LightsService(JNIEnv* env); int register_android_server_PowerManagerService(JNIEnv* env); int register_android_server_VibratorService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); int register_android_server_location_GpsLocationProvider(JNIEnv* env); }; using namespace android; extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("GetEnv failed!"); return result; } LOG_ASSERT(env, "Could not retrieve the env!"); register_android_server_PowerManagerService(env); register_android_server_InputManager(env); register_android_server_LightsService(env); register_android_server_AlarmManagerService(env); register_android_server_BatteryService(env); register_android_server_VibratorService(env); register_android_server_SystemServer(env); register_android_server_location_GpsLocationProvider(env); return JNI_VERSION_1_4; } 注冊文件主要做了兩件事情:1在android空間聲明注冊函數2.在C空間注冊jni中注冊相關的服務 將注冊文件編譯進內核 [cpp] LOCAL_SRC_FILES:= \ com_android_server_AlarmManagerService.cpp \ com_android_server_BatteryService.cpp \ com_android_server_InputManager.cpp \ com_android_server_LightsService.cpp \ com_android_server_PowerManagerService.cpp \ com_android_server_SystemServer.cpp \ com_android_server_UsbService.cpp \ com_android_server_VibratorService.cpp \ com_android_server_location_GpsLocationProvider.cpp \ onload.cpp 3.JNI實現(com_android_server_LightsService.cpp) [cpp] /* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "LightsService" #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include <utils/misc.h> #include <utils/Log.h> #include <hardware/hardware.h> #include <hardware/lights.h> #include <stdio.h> namespace android { // These values must correspond with the LIGHT_ID constants in // LightsService.java enum { LIGHT_INDEX_BACKLIGHT = 0, LIGHT_INDEX_KEYBOARD = 1, LIGHT_INDEX_BUTTONS = 2, LIGHT_INDEX_BATTERY = 3, LIGHT_INDEX_NOTIFICATIONS = 4, LIGHT_INDEX_ATTENTION = 5, LIGHT_INDEX_BLUETOOTH = 6, LIGHT_INDEX_WIFI = 7, LIGHT_COUNT }; struct Devices { light_device_t* lights[LIGHT_COUNT]; }; static light_device_t* get_device(hw_module_t* module, char const* name) { int err; hw_device_t* device; err = module->methods->open(module, name, &device); if (err == 0) { return (light_device_t*)device; } else { return NULL; } } static jint init_native(JNIEnv *env, jobject clazz) { int err; hw_module_t* module; Devices* devices; devices = (Devices*)malloc(sizeof(Devices)); err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); if (err == 0) { devices->lights[LIGHT_INDEX_BACKLIGHT] = get_device(module, LIGHT_ID_BACKLIGHT); devices->lights[LIGHT_INDEX_KEYBOARD] = get_device(module, LIGHT_ID_KEYBOARD); devices->lights[LIGHT_INDEX_BUTTONS] = get_device(module, LIGHT_ID_BUTTONS); devices->lights[LIGHT_INDEX_BATTERY] = get_device(module, LIGHT_ID_BATTERY); devices->lights[LIGHT_INDEX_NOTIFICATIONS] = get_device(module, LIGHT_ID_NOTIFICATIONS); devices->lights[LIGHT_INDEX_ATTENTION] = get_device(module, LIGHT_ID_ATTENTION); devices->lights[LIGHT_INDEX_BLUETOOTH] = get_device(module, LIGHT_ID_BLUETOOTH); devices->lights[LIGHT_INDEX_WIFI] = get_device(module, LIGHT_ID_WIFI); } else { memset(devices, 0, sizeof(Devices)); } return (jint)devices; } static void finalize_native(JNIEnv *env, jobject clazz, int ptr) { Devices* devices = (Devices*)ptr; if (devices == NULL) { return; } free(devices); } static void setLight_native(JNIEnv *env, jobject clazz, int ptr, int light, int colorARGB, int flashMode, int onMS, int offMS, int brightnessMode) { Devices* devices = (Devices*)ptr; light_state_t state; if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) { return ; } memset(&state, 0, sizeof(light_state_t)); state.color = colorARGB; state.flashMode = flashMode; state.flashOnMS = onMS; state.flashOffMS = offMS; state.brightnessMode = brightnessMode; devices->lights[light]->set_light(devices->lights[light], &state); } static JNINativeMethod method_table[] = { { "init_native", "()I", (void*)init_native },//int init_native() { "finalize_native", "(I)V", (void*)finalize_native },//void finalize_native(int) { "setLight_native", "(IIIIIII)V", (void*)setLight_native },//void setLight_native(int,int,int,int,int,int,int) }; int register_android_server_LightsService(JNIEnv *env) { return jniRegisterNativeMethods(env, "com/android/server/LightsService", method_table, NELEM(method_table)); } }; 這個文件主要完成的事情有 1.完成JNI向java的注冊: jniRegisterNativeMethods,這個函數體現JNI大部分的關鍵點,主要有env,JNI命名規則,函數簽名。 當VM載入libxxx_jni.so這個庫時,就會呼叫JNI_OnLoad()函數。在JNI_OnLoad()中注冊本地函數,繼續調用到AndroidRuntime::registerNativeMethods(),該函數向VM(即AndroidRuntime)注冊gMethods[]數組中包含的本地函數了。AndroidRuntime::registerNativeMethods()起到了以下兩個作用: 1,registerNativeMethods()函數使得java空間中的Native函數更加容易的找到對應的本地函數。(通過gMethods[]中的函數指針) 2,可以在執行期間進行本地函數的替換。因為gMethods[]數組是一個<java中函數名字,本地函數指針>的對應表,所以可以在程序的執行過程中,多次呼叫registerNativeMethods()函數來更換本地函數的指針,提高程序的彈性。 2.JNIEnv 介紹 JNIEnv 是一個與線程相關的變量,由於線程相關,所以線程B中不能使用線程A中的JNIEnv函數。那個多個線程由誰來保存並保證每個線程的JNIEnv結構體正確呢? jint JNI_OnLoad(JavaVM* vm, void* reserved)全進程只有一個JavaVM對象,可以保存並在任何地方使用沒有問題,獨此一份。利用JavaVM中的 AttachCurrentThread函數,就可以得到這個線程的 JNIEnv結構體,利用用DetachCurrnetThread釋放相應資源。 一般通過JNIEnv 操作jobject的jfieldID 操作成員變量和jmethodID 操作成員函數。這個再JNI中創建的每個資源都有自己的ID,在Java空間中通過找到這些ID,就可以找到相對應的成員變量和成員函數。 static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,const char* name, const char* sig) static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,const char* sig) 3.JNI命名規則 com_android_server_LightsService.cpp,表明這個服務是在com/android/server/下的LightsService.cpp文件,在注冊的時候以com/android/server/LightsService作為參數傳下去。 4.函數簽名 因為JAVA支持函數重載,可以定義同名但不同參數的函數,但直接根據函數名是沒法找到具體函數的,因此利用參數類型及返回類型組成簽名信息。利用JNINativeMethod結構保存其關系。 typedef struct { //JAVA中native函數名字 const char *name; //簽名信息,用字符串表示,參數類型及返回值類型的組合 const char *signature; ///JNI層函數函數指針,轉換為void*類型 void *fnPtr; }; 常用類型標識符: 類型標識 JAVA類型 字長 Z boolean 8位 B byte 8位 C char 16位 -- 注意喲 S short 16位 I int 32位 J long 64位 F float 32位 D double 64位 L/java/languageString String [I int[] int數組 [L/java/lang/object Object[] 對象數組 在method_table中主要完成了JNI對HAL的封裝函數。 5.垃圾回收 finalize_native 4.JNI其他功能 1.異常處理 常用的異常處理函數 Throw():丟棄一個現有的異常對象;在固有方法中用於重新丟棄一個異常。 ThrowNew():生成一個新的異常對象,並將其丟棄。 ExceptionOccurred():判斷一個異常是否已被丟棄,但尚未清除。 ExceptionDescribe():打印一個異常和堆棧跟蹤信息。 ExceptionClear():清除一個待決的異常。 FatalError():造成一個嚴重錯誤,不返回。 在所有這些函數中,最不能忽視的就是ExceptionOccurred()和ExceptionClear()。大多數JNI函數都能產生異常,而且沒有象在Java的try塊內的那種語言特性可供利用。所以在每一次JNI函數調用之後,都必須調用ExceptionOccurred(),了解異常是否已被丟棄。若偵測到一個異常,可選擇對其加以控制(可能時還要重新丟棄它)。然而,必須確保異常最終被清除。這可以在自己的函數中用ExceptionClear()來實現;若異常被重新丟棄,也可能在其他某些函數中進行。