編輯:關於Android編程
在分析完解析init.rc的action之後,剩下的一部分就是解析service了。
而解析service還是需要回到parse_config裡面來。根據前面的知識,我們也可以很容易的知道在關鍵字為section的時候,會進入到parse_new_section。
這裡會先執行parse_service,然後將service以及後面跟的option設置為執行parse_line:parse_line_service。
要理解service的解析流程的話,首先要關注的就是service的結構體。
struct service { /* list of all services */ struct listnode slist; // listnode slist const char *name; // name const char *classname; // 默認值為defult unsigned flags; // 選項 pid_t pid; // Service所在進程的PID time_t time_started; /* time of last start */ time_t time_crashed; /* first crash within inspection window */ int nr_crashed; /* number of times crashed within window */ uid_t uid; // effective user ID gid_t gid; // effective group ID gid_t supp_gids[NR_SVC_SUPP_GIDS]; // supplementary ids size_t nr_supp_gids; // supp_gids的大小 char *seclabel; struct socketinfo *sockets; // 為service創建的Sockets struct svcenvinfo *envvars; // 為service設置的環境變量 struct action onrestart; /* Actions to execute on restart. */ /* keycodes for triggering this service via /dev/keychord */ int *keycodes; int nkeycodes; int keychord_id; int ioprio_class; int ioprio_pri; int nargs; /* MUST BE AT THE END OF THE STRUCT */ char *args[1]; }; /* ^-------'args' MUST be at the end of this struct! */
這個結構體相比較而言就比較簡單了,除了service的本身屬性以外,對於數據結構方面就只有一個listnode。
struct listnode slist;這也就是說,在service的結構體中,這個結構體只會被加入一條鏈表而不是像action的兩條鏈表。
另外需要注意的是,在service的結構體中,也維護了一個action的結構體,這就是說,在service中,也存在著一個action的commands的鏈表?
然後我們就來看看parse_service的函數
static void *parse_service(struct parse_state *state, int nargs, char **args) { struct service *svc; // 聲明結構體 if (nargs < 3) { // 如果service的參數小於三個的話,我們會認為service是個不正常的service。service最少的nargs也是3,分別為service關鍵字,service的名字,service啟動的時候要執行的命令 parse_error(state, services must have a name and a program ); return 0; } if (!valid_name(args[1])) { //如果service的name為不標准的名字的話,含有其它的符號的話,我們會認為這個service是不規范的service。 parse_error(state, invalid service name '%s' , args[1]); return 0; } svc = service_find_by_name(args[1]); 。。// 會從已經存在的service_list裡面去查找,是否已經有同名的service存在 if (svc) { // 如果發現有同名的service存在的話,則會返回error parse_error(state, ignored duplicate definition of service '%s' , args[1]); return 0; } nargs -= 2; // 去除service關鍵字與service的name svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); // malloc這個結構體 if (!svc) { // 如果malloc失敗的話,提示out of memory parse_error(state, out of memory ); return 0; } svc->name = args[1]; // 設置service的name為service關鍵字後的第一個參數 svc->classname = default; // 默認的classname為default memcpy(svc->args, args + 2, sizeof(char*) * nargs); // 將args剩余的參數復制到svc的args裡面 svc->args[nargs] = 0; // 給args的最後一項設置為0 svc->nargs = nargs; // 參數的數目等於傳進來的參數的數目 svc->onrestart.name = onrestart; // 設置onrestart.name為onrestart list_init(&svc->onrestart.commands); // 初始化onrestart的鏈表 list_add_tail(&service_list, &svc->slist); // 將當前的service結構體加入到了service_list的鏈表裡面 return svc; }從上面我們知道,在執行完parse_service之後,會初始化service的一些屬性,將service servicename之後的做為args進行保存。
另外,將這個解析出來的service加入到service_list的鏈表裡面。
然後接下來,去執行的就是
state->parse_line = parse_line_service;那我們像action,再來看看parse_line_service的操作
static void parse_line_service(struct parse_state *state, int nargs, char **args) { struct service *svc = state->context; struct command *cmd; int i, kw, kw_nargs; if (nargs == 0) { return; } svc->ioprio_class = IoSchedClass_NONE; kw = lookup_keyword(args[0]); switch (kw) { case K_capability: break; case K_class: if (nargs != 2) { parse_error(state, class option requires a classname ); } else { svc->classname = args[1]; } break; case K_console: svc->flags |= SVC_CONSOLE; break; case K_disabled: svc->flags |= SVC_DISABLED; svc->flags |= SVC_RC_DISABLED; break; case K_ioprio: if (nargs != 3) { parse_error(state, ioprio optin usage: ioprio
可以看到,parse_line_service的這個函數,主要就是將解析service的每一行,將其對應進不同的case裡面,進行service結構體的填充。
可能這樣講,理解起來會有困難。
但是為了方便理解,我們從init.rc裡面找個例子出來分析:
service servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart healthd onrestart restart zygote onrestart restart media onrestart restart surfaceflinger onrestart restart drm在parse_line_service的時候,會在
class的關鍵字的時候,執行到
case K_class: if (nargs != 2) { // 判斷是否是兩個token,如果不是的話,格式錯誤 parse_error(state, class option requires a classname ); } else { svc->classname = args[1]; } break;也就是將service的classname從default修改為“args[1]”
會去執行
case K_user: if (nargs != 2) { parse_error(state, user option requires a user id ); } else { svc->uid = decode_uid(args[1]); } break;會將uid設置為system對應的uid
另外需要注意的是,在解析的過程中,會有一個比較重要的option是restart,來看一下當執行到這個關鍵字的時候的時候,會運行什麼。
case K_onrestart: nargs--; args++; kw = lookup_keyword(args[0]); if (!kw_is(kw, COMMAND)) { // 判斷是否是command,如果restart之後跟的不是command的話,就會返回error parse_error(state, invalid command '%s' , args[0]); break; } kw_nargs = kw_nargs(kw); // 獲得當前command所需的參數 if (nargs < kw_nargs) { // 如果傳遞的參數小於我們需要的參數的話,會返回error parse_error(state, %s requires %d %s , args[0], kw_nargs - 1, kw_nargs > 2 ? arguments : argument); break; } cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); // 初始化command cmd->func = kw_func(kw); // 將kw所包含的func賦值給cmd->func cmd->nargs = nargs; // 將參數的個數保存為nargs memcpy(cmd->args, args, sizeof(char*) * nargs); // 將這些參數復制到cmd的args中 list_add_tail(&svc->onrestart.commands, &cmd->clist); // 將這些command加入到service結構體的內部鏈表中 break;
在service的解析後,會生成一條鏈表保存service的結構體,然後service的結構體裡面自己運行維護一個action。
這個action會包含所有的restatt包含的內容,也就是restart的option關鍵字後會包含要執行的command
這個的結構應該比action的要簡單和明了的多。
分析完了解析,我們應該去看一下android是如何在啟動的過程中去執行這些action和service的。
前言開發做得久了,總免不了會遇到各種坑。而在Android開發的路上,『軟鍵盤擋住了輸入框』這個坑,可謂是一個曠日持久的巨坑——來來來,我們慢慢看。入門篇最基本的情況,如
先看個簡單的,先上個效果圖,吸引大家一下眼球。三個頁面間的滑動,此時是帶著上面的標題一塊滑動的。看一下android 對於PagerTitleStrip的官方解釋:Pag
引言 Andr
arcgis api for js默認的Navigation控件樣式風格如下圖: 這樣的風格不能說不好,各有各的愛好,審美觀,這裡也不是重點,這裡的重點是如何自