編輯:關於Android編程
不少公司在開發android產品的時候,都需要在android中增加自己的service,尤其是定制的工業用途的設備,我們公司的項目中就涉及到要增加一個service,是一個北斗通信service,具體的內容不便透露,涉及到保密。但是增加service的過程大概能描述一下,具體代碼就按著不重要的來帖,大家見諒!!
其實增加自己的service不論是誰來,我想都會仿照現有service來做,在android現有service中,最簡單明了的是vibrator service,其次的是location service也就是GPS,這兩個service 雖然簡單,但是service的架構都是相同,我們仿照的目標就是要這樣,明了的架構,往裡面填東西就是體力活了。下面我們從下往上一一來看每個步驟。
1.kernel層
我們的硬件連接到設備上一個串口,因此kernel層我們就不用什麼改動。當然如果你們添加的硬件設備需要驅動的話,自己加進去就是了,這裡不多說了。
2.HAL層
我們先找到了GPS的HAL層代碼 \android\hardware\imx\libgps中一共兩個文件,仔細分析來看,這兩個文件主要功能是生成一個動態鏈接庫,向下與硬件通信,向上為系統提供訪問的接口函數,知道了這個,我們就明白了HAL層大概的功能,我們的工作就是選一個適合自己硬件的方法實現這個功能。
我們的硬件是一個串口通信的設備,無非就是可以由上層控制來發送命令和接受命令,因此我選的是串口通信很常見的方式: 每條發送命令都單獨寫一個函數,有上層來控制發送哪條。接收命令就啟動一個接收線程,每當收到數據後,進行一系列的判定,把有效的數據傳送到上層。
下面我們來看具體代碼在android\hardware\imx\librd目錄中:
首先是我們與硬件通信的幾個函數:
static const RDInterface goldtelrdInterface = { sizeof(RDInterface), goldtel_rd_init, goldtel_rd_close, goldtel_rd_send_XTZJ, goldtel_rd_send_ICJC, goldtel_rd_send_SJSC, goldtel_rd_send_DWSQ, goldtel_rd_send_BBDQ, goldtel_rd_send_YHZL_SJTX, goldtel_rd_send_TXSQ, };
static int goldtel_rd_init(RDCallbacks* callbacks) { RdState* s = _rd_state; //lijianzhang write_sysfs("/sys/devices/platform/bd_power/enable_rdss","1",1); //給設備上電 usleep(1500000); if (!s->init) rd_state_init(s, callbacks);//進行一系列的初始化 注意這裡的一個參數callbacks, //這是一些回調函數,是在jni層實現的,傳到底層來運行 //android很多都是這麼來實現,如果以後看別的代碼看到 //類似方式就不要慌亂,去jni層裡肯定能找到 if (s->fd < 0) return -1; rd_state_start(s); //開始工作 return 0; }
static void rd_state_init( RdState* state, RDCallbacks* callbacks ) { ................. 一系列初始化 。。。。。。。。。。。。。。。。。。。 state->thread = callbacks->create_thread_cb( "rd_state_thread", rd_state_thread, state );//這個函數裡最主要的功能就是創建接收線程 。。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。。。。。。。 }下面來看接收線程裡的東西:
static void rd_state_thread( void* arg ) { RdState* state = (RdState*) arg; NmeaReader reader[1]; int epoll_fd = epoll_create(2); int started = 0; int rd_fd = state->fd; int control_fd = state->control[1]; nmea_reader_init( reader ); // 注冊epoll 文件 epoll_register( epoll_fd, control_fd ); epoll_register( epoll_fd, rd_fd ); LOGE("RD thread running"); // now loop for (;;) { struct epoll_event events[2]; int ne, nevents; nevents = epoll_wait( epoll_fd, events, 2, -1 ); //等待epoll消息 if (nevents < 0) { if (errno != EINTR) E("epoll_wait() unexpected error: %s", strerror(errno)); continue; } D("rd thread received %d events", nevents); for (ne = 0; ne < nevents; ne++) { //處理每條消息 if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) { E("EPOLLERR or EPOLLHUP after epoll_wait() !?"); return; } if ((events[ne].events & EPOLLIN) != 0) { int fd = events[ne].data.fd; if (fd == control_fd) //如果這個消息是控制命令的話,這裡的控制命令其實就兩條, //服務開始和服務結束,下面就是分別對著兩條命令進行處理 { char cmd = 255; int ret; D("rd control fd event"); do { ret = read( fd, &cmd, 1 ); } while (ret < 0 && errno == EINTR); if (cmd == CMD_QUIT) { //服務退出 if (started) { started = 0; nmea_reader_set_DWXX_callback(reader,NULL); nmea_reader_set_BBXX_callback(reader,NULL); nmea_reader_set_FKXX_callback(reader,NULL); nmea_reader_set_ICXX_callback(reader,NULL); nmea_reader_set_ZJXX_callback(reader,NULL); nmea_reader_set_TXHZ_callback(reader,NULL); nmea_reader_set_TXXX_callback(reader,NULL); } return; } else if (cmd == CMD_START) { //服務開始 if (!started) { LOGE("rd_state_thread start!"); started = 1; nmea_reader_set_DWXX_callback(reader,state->callbacks.dwxx_cb); nmea_reader_set_BBXX_callback(reader,state->callbacks.bbxx_cb); nmea_reader_set_FKXX_callback(reader,state->callbacks.fkxx_cb); nmea_reader_set_ICXX_callback(reader,state->callbacks.icxx_cb); nmea_reader_set_ZJXX_callback(reader,state->callbacks.zjxx_cb); nmea_reader_set_TXHZ_callback(reader,state->callbacks.txhz_cb); nmea_reader_set_TXXX_callback(reader,state->callbacks.txxx_cb); } } } else if (fd == rd_fd) //如果是串口通信的消息 { // LOGE("start read data"); char buff[32]; for (;;) { int nn, ret; ret = read( fd, buff, sizeof(buff) ); //從串口中讀出數據 if (ret < 0) { if (errno == EINTR) continue; if (errno != EWOULDBLOCK) E("error while reading from gps daemon socket: %s:", strerror(errno)); break; } for (nn = 0; nn < ret; nn++) { //LOGE("start read data %02X",buff[nn]); nmea_reader_addc( reader, buff[nn] ); //判定數據有效性並進行處理 } } } else { E("epoll_wait() returned unkown fd %d ?", fd); } } } } }
進程創建完成了,hal層基本功能就完成了,下一步就是將這些功能聲稱一個.so動態鏈接庫,方法就是下面的代碼:
static const RDInterface* get_rd_hardware_interface() { return &goldtelrdInterface; } static int open_rd(const struct hw_module_t* module, char const* name, struct hw_device_t** device) { struct rd_device_t *dev = malloc(sizeof(struct rd_device_t)); memset(dev, 0, sizeof(*dev)); dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = 0; dev->common.module = (struct hw_module_t*)module; dev->get_rd_interface = get_rd_hardware_interface; *device = (struct hw_device_t*)dev; return 0; } static struct hw_module_methods_t rd_module_methods = { .open = open_rd }; const struct hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .version_major = 1, .version_minor = 0, .id = RD_HARDWARE_MODULE_ID, .name = "Real6410 rd Module", .author = "The Android Open Source Project", .methods = &rd_module_methods, };
動態鏈接庫生成完了,下面就到了framework層,我們找到了location service的代碼在目錄\android\frameworks\base\services\jni\中功能就是打開這個動態鏈接庫然後,向java層提供函數接口,功能很簡單我們直接來看我寫的代碼在android\\frameworks\base\services\jni\com_android_server_rdmessage_RDMessageDispatch.cpp中
首先是打開動態鏈接庫
static const RDInterface* get_rd_interface() { int err; hw_module_t* module; const RDInterface* interface = NULL; err = hw_get_module(RD_HARDWARE_MODULE_ID, (hw_module_t const**)&module); if (err == 0) { hw_device_t* device; err = module->methods->open(module, RD_HARDWARE_MODULE_ID, &device); if (err == 0) { rd_device_t* rd_device = (rd_device_t *)device; interface = rd_device->get_rd_interface(rd_device); } } return interface; }最後的是想上層提供的函數接口
static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"class_init_native", "()V", (void *)android_rdmessage_RDMessageDispatch_class_init_native}, {"native_is_supported","()Z",(void*)android_rdmessage_RDMessageDispatch_is_supported}, {"native_BDMessage_start", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_BDMessage_start}, {"native_close","()V",(void*)android_rdmessage_RDMessageDispatch_native_close}, {"native_sendXTZJ", "(I)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendXTZJ}, {"native_sendICJC", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_sendICJC}, {"native_sendSJSC", "(I)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendSJSC}, {"native_sendDWSQ", "(ZZ)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendDWSQ}, {"native_sendBBDQ", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_sendBBDQ}, {"native_sendYHZL_SJTX", "(II)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendYHZL_SJTX}, {"native_SendBDMessage", "(I[BIIZ)Z", (void*)android_rdmessage_RDMessageDispatch_native_SendBDMessage}, {"read_TXXX_message", "([BI)I", (void*)android_rdmessage_RDMessageDispatch_read_TXXX_message}, {"read_DWXX_message","([B[B[B[B[B)V",(void*)android_rdmessage_RDMessageDispatch__read_DWXX_message}, };中間就是實現這兩者之間的轉換,我們來說轉換過程中比較重要的幾點首先是發送命令,很簡單,就是直接調用動態鏈接庫中提供的發送函數,看代碼
static jboolean android_rdmessage_RDMessageDispatch_native_sendICJC (JNIEnv *env, jobject obj) { if (!sRdInterface) return false; if(sRdInterface->rd_send_ICJC()!=0) return false; return true; }
static void ZJXX_callback(ZJXXInfo *zjxx) { JNIEnv* env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod(mCallbacksObj,method_reportZJXX,zjxx->UserId,zjxx->ICStatus, zjxx->YJStatus,zjxx->DCStatus,zjxx->RZStatus,zjxx->bs1Status,zjxx->bs2Status, zjxx->bs3Status,zjxx->bs4Status,zjxx->bs5Status,zjxx->bs6Status); checkAndClearExceptionFromCallback(env, __FUNCTION__); }
4. framework層
我們看到location service的代碼在android\frameworks\base\services\java\com\android\server\LocationManagerService.java
android\frameworks\base\services\java\com\android\server\location目錄 android\\frameworks\base\location\java\android\location目錄中
分析一下架構,就是向下接口JNI層的 代碼,在此基礎上封裝service實現函數,最後在servicemanager中運行這個服務,還有增加這個服務的aidl文件,以便於所有的app都能訪問到。所以我們所做的工作也是這些,其實比較簡單,主要是為界面提供支持,比如更新狀態欄北斗信號強度等等,這裡就不再貼代碼了,大家可以自己看gps是怎麼做的仿照來就行。
實現了這些函數最後要在android\frameworks\base\services\java\com\android\server\SystemServer.java中增加
try { //啟動這個service Slog.i(TAG, "RdMessage Manager"); rdmessage = new RdMessageManagerService(context); ServiceManager.addService(Context.RD_MESSAGE_SERVICE, rdmessage); } catch (Throwable e) { reportWtf("starting RdMessage Manager", e); }
try { if (rdmessageF != null) rdmessageF.systemReady(); //告訴系統服務啟動完成 } catch (Throwable e) { reportWtf("making RdMessage Service ready", e); }
到了這裡整個service添加就完成了,我們就可以通過app來訪問這個service了。
以前給大家介紹的xUtils是國內比較火的快速開發框架,但是它的注解機制不是太穩定而且注解可選也比較少,今天給大家介紹一個國外的一個框架主要專注於注解的開發,簡化Andr
本節引言: 不知道標題這兩個玩意你熟不熟悉啦,如果自己實現過圓角或者圓形圖片,相信對這兩個名詞 並不模式,一時半伙沒想起來?沒關系,下面這個圖你可曾見過?
使用HTTP訪問網絡資源 前面介紹了 URLConnection己經可以非常方便地與指定站點交換信息,URLConnection還有一個子類:HttpURL
本文只是寫了如何配置JDK,以及adt-bundle的配置。對於以前的adt-bundle的版本,會自帶CPU/ABI系統鏡像,經過本文所描述的兩個步驟後可以直接創建AV
(一)前言今天我們一起來看一下抽屜DrawerLayoutAndroid