android 電話管理

http://hi.baidu.com/anly%5Fjun/blog/index/0

Andriod Phone模塊相關(總覽)
2010-01-30 13:50
1、從java端發送at命令的處理流程。
2、unsolicited 消息從modem上報到java的流程。
3、貓相關的各種狀態的監聽和通知機制。
4、通話相關的圖標變換的工作原理。
5、gprs撥號上網的通路原理。
6、通話相關的語音通路切換原理、震動接口。
7、通話相關的notification服務。
8、通話相關的各種server。

Andriod Phone模塊相關(一)
2010-01-30 14:52
第一部分:從java端發送at命令的處理流程。
撥出電話流程:
1、Contacts的AndroidManifest.xml 中android:process="android.process.acore"說明此應用程序運行在acore進程中。
DialtactsActivity的intent-filter的action屬性設置爲main,catelog屬性設置爲launcher,所以此activity能出現在主菜單中,並且是點擊此應用程序的第一個界面。dialtactsactivity包含四個tab,分別由TwelveKeyDialer、RecentCallsListActivity,兩個activity-alias DialtactsContactsEntryActivity和DialtactsFavoritesEntryActivity分別表示聯繫人和收藏tab,但是正真的聯繫人列表和收藏是由ContactsListActivity負責。
2、進入TwelveKeyDialer 中OnClick方法,按住的按鈕id爲:R.id.dialButton,執行placecall()方法:
Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,Uri.fromParts("tel", number, null));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
3、intert.ACTION_CALL_PRIVILEGED實際字符串爲android.intent.action.CALL_PRIVILEGED,通過查找知道了packegs/phone下面的AndroidManifest.xml中PrivilegedOutgoingCallBroadcaster activity-alias設置了intent-filter,所以需要找到其targetactivity爲OutgoingCallBroadcaster。所以進入OutgoingCallBroadcaster的onCreate()中:
String action = intent.getAction();
String number = PhoneNumberUtils.getNumberFromIntent(intent, this);
if (number != null) {
number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
number = PhoneNumberUtils.stripSeparators(number);
}
final boolean emergencyNumber =
(number != null) && PhoneNumberUtils.isEmergencyNumber(number);
獲取過來的Action以及Number,並對Action以及Number類型進行判斷。
//如果爲callNow = true;則啓動InCall界面:
intent.setClass(this, InCallScreen.class);
startActivity(intent);
併發送廣播給OutgoingCallReceiver:
Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
if (number != null) broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);
broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, intent.getData().toString());
sendOrderedBroadcast(broadcastIntent, PERMISSION,
new OutgoingCallReceiver(), null, Activity.RESULT_OK, number, null);
4、Intent.ACTION_NEW_OUTGOING_CALL實際字符串android.intent.action.NEW_OUTGOING_CALL,通過查找知道了packegs/phone下面的androidmanifest.xml中OutgoingCallReceiver Receiver接收此intent消息。找到OutgoingCallBroadcaster類中的內部類OutgoingCallReceiver,執行onReceive()函數:
執行doReceive(context, intent);方法:
獲取傳給來的號碼,根據PhoneApp的實例獲取PhoneType等。最後啓動InCall界面:
Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
newIntent.setClass(context, InCallScreen.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
5、請求撥號的java部分流程

6、請求撥號的c/c++部分流程
6.1、初始化事件循環,啓動串口監聽,註冊socket監聽。
rild.c->main()
(1)、RIL_startEventLoop
//建立事件循環線程
ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
//註冊進程喚醒事件回調
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL);
rilEventAddWakeup (&s_wakeupfd_event);
//建立事件循環
ril_event_loop
for (;;) {
...
n = select(nfds, &rfds, NULL, NULL, ptv);
// Check for timeouts
processTimeouts();
// Check for read-ready
processReadReadies(&rfds, n);
// Fire away
firePending();
}
(2)、funcs = rilInit(&s_rilEnv, argc, rilArgv);//實際是通過動態加載動態庫的方式執行reference-ril.c中的RIL_Init
//單獨啓動一個線程讀取串口數據
ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);
fd = open (s_device_path, O_RDWR);
ret = at_open(fd, onUnsolicited);
ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);

在initializeCallback中執行的程序:
setRadioState (RADIO_STATE_OFF);
at_handshake();
/* note: we don't check errors here. Everything important will
be handled in onATTimeout and onATReaderClosed */
/* atchannel is tolerant of echo but it must */
/* have verbose result codes */
at_send_command("ATE0Q0V1", NULL);
/* No auto-answer */
at_send_command("ATS0=0", NULL);
...
//註冊rild socket端口事件監聽到事件循環中
(3)、RIL_register(funcs);
s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
ret = listen(s_fdListen, 4);
ril_event_set (&s_listen_event, s_fdListen, false,
listenCallback, NULL);//將此端口加入事件select隊列
rilEventAddWakeup (&s_listen_event);

如果rild socket端口有數據來了將執行listencallback函數
listencallback
//爲此客戶端連接創建新的監聽句柄,s_fdListen繼續監聽其他客戶端的連接。
s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
ril_event_set (&s_commands_event, s_fdCommand, 1,
processCommandsCallback, p_rs);//將此端口加入事件select隊列
rilEventAddWakeup (&s_commands_event);
6.2、socket監聽,收到dial的socket請求
processCommandsCallback
//讀數據到p_record中
ret = record_stream_get_next(p_rs, &p_record, &recordlen);
processCommandBuffer(p_record, recordlen);
p.setData((uint8_t *) buffer, buflen);
// status checked at end
status = p.readInt32(&request);
status = p.readInt32 (&token);//請求隊列中的序號
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
pRI->token = token;

/*
包含#include "ril_commands.h"語句,結構體如下:
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
*/
pRI->pCI = &(s_commands[request]);
pRI->p_next = s_pendingRequests;
s_pendingRequests = pRI;
pRI->pCI->dispatchFunction(p, pRI);

//假設是接收了dial指令,pRI->PCI->dispatchFunction(p,pRI),調用dispatchDial (p,pRI)
dispatchDial (p,pRI)
s_callbacks.onRequest(pRI->pCI->requestNumber, &dial, sizeof(dial), pRI);
in reference-ril.c onRequest()
...
switch (request) {
case RIL_REQUEST_DIAL:
requestDial(data, datalen, t);
asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);
ret = at_send_command(cmd, NULL);
err = at_send_command_full (command, NO_RESULT, NULL, NULL, 0, pp_outResponse);
err = at_send_command_full_nolock(command, type, responsePrefix, smspdu,timeoutMsec, sponse);
err = writeline (command);
//此處等待,直到收到成功應答或失敗的應答,如:ok,connect,error cme等
err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
waiting....
waiting....

/* success or failure is ignored by the upper layer here.
it will call GET_CURRENT_CALLS and determine success that way */
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
p.writeInt32 (RESPONSE_SOLICITED);
p.writeInt32 (pRI->token);
errorOffset = p.dataPosition();
p.writeInt32 (e);
if (e == RIL_E_SUCCESS) {
/* process response on success */
ret = pRI->pCI->responseFunction(p, response, responselen);
if (ret != 0) {
p.setDataPosition(errorOffset);
p.writeInt32 (ret);
}
}
sendResponse(p);
sendResponseRaw(p.data(), p.dataSize());
blockingWrite(fd, (void *)&header, sizeof(header));
blockingWrite(fd, data, dataSize);
6.4、串口監聽收到atd命令的應答"OK"或"no carrier"等
readerLoop()
line = readline();
processLine(line);
handleFinalResponse(line);
pthread_cond_signal(&s_commandcond);//至此,前面的等待結束,接着執行RIL_onRequestComplete函數
6.5、java層收到應答後的處理,以dial爲例子.
ril.java->RILReceiver.run()
for(;;)
{
...
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p);
type = p.readInt();
if (type == RESPONSE_SOLICITED) {
processSolicited (p);
serial = p.readInt();
rr = findAndRemoveRequestFromList(serial);
rr.mResult.sendToTarget();
......
}
CallTracker.java->handleMessage (Message msg)
switch (msg.what) {
case EVENT_OPERATION_COMPLETE:
ar = (AsyncResult)msg.obj;
operationComplete();
cm.getCurrentCalls(lastRelevantPoll);

Andriod Phone模塊相關(二)
2010-01-30 15:09
第二部分:unsolicited 消息從modem上報到java的流程。
C++部分:
readerLoop()
line = readline();
processLine(line);
handleUnsolicited(line);
if (s_unsolHandler != NULL) {
s_unsolHandler (line1, line2);//實際執行的是void onUnsolicited (const char *s, const char *sms_pdu)
if (strStartsWith(s,"+CRING:")|| strStartsWith(s,"RING")
|| strStartsWith(s,"NO CARRIER") || strStartsWith(s,"+CCWA") )
RIL_onUnsolicitedResponse (RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, NULL, 0);
p.writeInt32 (RESPONSE_UNSOLICITED);
p.writeInt32 (unsolResponse);
ret = s_unsolResponses[unsolResponseIndex].responseFunction(p, data, datalen);
ret = sendResponse(p);
sendResponseRaw(p.data(), p.dataSize());
ret = blockingWrite(fd, (void *)&header, sizeof(header));
blockingWrite(fd, data, dataSize);
Java部分:
ril.java->RILReceiver.run()
for(;;)
{
...
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p);
processUnsolicited (p);
response = p.readInt();
switch(response) {
...
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break;
...
}
switch(response) {
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
if (RILJ_LOGD) unsljLog(response);
mCallStateRegistrants
.notifyRegistrants(new AsyncResult(null, null, null));
...
}

Andriod Phone模塊相關(三、四)
2010-02-01 10:51
第三部分:貓相關的各種狀態的監聽和通知機制
第四部分:通話相關的圖標變換的工作原理。
A. 註冊監聽部分

B.事件通知部分

注:所有的狀態改變通知都在TelephonyRegistry中處理,詳見該類源碼。

手機SIM卡功能解析
2010-02-01 17:25
SIM卡是GSM手機特有的用戶身份的象徵。
那麼,SIM卡到底具有哪些功能,其原理如何呢?下面作一簡要描述。
SIM卡作爲用戶身份的象徵,主要含有以下兩種信息:IMSI號和鑑權、加密算法。

IMSI號全稱爲國際移動臺用戶識別號,與IMEI國際移動設備識別號是完全不同的兩個概念。IMSI號是固化在SIM卡內部存儲芯片上的號碼。當客戶申請入網時,電信營業人員隨意拿來一張嶄新的SIM卡,將卡上標註的15位IMSI號,對應記錄在用戶挑選的號碼資料中,輸入電腦建立檔案。這就是GSM系統方便快捷的入網方式。
IMEI號則是一部手機機身內部固有的一個號碼,反應這部手機的出廠地、所屬廠商等一系列信息。
這兩個號碼的不同體現了GSM系統機、號分開的原則。
GSM系統具有良好的保密性還體現在SIM卡上。在用戶上網通話時,需要在空中傳送IMSI號碼以便鑑權。IMSI號碼在空中傳送是經過SIM卡中的鑑權、加密運算後發送的。經過這些複雜的運算,破譯基本上是不可能的。這也是GSM系統優於ETACS系統的一大體現。
從外觀上看,SIM卡有大、小卡之分,這是爲滿足不同手機的不同尺寸需求而設計的。但隨着手機市場日益小巧、輕便的發展趨勢,越來越多的廠商淘汰了大卡機型,小卡越來越受到青睞。
觀察SIM卡可以看到每張卡上,都有8個金屬觸腳,它們分別有如下功能,見圖1。

圖1 SIM卡引腳
SIM卡的供電有兩種:5V和3V。早期的SIM卡一般是5V供電。隨着人們對電池使用時間的要求日趨加長,廠家採取了各種手法來降低手機的用電量,包括將CPU由原來的5V左右供電降至3V左右,隨之手機整體機身的供電也基本上降到3V左右,這樣SIM卡供電電壓的下降也就勢在必行了。目前,許多SIM卡可以兼容兩種電壓供電,這是爲了適應過渡時期的需要。
另外,SIM卡的容量也不相同,這取決於SIM卡內部存儲芯片的內存容量大小。卡的容量體現在用戶使用電話簿功能時能往SIM卡上存多少條記錄。
在日常使用時,有時會出現"SIM卡不被接受"、"請插入SIM卡"等不正常的現象。這時,我們可以將SIM卡從機內取出,用橡皮輕輕地擦卡面。切不可用尖銳的東西刮卡面,以免造成卡觸腳不平而接觸不良,甚至徹底損壞SIM卡。如果擦拭後仍無法正常使用,則應將手機連卡送到專業維修點,讓維修人員檢查。

Android Phone分析(一)
2010-01-27 17:45
Android的Radio Interface Layer (RIL)提供了電話服務和的radio硬件之間的抽象層。
Radio Interface Layer RIL(Radio Interface Layer)負責數據的可靠傳輸、AT命令的發送以及response的解析。應用處理器通過AT命令集與帶GPRS功能的無線通訊模塊通信。
AT command由Hayes公司發明,是一個調制解調器製造商採用的一個調制解調器命令語言,每條命令以字母"AT"開頭。

JAVA Framework
代碼的路徑爲:
frameworks/base/telephony/java/android/telephony
android.telephony以及android.telephony.gsm

Core native:
在hardware/ril目錄中,提供了對RIL支持的本地代碼,包括4個文件夾:
hardware/ril/include
hardware/ril/libril
hardware/ril/reference-ril
hardware/ril/rild

kernel Driver
在Linux內核的驅動中,提供了相關的驅動程序的支持,可以建立在UART或者SDIO,USB等高速的串行總線上。
hardware/ril/include/telephony/目錄中的ril.h文件是ril部分的基礎頭文件。
其中定義的結構體RIL_RadioFunctions如下所示:
typedef struct {
int version;
RIL_RequestFunc onRequest;
RIL_RadioStateRequest onStateRequest;
RIL_Supports supports;
RIL_Cancel onCancel;
RIL_GetVersion getVersion;
} RIL_RadioFunctions;
RIL_RadioFunctions中包含了幾個函數指針的結構體,這實際上是一個移植層的接口,下層的庫實現後,由rild守護進程得到這些函數指針,執行對應的函數。
幾個函數指針的原型爲:
typedef void (*RIL_RequestFunc) (int request, void *data,
size_t datalen, RIL_Token t);
typedef RIL_RadioState (*RIL_RadioStateRequest)();
typedef int (*RIL_Supports)(int requestCode);
typedef void (*RIL_Cancel)(RIL_Token t);
typedef const char * (*RIL_GetVersion) (void);
其中最爲重要的函數是onRequest(),它是一個請求執行的函數。

Android Phone分析(二)
2010-01-27 17:47
Android的RIL驅動模塊, 在hardware/ril目錄下,一共分rild,libril.so以及librefrence_ril.so三個部分,另有一 radiooptions可供自動或手動調試使用。都依賴於include目錄中ril.h頭文件。目前cupcake分支上帶的是gsm的支持,另有一 cdma分支,這裏分析的是gsm驅動。
GSM模塊,由於Modem的歷史原因,AP一直是通過基於串口的AT命令與BB交互。包括到了目前的一些edge或3g模塊,或像omap這類ap,bp集成的芯片, 已經使用了USB或其他等高速總線通信,但大多仍然使用模擬串口機制來使用AT命令。這裏的RIL(Radio Interface Layer)層,主要也就是基於AT命令的操作,如發命令,response解析等。(gprs等傳輸會用到的MUX協議等在這裏並沒有包含,也暫不作介 紹。)
  以下是詳細分析,本文主要涉及基本架構和初始化的內容:
  首先介紹一下rild與libril.so以及librefrence_ril.so的關係:
  1. rild:
  僅實現一main函數作爲整個ril層的入口點,負責完成初始化。
  2. libril.so:
   與rild結合相當緊密,是其共享庫,編譯時就已經建立了這一關係。組成部分爲ril.cpp,ril_event.cpp。libril.so駐留在 rild這一守護進程中,主要完成同上層通信的工作,接受ril請求並傳遞給librefrence_ril.so, 同時把來自librefrence_ril.so的反饋回傳給調用進程。
  3. librefrence_ril.so:
   rild通過手動的dlopen方式加載,結合稍微鬆散,這也是因爲librefrence.so主要負責跟Modem硬件通信的緣故。這樣做更方便替 換或修改以適配更多的Modem種類。它轉換來自libril.so的請求爲AT命令,同時監控Modem的反饋信息,並傳遞迴libril.so。在初 始化時, rild通過符號RIL_Init獲取一組函數指針並以此與之建立聯繫。
4. radiooptions:
  radiooptiongs通過獲取啓動參數, 利用socket與rild通信,可供調試時配置Modem參數。

Android Phone分析(三)
2010-01-27 17:51
分析初始化流程,主入口是rild.c中的main函數,主要完成三個任務:
  1. 開啓libril.so中的event機制, 在RIL_startEventLoop中,是最核心的由多路I/O驅動的消息循環。
  2. 初始化librefrence_ril.so,也就是跟硬件或模擬硬件modem通信的部分(後面統一稱硬件), 通過RIL_Init函數完成。
  3. 通過RIL_Init獲取一組函數指針RIL_RadioFunctions, 並通過RIL_register完成註冊,並打開接受上層命令的socket通道。
   首先看第一個任務,也就是RIL_startEventLoop函數。RIL_startEventLoop在ril.cpp中實現, 它的主要目的是通過pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL)建立一個dispatch線程,入口點在eventLoop. 而eventLoop中,會調ril_event.cpp中的ril_event_loop()函數,建立起消息(event)隊列機制。
  我們來仔細看看這一消息隊列的機制,這些代碼都在ril_event.cpp中。
void ril_event_init();
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param);
void ril_event_add(struct ril_event * ev);
void ril_timer_add(struct ril_event * ev, struct timeval * tv);
void ril_event_del(struct ril_event * ev);
void ril_event_loop();
struct ril_event {
struct ril_event *next;
struct ril_event *prev;
int fd;
int index;
bool persist;
struct timeval timeout;
ril_event_cb func;
void *param;
};
  每個ril_event結構,與一個fd句柄綁定(可以是文件,socket,管道等),並且帶一個func指針去執行指定的操作。
   具體流程是: ril_event_init完成後,通過ril_event_set來配置一新ril_event,並通過ril_event_add加入隊列之中(實 際通常用rilEventAddWakeup來添加),add會把隊列裏所有ril_event的fd,放入一個fd集合readFds中。這樣 ril_event_loop能通過一個多路複用I/O的機制(select)來等待這些fd, 如果任何一個fd有數據寫入,則進入分析流程processTimeouts(),processReadReadies(&rfds, n),firePending()。 後文會詳細分析這些流程。
  另外我們可以看到, 在進入ril_event_loop之前, 已經掛入了一s_wakeupfd_event, 通過pipe的機制實現的, 這個event的目的是可以在一些情況下,能內部喚醒ril_event_loop的多路複用阻塞,比如一些帶timeout的命令timeout到期的 時候。
  至此第一個任務分析完畢,這樣便建立起了基於event隊列的消息循環,稍後便可以接受上層發來的的請求了(上層請求的event對象建立,在第三個任務中)。
  接下來看第二個任務,這個任務的入口是RIL_Init, RIL_Init首先通過參數獲取硬件接口的設備文件或模擬硬件接口的socket. 接下來便新開一個線程繼續初始化, 即mainLoop。
   mainLoop的主要任務是建立起與硬件的通信,然後通過read方法阻塞等待硬件的主動上報或響應。在註冊一些基礎回調 (timeout,readerclose)後,mainLoop首先打開硬件設備文件,建立起與硬件的通信,s_device_path和s_port 是前面獲取的設備路徑參數,將其打開(兩者可以同時打開並擁有各自的reader,這裏也很容易添加雙卡雙待等支持)。
  接下來通過 at_open函數建立起這一設備文件上的reader等待循環,這也是通過新建一個線程完成, ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr),入口點readerLoop。
   AT命令都是以rn或nr的換行符來作爲分隔符的,所以readerLoop是line驅動的,除非出錯,超時等,否則會讀到一行完整的響應或主動上 報,纔會返回。這個循環跑起來以後,我們基本的AT響應機制已經建立了起來。它的具體分析,包括at_open中掛接的ATUnsolHandler, 我們都放到後面分析response的連載文章裏去。
  有了響應的機制(當然,能與硬件通信也已經可以發請求了),通過 RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0),跑到initializeCallback中,執行一些Modem的初始化命令,主要都是AT命令的方式。發AT命令的 流程,我們放到後面分析request的連載文章裏。這裏可以看到,主要是一些參數配置,以及網絡狀態的檢查等。至此第二個任務分析完畢,硬件已經可以訪 問了。
  最後是第三個任務。第三個任務是由RIL_Init的返回值開始的,這是一個RIL_RadioFunctions結構的指針。
typedef struct {
int version;        /* set to RIL_VERSION */
RIL_RequestFunc onRequest;
RIL_RadioStateRequest onStateRequest;
RIL_Supports supports;
RIL_Cancel onCancel;
RIL_GetVersion getVersion;
} RIL_RadioFunctions;
  其中最重要的是onRequest域,上層來的請求都由這個函數進行映射後轉換成對應的AT命令發給硬件。
  rild通過RIL_register註冊這一指針。
  RIL_register中要完成的另外一個任務,就是打開前面提到的跟上層通信的socket接口(s_fdListen是主接口,s_fdDebug供調試時使用)。
  然後將這兩個socket接口使用任務一中實現的機制進行註冊(僅列出s_fdListen)
ril_event_set (&s_listen_event, s_fdListen, false,
listenCallback, NULL);
rilEventAddWakeup (&s_listen_event);
  這樣將兩個socket加到任務一中建立起來多路複用I/O的檢查句柄集合中,一旦有上層來的(調試)請求,event機制便能響應處理了。到這裏啓動流程已經分析完畢。
Android Phone分析(四)
2010-01-27 17:52
request流程
1. 多路複用I/O機制的運轉
上文說到request是接收,是通過ril_event_loop中的多路複用I/O,也對初始化做了分析.現在我們來仔細看看這個機制如何運轉.
ril_event_set負責配置一個event,主要有兩種event:
ril_event_add添加使用多路I/O的event,它負責將其掛到隊列,同時將event的通道句柄fd加入到watch_table,然後通過select等待.
ril_timer_add添加timer event,它將其掛在隊列,同時重新計算最短超時時間.
無論哪種add,最後都會調用triggerEvLoop來刷新隊列,更新超時值或等待對象.

刷新之後, ril_event_loop從阻塞的位置,select返回,只有兩種可能,一是超時,二是等待到了某I/O操作.
超時的處理在processTimeouts中,摘下超時的event,加入pending_list.
檢查有I/O操作的通道的處理在processReadReadies中,將超時的event加入pending_list.
最後在firePending中,檢索pending_list的event並依次執行event->func.
這些操作完之後,計算新超時時間,並重新select阻塞於多路I/O.

前面的初始化流程已分析得知,初始化完成以後,隊列上掛了3個event對象,分別是:
s_listen_event: 名爲rild的socket,主要requeset & response通道
s_debug_event: 名爲rild-debug的socket,調試用requeset & response通道(流程與s_listen_event基本相同,後面僅分析s_listen_event)
s_wakeupfd_event: 無名管道,用於隊列主動喚醒(前面提到的隊列刷新,就用它來實現,請參考使用它的相關地方)

2. request的傳入和dispatch
明白了event隊列的基本運行流程,我們可以來看看request是怎麼傳入和dispatch的了.
上 層的部分,核心代碼在frameworks/base/telephony/java/com/android/internal/telephony /gsm/RIL.java,這是android java框架處理radio(gsm)的核心組件.本文因爲主要關注rild,也就是驅動部分,所以這裏只作簡單介紹.
我們看一個具體的例子,RIL.java中的dial函數:
public void
dial (String address, int clirMode, Message result)
{
RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

rr.mp.writeString(address);
rr.mp.writeInt(clirMode);

if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

send(rr);
}
rr是以RIL_REQUEST_DIAL爲request號而申請的一個RILRequest對象.這個request號在java框架和rild庫中共享(參考RILConstants.java中這些值的由來:))
RILRequest初始化的時候,會連接名爲rild的socket(也就是rild中s_listen_event綁定的socket),初始化數據傳輸的通道.
rr.mp 是Parcel對象,Parcel是一套簡單的序列化協議,用於將對象(或對象的成員)序列化成字節流,以供傳遞參數之用.這裏可以看到String address和int clirMode都是將依次序列化的成員.在這之前,rr初始化的時候,request號跟request的序列號(自動生成的遞增數),已經成爲頭兩個 將被序列化的成員.這爲後面的request解析打下了基礎.
接下來是send到handleMessage的流程,send將rr直接傳遞給另 一個線程的handleMessage,handleMessage執行data = rr.mp.marshall()執行序列化操作, 並將data字節流寫入到rild socket.

接下來回到我們的rild,select發現rild socket有了請求鏈接的信號,導致s_listen_event被掛入pending_list,執行event->func,即
static void listenCallback (int fd, short flags, void *param);
接下來,s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen),獲取傳入的socket描述符,也就是上層的java RIL傳入的連接.
然 後,通過record_stream_new建立起一個record_stream, 將其與s_fdCommand綁定, 這裏我們不關注record_stream 的具體流程, 我們來關注command event的回調, processCommandsCallback函數, 從前面的event機制分析, 一旦s_fdCommand上有數據, 此回調函數就會被調用. (略過onNewCommandConnect的分析)
processCommandsCallback通過 record_stream_get_next阻塞讀取s_fdCommand上發來的 數據, 直到收到一完整的request(request包的完整性由record_stream的機制保證), 然後將其送達processCommandBuffer.
進入processCommandBuffer以後,我們就正式進入了命令的解析部分. 每個命令將以RequestInfo的形式存在.
typedef struct RequestInfo {
int32_t token; //this is not RIL_Token
CommandInfo *pCI;
struct RequestInfo *p_next;
char cancelled;
char local; // responses to local commands do not go back to command process
} RequestInfo;
這 裏的pRI就是一個RequestInfo結構指針, 從socket過來的數據流, 前面提到是Parcel處理過的序列化字節流, 這裏會通過反序列化的方法提取出來. 最前面的是request號, 以及token域(request的遞增序列號). 我們更關注這個request號, 前面提到, 上層和rild之間, 這個號是統一的. 它的定義是一個包含ril_commands.h的枚舉, 在ril.cpp中
static CommandInfo s_commands[] = {
#include "ril_commands.h"
};
pRI直接訪問這個數組, 來獲取自己的pCI.
這是一個CommandInfo結構:
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
基本解析到這裏就完成了, 接下來, pRI被掛入pending的request隊列, 執行具體的pCI->dispatchFunction, 進行詳細解析.

3. request的詳細解析
對dial而言, CommandInfo結構是這樣初始化的:
{RIL_REQUEST_DIAL, dispatchDial, responseVoid},
這 裏執行dispatchFunction, 也就是dispatchDial這一函數.我們可以看到其實有很多種類的dispatch function, 比如dispatchVoid, dispatchStrings, dispatchSIM_IO等等, 這些函數的區別, 在於Parcel傳入的參數形式,Void就是不帶參數的,Strings是以string[]做參數,又如Dial等,有自己的參數解析方式,以此類 推.
request號和參數現在都有了,那麼可以進行具體的request函數調用了.
s_callbacks.onRequest(pRI->pCI->requestNumber, xxx, len, pRI)完成這一操作.
s_callbacks 是上篇文章中提到的獲取自libreference-ril的RIL_RadioFunctions結構指針,request請求在這裏轉入底層的 libreference-ril處理,handler是reference-ril.c中的onRequest.
onRequest進行一個簡單的switch分發,我們依然來看RIL_REQUEST_DIAL
流程是 onRequest-->requestDial-->at_send_command-->at_send_command_full-->at_send_command_full_nolock-->writeline
requestDial中將命令和參數轉換成對應的AT命令,調用公共send command接口at_send_command.
除 了這個接口之外,還有 at_send_command_singleline,at_send_command_sms,at_send_command_multiline 等,這是根據at返回值,以及發命令流程的類型來區別的.比如at+csq這類,需要at_send_command_singleline,而發送短 信,因爲有prompt提示符">",傳裸數據,結束符等一系列操作,需要專門用at_send_command_sms來實現.
然後執行at_send_command_full,前面幾個接口都會最終到這裏,再通過一個互斥的at_send_command_full_nolock調用,然後完成最終的寫出操作,在writeline中,寫出到初始化時打開的設備中.
writeline返回之後,還有一些操作,如保存type等信息,供response回來時候使用,以及一些超時處理. 不再詳述.

到這裏,request的詳細流程,就分析完畢了.

Android Phone分析(五)
2010-01-27 17:53
response流程
前文對request的分析, 終止在了at_send_command_full_nolock裏的writeline操作,因爲這裏完成命令寫出到硬件設備的操作,接下來就是等待硬件響應,也就是response的過程了。我們的分析也是從這裏開始。
response信息的獲取,是在第一篇初始化分析中,提到的readerLoop中。由readline函數以‘行'爲單位接收上來。
AT的response有兩種,一是主動上報的,比如網絡狀態,短信,來電等都不需要經過請求,有一unsolicited詞語專門描述。另一種纔是真正意義上的response,也就是命令的響應。
這 裏我們可以看到,所有的行,首先經過sms的自動上報篩選,因爲短信的AT處理通常比較麻煩,無論收發都單獨列出。這裏是因爲要即時處理這條短信消息(兩 行,標誌+pdu),而不能拆開處理。處理函數爲onUnsolicited(由s_unsolHandler指向),我們等下介紹。
除開sms的特例,所有的line都要經過processLine,我們來看看這個流程:
processLine
|----no cmd--->handleUnsolicited //主動上報
|----isFinalResponseSuccess--->handleFinalResponse //成功,標準響應
|----isFinalResponseError--->handleFinalResponse //失敗,標準響應
|----get '>'--->send sms pdu //收到>符號,發送sms數據再繼續等待響應
|----switch s_type--->具體響應 //命令有具體的響應信息需要對應分析

我 們這裏主要關注handleUnsolicited自動上報(會調用到前面smsUnsolicite也調用的onUnsolicite),以及 switch s_type具體響應信息,另外具體響應需要handleFinalResponse這樣的標準響應來最終完成。
1. onUnsolicite(主動上報響應)
static void onUnsolicited (const char *s, const char *sms_pdu);
短信的AT設計真是麻煩的主,以致這個函數的第二個參數完全就是爲它準備的。
response 的主要的解析過程,由at_tok.c中的函數完成,其實就是字符串按塊解析,具體的解析方式由每條命令或上報信息自行決定。這裏不再詳 述,onUnsolicited只解析出頭部(一般是+XXXX的形式),然後按類型決定下一步操作,操作爲 RIL_onUnsolicitedResponse和RIL_requestTimedCallback兩種。
a)RIL_onUnsolicitedResponse:
將 unsolicited的信息直接返回給上層。通過Parcel傳遞,將 RESPONSE_UNSOLICITED,unsolResponse(request號)寫入Parcel先,然後通過 s_unsolResponses數組,查找到對應的responseFunction完成進一步的的解析,存入Parcel中。最終通過 sendResponse將其傳遞迴原進程。流程:
sendResponse-->sendResponseRaw-->blockingWrite-->write to s_fdCommand(前面建立起來的和上層框架的socket連接)
這些步驟之後有一些喚醒系統等其他操作。不再詳述。
b)RIL_requestTimedCallback:
通 過event機制(參考文章二)實現的timer機制,回調對應的內部處理函數。通過internalRequestTimedCallback將回調添 加到event循環,最終完成callback上掛的函數的回調。比如pollSIMState,onPDPContextListChanged等回 調, 不用返回上層, 內部處理就可以。

2. switch s_type(命令的具體響應)及handleFinalResponse(標準響應)
命 令的類型(s_type)在send command的時候設置(參考文章二),有NO_RESULT,NUMERIC,SINGLELINE,MULTILINE幾種,供不同的AT使用。比 如AT+CSQ是singleline, 返回at+csq=xx,xx,再加一行OK,比如一些設置命令,就是no_result, 只有一行OK或ERROR。
這幾個類型的解析都很相仿,通過一定的判斷(比較AT頭標記等),如果是對應的響應,就通過 addIntermediate掛到一個臨時結果sp_response->p_intermediates隊列裏。如果不是對應響應,那它其實應 該是穿插其中的自動上報,用onUnsolicite來處理。
具體響應,只起一個獲取響應信息到臨時結果,等待具體分析的作用。無論有無具體響應,最終都得以標準響應handleFinalResponse來完成,也就是接受到OK,ERROR等標準response來結束,這是大多數AT命令的規範。
handleFinalResponse 會設置s_commandcond這一object,也就是at_send_command_full_nolock等待的對象。到這裏,響應的完整信息 已經完全獲得,send command可以進一步處理返回的信息了(臨時結果,以及標準返回的成功或失敗,都在sp_response中)。
pp_outResponse參數將sp_response返回給調用at_send_command_full_nolock的函數。
繼續我們在文章二的分析的話,這個函數其實是requestDial,不過requestDial忽略了響應,所以我們另外看個例子,如requestSignalStrength,命令其實就是前面提到的at+csq:
可以看到確實是通過at_send_command_singleline來進行的操作,response在p_response中。
p_response如果返回失敗(也就是標準響應的ERROR等造成),則通過RIL_onRequestComplete發送返回數據給上層,結束命令。
如果成功,則進一步分析p_response->p_intermediates, 同樣是通過at_tok.c裏的函數進行分析。並同樣將結果通過RIL_onRequestComplete返回。
RIL_onRequestComplete:
RIL_onRequestComplete和RIL_onUnsolicitedResponse很相仿,功能也一致。
通 過Parcel來傳遞迴上層,同樣是先寫入RESPONSE_SOLICITED(區別於 RESPONSE_UNSOLICITED),pRI->token(上層傳下的request號),錯誤碼(send command的錯誤,不是AT響應)。如果有AT響應,通過訪問pRI->pCI->responseFunction來完成具體 response的解析,並寫入Parcel。
然後通過同樣的途徑:
sendResponse-->sendResponseRaw-->blockingWrite-->write to s_fdCommand
完成最終的響應傳遞。

到這裏,我們分析了自動上報與命令響應,其實response部分,也就告一段落了。


Android Parcel理解
2010-01-30 12:44
android 中Parcel 的使用,他是一個存儲基本數據類型和引用數據類型的容器,在andorid 中通過IBinder來綁定數據在進程間傳遞數據。
Parcel parcel = Parcel.obtain();// 獲取一個Parcel 對象
下面就可以對其進行方法進行操作了,createXXX(),wirteXXX(),readXXX(),
其中dataPosition(),返回當前Parcel 當前對象存儲數據的偏移量,而setDataPosition(),設置當前Parcel 對象的偏移量,方便讀取parcel 中的數據,可問題就出在我讀取出來的數據要麼是空(null),要麼永遠是第一個偏移量處的值,存儲和讀取數據的。Parcel採用什麼機制實現的,是以什麼形式的存儲的,然後我才能任意對其操作,讀取目標數據。
基本數據類型的取值範圍,
boolean 1bit
short 16bit
int 32bit
long 64bit
float 32bit
double 64bit
char 16bit
byte 8bit
由此我可以猜想,Parcel 32bit 作爲基本單位存儲寫入的變量,4byte*8=32bit,在內存中的引用地址變量是採用16進制進行編碼,且作爲偏移量,即偏移量是4的倍數,0,4,8,12,16,20,24,28,32,36,40,44,48......4*N,
f(x) = 4*y{y>=0&y是自然數}
我想絕對不會出現向偏移量是3,6,9這樣的數據。。。
由此我們可以推斷出,無論他存儲的是基本數據類型或引用數據類型的變量,都是以32bit基本單位作爲偏移量,
parcel.writeInt(1);
parcel.writeInt(2);
parcel.writeInt(3);
parcel.writeInt(4);
parcel.writeInt(5);
parcel.writeInt(6);
parcel.writeInt(7);
parcel.writeInt(81011111);
parcel.writeFloat(1f);
parcel.writeFloat(1000000000000000000000000000000000000f);

parcel.writeXXX(),每寫一次數據,在32bit的空間裏能夠存儲要放入的變量,怎只佔一個偏移量,也就之一動4個位置,而當存儲的數據如 parcel.writeFloat(1000000000000000000000000000000000000f);他就自動往後移動,
parcel.writeString("a");
parcel.writeString("b");
parcel.writeString("d");
parcel.writeString("c");

parcel.writeString("abcd"); 的區別。有此可見,他的內存的分配原來是這樣的。
那我怎樣才能把我存進去的書據依次的去出來呢?setDataPosition(),設置parcel 的偏移量,在readXXX(),讀取數據
int size = parcel.dataSize();
int i = 0;
while (i <= size ) {
parcel.setDataPosition(i);
int curr_int = parcel.readInt();
i+=4;
int j = 0;
j++;
}
由此可見parcel 寫入數據是按照32bit 爲基本的容器,依次存儲寫入的數據,基本和引用(其實引用的也是有多個基本數據類型組合而成OBJECTS-屬性|方法),讀取的時候我們就可以按照這種規律根據目標數據的偏移量的位置(curr_position),以及偏移量的大小(size),,取出已經存進去的數據了
int i = curr_position;
while (i <= size ) {
parcel.setDataPosition(i);
int curr_int = parcel.readXXXt();
i+=4;
int j = 0;
j++;
}
這樣就ok 了
他的createXXX()方法現在沒用,用了在說吧!
總結一句話,java 中 基本數據類型的取值範圍,引用類型的數據,相當於c中的指針,以及各進制之間的相互轉換和靈活的引用,以及定製自己想要的任意進制數據類型。

Android中Message機制的靈活應用(一)
2010-02-02 11:10
轉載請註明來自easyandroid論壇
活用Android線程間通信的Message機制
1.1.Message
代碼在frameworks/base/core/java/android/Os/Message.java中。
Message.obtain函數:有多個obtain函數,主要功能一樣,只是參數不一樣。作用是從Message Pool中取出一個Message,如果Message Pool中已經沒有Message可取則新建一個Message返回,同時用對應的參數給得到的Message對象賦值。
Message Pool:大小爲10個;通過Message.mPool->(Message並且Message.next)-> (Message並且Message.next)-> (Message並且Message.next)...構造一個Message Pool。Message Pool的第一個元素直接new出來,然後把Message.mPool(static類的static變量)指向它。其他的元素都是使用完的Message通過Message的recycle函數清理後放到Message Pool(通過Message Pool最後一個Message的next指向需要回收的Message的方式實現)。下圖爲Message Pool的結構:

1.2.MessageQueue
MessageQueue裏面有一個收到的Message的對列:
MessageQueue.mMessages(static變量)->( Message並且Message.next)-> ( Message並且Message.next)->...,下圖爲接收消息的消息隊列:

上層代碼通過Handler的sendMessage等函數放入一個message到MessageQueue裏面時最終會調用MessageQueue的enqueueMessage函數。enqueueMessage根據上面的接收的Message的隊列的構造把接收到的Message放入隊列中。
MessageQueue的removeMessages函數根據上面的接收的Message的隊列的構造把接收到的Message從隊列中刪除,並且調用對應Message對象的recycle函數把不用的Message放入Message Pool中。
1.3.Looper
Looper對象的創建是通過prepare函數,而且每一個Looper對象會和一個線程關聯
public static final void prepare() {

if (sThreadLocal.get() != null) {

throw new RuntimeException("Only one Looper may be created per thread");

}

sThreadLocal.set(new Looper());
}
Looper對象創建時會創建一個MessageQueue,主線程默認會創建一個Looper從而有MessageQueue,其他線程默認是沒有MessageQueue的不能接收Message,如果需要接收Message則需要通過prepare函數創建一個MessageQueue。具體操作請見示例代碼。
private Looper() {

mQueue = new MessageQueue();

mRun = true;

mThread = Thread.currentThread();
}
prepareMainLooper函數只給主線程調用(系統處理,程序員不用處理),它會調用prepare建立Looper對象和MessageQueue。
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {

myLooper().mQueue.mQuitAllowed = false;
}
}
Loop函數從MessageQueue中從前往後取出Message,然後通過Handler的dispatchMessage函數進行消息的處理(可見消息的處理是Handler負責的),消息處理完了以後通過Message對象的recycle函數放到Message Pool中,以便下次使用,通過Pool的處理提供了一定的內存管理從而加速消息對象的獲取。至於需要定時處理的消息如何做到定時處理,請見MessageQueue的next函數,它在取Message來進行處理時通過判斷MessageQueue裏面的Message是否符合時間要求來決定是否需要把Message取出來做處理,通過這種方式做到消息的定時處理。
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
//
break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what

);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to
" + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
1.4.Handler
Handler的構造函數表示Handler會有成員變量指向Looper和MessageQueue,後面我們會看到沒什麼需要這些引用;至於callback是實現了Callback接口的對象,後面會看到這個對象的作用。
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
public interface Callback {

public boolean handleMessage(Message msg);
}
獲取消息:直接通過Message的obtain方法獲取一個Message對象。
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
發送消息:通過MessageQueue的enqueueMessage把Message對象放到MessageQueue的接收消息隊列中
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
線程如何處理MessageQueue中接收的消息:在Looper的loop函數中循環取出MessageQueue的接收消息隊列中的消息,然後調用Hander的dispatchMessage函數對消息進行處理,至於如何處理(相應消息)則由用戶指定(三個方法,優先級從高到低:Message裏面的Callback,一個實現了Runnable接口的對象,其中run函數做處理工作;Handler裏面的mCallback指向的一個實現了Callback接口的對象,裏面的handleMessage進行處理;處理消息Handler對象對應的類繼承並實現了其中handleMessage函數,通過這個實現的handleMessage函數處理消息)。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Runnable說明:Runnable只是一個接口,實現了這個接口的類對應的對象也只是個普通的對象,並不是一個Java中的Thread。Thread類經常使用Runnable,很多人有誤解,所以這裏澄清一下。
從上可知以下關係圖:

其中清理Message是Looper裏面的loop函數指把處理過的Message放到Message的Pool裏面去,如果裏面已經超過最大值10個,則丟棄這個Message對象。
調用Handler是指Looper裏面的loop函數從MessageQueue的接收消息隊列裏面取出消息,然後根據消息指向的Handler對象調用其對應的處理方法。

Android中Message機制的靈活應用(二)
2010-02-02 11:44
1.5.代碼示例
下面我們會以android實例來展示對應的功能,程序界面於下:


程序代碼如下,後面部分有代碼說明:

說明(代碼詳細解釋請見後文):
1.
2. package com.android.messageexample;
3. import android.app.Activity;
4. import android.content.Context;
5. import android.graphics.Color;
6. import android.os.Bundle;
7. import android.os.Handler;
8. import android.os.Looper;
9. import android.os.Message;
10. import android.util.Log;
11. import android.view.View;
12. import android.view.View.OnClickListener;
13. import android.widget.Button;
14. import android.widget.LinearLayout;
15. import android.widget.TextView;
16. public class MessageExample extends Activity implements OnClickListener {
17. private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
18. private final int FP = LinearLayout.LayoutParams.FILL_PARENT;
19. public TextView tv;
20. private EventHandler mHandler;
21. private Handler mOtherThreadHandler=null;
22. private Button btn, btn2, btn3, btn4, btn5, btn6;
23. private NoLooperThread noLooerThread = null;
24. private OwnLooperThread ownLooperThread = null;
25. private ReceiveMessageThread receiveMessageThread =null;
26. private Context context = null;
27. private final String sTag = "MessageExample";
28. private boolean postRunnable = false;
29.
30. /** Called when the activity is first created. */
31. @Override
32. public void onCreate(Bundle savedInstanceState) {
33. super.onCreate(savedInstanceState);
34. context = this.getApplicationContext();
35. LinearLayout layout = new LinearLayout(this);
36. layout.setOrientation(LinearLayout.VERTICAL);
37. btn = new Button(this);
38. btn.setId(101);
39. btn.setText("message from main thread self");
40. btn.setOnClickListener(this);
41. LinearLayout.LayoutParams param =
42. new LinearLayout.LayoutParams(250,50);
43. param.topMargin = 10;
44. layout.addView(btn, param);
45. btn2 = new Button(this);
46. btn2.setId(102);
47. btn2.setText("message from other thread to main thread");
48. btn2.setOnClickListener(this);
49. layout.addView(btn2, param);
50. btn3 = new Button(this);
51. btn3.setId(103);
52. btn3.setText("message to other thread from itself");
53. btn3.setOnClickListener(this);
54. layout.addView(btn3, param);
55. btn4 = new Button(this);
56. btn4.setId(104);
57. btn4.setText("message with Runnable as callback from other thread to main thread");
58. btn4.setOnClickListener(this);
59. layout.addView(btn4, param);
60. btn5 = new Button(this);
61. btn5.setId(105);
62. btn5.setText("main thread's message to other thread");
63. btn5.setOnClickListener(this);
64. layout.addView(btn5, param);
65. btn6 = new Button(this);
66. btn6.setId(106);
67. btn6.setText("exit");
68. btn6.setOnClickListener(this);
69. layout.addView(btn6, param);
70. tv = new TextView(this);
71. tv.setTextColor(Color.WHITE);
72. tv.setText("");
73. LinearLayout.LayoutParams param2 =
74. new LinearLayout.LayoutParams(FP, WC);
75. param2.topMargin = 10;
76. layout.addView(tv, param2);
77. setContentView(layout);
78.
79. //主線程要發送消息給other thread, 這裏創建那個other thread
80. receiveMessageThread = new ReceiveMessageThread();
81. receiveMessageThread.start();
82. }
83.
84. //implement the OnClickListener interface
85. @Override
86. public void onClick(View v) {
87. switch(v.getId()){
88. case 101:
89. //主線程發送消息給自己
90. Looper looper;
91. looper = Looper.myLooper(); //get the Main looper related with the main thread
92. //如果不給任何參數的話會用當前線程對應的Looper(這裏就是Main Looper)爲Handler裏面的成員mLooper賦值
93. mHandler = new EventHandler(looper);
94. //mHandler = new EventHandler();
95. // 清除整個MessageQueue裏的消息
96. mHandler.removeMessages(0);
97. String obj = "This main thread's message and received by itself!";
98. //得到Message對象
99. Message m = mHandler.obtainMessage(1, 1, 1, obj);
100. // 將Message對象送入到main thread的MessageQueue裏面
101. mHandler.sendMessage(m);
102. break;
103. case 102:
104. //other線程發送消息給主線程
105. postRunnable = false;
106. noLooerThread = new NoLooperThread();
107. noLooerThread.start();
108. break;
109. case 103:
110. //other thread獲取它自己發送的消息
111. tv.setText("please look at the error level log for other thread received message");
112. ownLooperThread = new OwnLooperThread();
113. ownLooperThread.start();
114. break;
115. case 104:
116. //other thread通過Post Runnable方式發送消息給主線程
117. postRunnable = true;
118. noLooerThread = new NoLooperThread();
119. noLooerThread.start();
120. break;
121. case 105:
122. //主線程發送消息給other thread
123. if(null!=mOtherThreadHandler){
124. tv.setText("please look at the error level log for other thread received message from main thread");
125. String msgObj = "message from mainThread";
126. Message mainThreadMsg = mOtherThreadHandler.obtainMessage(1, 1, 1, msgObj);
127. mOtherThreadHandler.sendMessage(mainThreadMsg);
128. }
129. break;
130. case 106:
131. finish();
132. break;
133. }
134. }
135. class EventHandler extends Handler
136. {
137. public EventHandler(Looper looper) {
138. super(looper);
139. }
140. public EventHandler() {
141. super();
142. }
143. public void handleMessage(Message msg) {
144. //可以根據msg.what執行不同的處理,這裏沒有這麼做
145. switch(msg.what){
146. case 1:
147. tv.setText((String)msg.obj);
148. break;
149. case 2:
150. tv.setText((String)msg.obj);
151. noLooerThread.stop();
152. break;
153. case 3:
154. //不能在非主線程的線程裏面更新UI,所以這裏通過Log打印收到的消息
155. Log.e(sTag, (String)msg.obj);
156. ownLooperThread.stop();
157. break;
158. default:
159. //不能在非主線程的線程裏面更新UI,所以這裏通過Log打印收到的消息
160. Log.e(sTag, (String)msg.obj);
161. break;
162. }
163. }
164. }
165. //NoLooperThread
166. class NoLooperThread extends Thread{
167. private EventHandler mNoLooperThreadHandler;
168. public void run() {
169. Looper myLooper, mainLooper;
170. myLooper = Looper.myLooper();
171. mainLooper = Looper.getMainLooper(); //這是一個static函數
172. String obj;
173. if(myLooper == null){
174. mNoLooperThreadHandler = new EventHandler(mainLooper);
175. obj = "NoLooperThread has no looper and handleMessage function executed in main thread!";
176. }
177. else {
178. mNoLooperThreadHandler = new EventHandler(myLooper);
179. obj = "This is from NoLooperThread self and handleMessage function executed in NoLooperThread!";
180. }
181. mNoLooperThreadHandler.removeMessages(0);
182. if(false == postRunnable){
183. //send message to main thread
184. Message m = mNoLooperThreadHandler.obtainMessage(2, 1, 1, obj);
185. mNoLooperThreadHandler.sendMessage(m);
186. Log.e(sTag, "NoLooperThread id:" + this.getId());
187. }else{
188. //下面new出來的實現了Runnable接口的對象中run函數是在Main Thread中執行,不是在NoLooperThread中執行
189. //注意Runnable是一個接口,它裏面的run函數被執行時不會再新建一個線程
190. //您可以在run上加斷點然後在eclipse調試中看它在哪個線程中執行
191. mNoLooperThreadHandler.post(new Runnable(){
192. @Override
193. public void run() {
194. tv.setText("update UI through handler post runnalbe mechanism!");
195. noLooerThread.stop();
196. }
197. });
198. }
199. }
200. }
201.
202. //OwnLooperThread has his own message queue by execute Looper.prepare();
203. class OwnLooperThread extends Thread{
204. private EventHandler mOwnLooperThreadHandler;
205. public void run() {
206. Looper.prepare();
207. Looper myLooper, mainLooper;
208. myLooper = Looper.myLooper();
209. mainLooper = Looper.getMainLooper(); //這是一個static函數
210. String obj;
211. if(myLooper == null){
212. mOwnLooperThreadHandler = new EventHandler(mainLooper);
213. obj = "OwnLooperThread has no looper and handleMessage function executed in main thread!";
214. }
215. else {
216. mOwnLooperThreadHandler = new EventHandler(myLooper);
217. obj = "This is from OwnLooperThread self and handleMessage function executed in NoLooperThread!";
218. }
219. mOwnLooperThreadHandler.removeMessages(0);
220. //給自己發送消息
221. Message m = mOwnLooperThreadHandler.obtainMessage(3, 1, 1, obj);
222. mOwnLooperThreadHandler.sendMessage(m);
223. Looper.loop();
224. }
225. }
226.
227. //ReceiveMessageThread has his own message queue by execute Looper.prepare();
228. class ReceiveMessageThread extends Thread{
229. public void run() {
230. Looper.prepare();
231. mOtherThreadHandler = new Handler(){
232. public void handleMessage(Message msg) {
233. Log.e(sTag, (String)msg.obj);
234. }
235. };
236. Looper.loop();
237. }
238. }
239.
240. }

使用Looper.myLooper靜態方法可以取得當前線程的Looper對象。
使用mHandler = new EevntHandler(Looper.myLooper()); 可建立用來處理當前線程的Handler對象;其中,EevntHandler是Handler的子類。
使用mHandler = new EevntHandler(Looper.getMainLooper()); 可建立用來處理main線程的Handler對象;其中,EevntHandler是Handler的子類。
1.5.1.主線程給自己發送消息示例
主線程發送消息:
在onClick的case 101中創建一個繼承自Handler的EventHandler對象,然後獲取一個消息,然後通過EventHandler對象調用sendMessage把消息發送到主線程的MessageQueue中。主線程由系統創建,系統會給它建立一個Looper對象和MessageQueue,所以可以接收消息。這裏只要根據主線程的Looper對象初始化EventHandler對象,就可以通過EventHandler對象發送消息到主線程的消息隊列中。
主線程處理消息:
這裏是通過EventHandler的handleMessage函數處理的,其中收到的Message對象中what值爲一的消息就是發送給它的,然後把消息裏面附帶的字符串在TextView上顯示出來。
1.5.2.其他線程給主線程發送消息示例
其他線程發送消息(這裏是說不使用Runnable作爲callback的消息):
首先postRunnable設爲false,表示不通過Runnable方式進行消息相關的操作。然後啓動線程noLooerThread, 然後以主線程的Looper對象爲參數建立EventHandler的對象mNoLooperThreadHandler,然後獲取一個Message並把一個字符串賦值給它的一個成員obj,然後通過mNoLooperThreadHandler把消息發送到主線程的MessageQueue中。
主線程處理消息:
這裏是通過EventHandler的handleMessage函數處理的,其中收到的Message對象中what值爲二的消息就是上面發送給它的,然後把消息裏面附帶的字符串在TextView上顯示出來。
1.5.3.其他線程給自己發送消息示例
其他線程發送消息:
其他非主線程建立後沒有自己的Looper對象,所以也沒有MessageQueue,需要給非主線程發送消息時需要建立MessageQueue以便接收消息。下面說明如何給自己建立MessageQueue和Looper對象。從OwnLooperThread的run函數中可以看見有一個Looper.prepare()調用,這個就是用來建立非主線程的MessageQueue和Looper對象的。
所以這裏的發送消息過程是建立線程mOwnLooperThread,然後線程建立自己的Looper和MessageQueue對象,然後根據上面建立的Looper對象建立對應的EventHandler對象mOwnLooperThreadHandler,然後由mOwnLooperThreadHandler建立消息並且發送到自己的MessageQueue裏面。
其他線程處理接收的消息:
線程要接收消息需要在run函數中調用Looper.loop(),然後loop函數會從MessageQueue中取出消息交給對應的Handler對象mOwnLooperThreadHandler處理,在mOwnLooperThreadHandler的handleMessage函數中會把Message對象中what值爲三的消息(上面發送的消息)在Log中打印出來,可以通過Logcat工具查看log。
1.5.4.其他線程以Runnable爲消息參數給主線程發送消息示例
其他線程發送消息(這裏是說使用Runnable作爲callback的消息):
首先postRunnable設爲true,表示通過Runnable方式進行消息相關的操作。然後啓動線程noLooerThread, 然後以主線程的Looper對象爲參數建立EventHandler的對象mNoLooperThreadHandler,然後獲取一個Message並把一個字符串賦值給它的一個成員obj,然後通過mNoLooperThreadHandler把消息發送到主線程的MessageQueue中。
主線程處理消息:
主線程收到上面發送的Message後直接運行上面Runnable對象中的run函數進行相應的操作。run函數通過Log打印一個字符串,可以通過Logcat工具查看log。
1.5.5.主線程給其他線程發送消息示例
主線程發送消息:
這裏首先要求線程receiveMessageThread運行(在onCreate函數中完成),並且準備好自己的Looper和MessageQueue(這個通過ReceiveMessageThread中的run函數中的Looper.prepare()調用完成),然後根據建立的Looper對象初始化Handler對象mOtherThreadHandler。然後在onClick的case 105中由mOtherThreadHandler建立一個消息(消息中有一個字符串對象)並且發送到線程receiveMessageThread中的MessageQueue中。
其他線程處理接收的消息:
線程要接收消息需要在run函數中調用Looper.loop(),然後loop函數會從MessageQueue中取出消息交給對應的Handler對象mOtherThreadHandler處理,在mOtherThreadHandler的handleMessage函數中會把Message對象中的字符串對象在Log中打印出來,可以通過Logcat工具查看log。


詳解 Android 的 Activity 組件(轉)
2010-02-03 11:34
本文詳細介紹了 Android 應用編程中 Activity 的生命週期、通信方式和 Intent Filter 等內容,並提供了一些日常開發中經常用到的關於 Activity 的技巧和方法。通過本文,你可以進一步了接 Android 中 Activity 的運作方式。
詳解 Android 的 Activity 組件
Activity 的生命週期
和 J2ME 的 MIDlet 一樣,在 android 中,Activity 的生命週期交給系統統一管理。與 MIDlet 不同的是安裝在 android 中的所有的 Activity 都是平等的。
Activity 的狀態及狀態間的轉換
在 android 中,Activity 擁有四種基本狀態:
1. Active/Runing一個新 Activity 啓動入棧後,它在屏幕最前端,處於棧的最頂端,此時它處於可見並可和用戶交互的激活狀態。
2. Paused 當 Activity 被另一個透明或者 Dialog 樣式的 Activity 覆蓋時的狀態。此時它依然與窗口管理器保持連接,系統繼續維護其內部狀態,所以它仍然可見,但它已經失去了焦點故不可與用戶交互。
3. Stoped 當 Activity 被另外一個 Activity 覆蓋、失去焦點並不可見時處於 Stoped狀態。
4. Killed Activity 被系統殺死回收或者沒有被啓動時處於 Killed狀態。
當一個 Activity 實例被創建、銷燬或者啓動另外一個 Activity 時,它在這四種狀態之間進行轉換,這種轉換的發生依賴於用戶程序的動作。下圖說明了 Activity 在不同狀態間轉換的時機和條件:

圖 1. Activity 的狀態轉換

如上所示,Android 程序員可以決定一個 Activity 的"生",但不能決定它的"死",也就時說程序員可以啓動一個 Activity,但是卻不能手動的"結束"一個 Activity。當你調用 Activity.finish()方法時,結果和用戶按下 BACK 鍵一樣:告訴 Activity Manager 該 Activity 實例完成了相應的工作,可以被"回收"。隨後 Activity Manager 激活處於棧第二層的 Activity 並重新入棧,同時原 Activity 被壓入到棧的第二層,從 Active 狀態轉到 Paused 狀態。例如:從 Activity1 中啓動了 Activity2,則當前處於棧頂端的是 Activity2,第二層是 Activity1,當我們調用 Activity2.finish()方法時,Activity Manager 重新激活 Activity1 併入棧,Activity2 從 Active 狀態轉換 Stoped 狀態,Activity1. onActivityResult(int requestCode, int resultCode, Intent data)方法被執行,Activity2 返回的數據通過 data參數返回給 Activity1。
Activity 棧
Android 是通過一種 Activity 棧的方式來管理 Activity 的,一個 Activity 的實例的狀態決定它在棧中的位置。處於前臺的 Activity 總是在棧的頂端,當前臺的 Activity 因爲異常或其它原因被銷燬時,處於棧第二層的 Activity 將被激活,上浮到棧頂。當新的 Activity 啓動入棧時,原 Activity 會被壓入到棧的第二層。一個 Activity 在棧中的位置變化反映了它在不同狀態間的轉換。Activity 的狀態與它在棧中的位置關係如下圖所示:

圖 2. Activity 的狀態與它在棧中的位置關係

如上所示,除了最頂層即處在 Active 狀態的 Activity 外,其它的 Activity 都有可能在系統內存不足時被回收,一個 Activity 的實例越是處在棧的底層,它被系統回收的可能性越大。系統負責管理棧中 Activity 的實例,它根據 Activity 所處的狀態來改變其在棧中的位置。
Activity 生命週期
在 android.app.Activity類中,Android 定義了一系列與生命週期相關的方法,在我們自己的 Activity 中,只是根據需要複寫需要的方法,Java 的多態性會保證我們自己的方法被虛擬機調用,這一點與 J2ME 中的 MIDlet 類似。
public class OurActivity extends Activity {
protected void onCreate(Bundle savedInstanceState);
protected void onStart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestroy();
}

這些方法的說明如下:
i. protected void onCreate(Bundle savedInstanceState)一個 Activity 的實例被啓動時調用的第一個方法。一般情況下,我們都覆蓋該方法作爲應用程序的一個入口點,在這裏做一些初始化數據、設置用戶界面等工作。大多數情況下,我們都要在這裏從 xml 中加載設計好的用戶界面。例如:
setContentView(R.layout.main);

當然,也可從 savedInstanceState中讀我們保存到存儲設備中的數據,但是需要判斷 savedInstanceState是否爲 null,因爲 Activity 第一次啓動時並沒有數據被存貯在設備中:
if(savedInstanceState!=null){
savedInstanceState.get("Key");
}

i. protected void onStart()該方法在 onCreate() 方法之後被調用,或者在 Activity 從 Stop 狀態轉換爲 Active 狀態時被調用。
ii. protected void onResume()在 Activity 從 Pause 狀態轉換到 Active 狀態時被調用。
iii. protected void onResume()在 Activity 從 Active 狀態轉換到 Pause 狀態時被調用。
iv. protected void onStop()在 Activity 從 Active 狀態轉換到 Stop 狀態時被調用。一般我們在這裏保存 Activity 的狀態信息。
v. protected void onDestroy()在 Active 被結束時調用,它是被結束時調用的最後一個方法,在這裏一般做些釋放資源,清理內存等工作。

圖 3. 這些方法的調用時機

此外,Android 還定義了一些不常用的與生命週期相關的方法可用:
protected void onPostCreate(Bundle savedInstanceState);
protected void onRestart();
protected void onPostResume();

Android 提供的文檔詳細的說明了它們的調用規則。
創建一個 Activity
在 android 中創建一個 Activity 是很簡單的事情,編寫一個繼承自 android.app.Activity的 Java 類並在 AndroidManifest.xml聲明即可。下面是一個爲了研究 Activity 生命週期的一個 Activity 實例(工程源碼見下載):
Activity 文件:
public class EX01 extends Activity {
private static final String LOG_TAG = EX01.class.getSimpleName();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.e(LOG_TAG, "onCreate");
}
@Override
protected void onStart() {
Log.e(LOG_TAG, "onStart");
super.onStart();
}
@Override
protected void onResume() {
Log.e(LOG_TAG, "onResume");
super.onResume();
}
@Override
protected void onPause() {
Log.e(LOG_TAG, "onPause");
super.onPause();
}
@Override
protected void onStop() {
Log.e(LOG_TAG, "onStop");
super.onStop();
}
@Override
protected void onDestroy() {
Log.e(LOG_TAG, "onDestroy ");
super.onDestroy();
}
}

AndroidManifest.xml 中通過 <activity> 節點說明 Activity,將 apk 文件安裝後,系統根據這裏的說明來查找讀取 Activity,本例中的說明如下:
<activity android:name=".EX01" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

啓動另外一個 Activity
Activity.startActivity()方法可以根據傳入的參數啓動另外一個 Activity:
Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);
startActivity(intent);

當然,OtherActivity同樣需要在 AndroidManifest.xml 中定義。
Activity 之間通信
使用 Intent 通信
在 Android 中,不同的 Activity 實例可能運行在一個進程中,也可能運行在不同的進程中。因此我們需要一種特別的機制幫助我們在 Activity 之間傳遞消息。Android 中通過 Intent 對象來表示一條消息,一個 Intent 對象不僅包含有這個消息的目的地,還可以包含消息的內容,這好比一封 Email,其中不僅應該包含收件地址,還可以包含具體的內容。對於一個 Intent 對象,消息"目的地"是必須的,而內容則是可選項。
在上面的實例中通過 Activity. startActivity(intent)啓動另外一個 Activity 的時候,我們在 Intent 類的構造器中指定了"收件人地址"。
如果我們想要給"收件人"Activity 說點什麼的話,那麼可以通過下面這封"e-mail"來將我們消息傳遞出去:
Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);
// 創建一個帶"收件人地址"的 email
Bundle bundle =new Bundle();// 創建 email 內容
bundle.putBoolean("boolean_key", true);// 編寫內容
bundle.putString("string_key", "string_value");
intent.putExtra("key", bundle);// 封裝 email
startActivity(intent);// 啓動新的 Activity

那麼"收件人"該如何收信呢?在 OtherActivity類的 onCreate()或者其它任何地方使用下面的代碼就可以打開這封"e-mail"閱讀其中的信息:
Intent intent =getIntent();// 收取 email
Bundle bundle =intent.getBundleExtra("key");// 打開 email
bundle.getBoolean("boolean_key");// 讀取內容
bundle.getString("string_key");

上面我們通過 bundle對象來傳遞信息,bundle維護了一個 HashMap<String, Object>對象,將我們的數據存貯在這個 HashMap 中來進行傳遞。但是像上面這樣的代碼稍顯複雜,因爲 Intent 內部爲我們準備好了一個 bundle,所以我們也可以使用這種更爲簡便的方法:
Intent intent =new Intent(EX06.this,OtherActivity.class);
intent.putExtra("boolean_key", true);
intent.putExtra("string_key", "string_value");
startActivity(intent);

接收:
Intent intent=getIntent();
intent.getBooleanExtra("boolean_key",false);
intent.getStringExtra("string_key");

使用 SharedPreferences
SharedPreferences 使用 xml 格式爲 Android 應用提供一種永久的數據存貯方式。對於一個 Android 應用,它存貯在文件系統的 /data/ data/your_app_package_name/shared_prefs/目錄下,可以被處在同一個應用中的所有 Activity 訪問。Android 提供了相關的 API 來處理這些數據而不需要程序員直接操作這些文件或者考慮數據同步問題。
// 寫入 SharedPreferences
SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);
Editor editor = preferences.edit();
editor.putBoolean("boolean_key", true);
editor.putString("string_key", "string_value");
editor.commit();

// 讀取 SharedPreferences
SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);
preferences.getBoolean("boolean_key", false);
preferences.getString("string_key", "default_value");

其它方式
Android 提供了包括 SharedPreferences 在內的很多種數據存貯方式,比如 SQLite,文件等,程序員可以通過這些 API 實現 Activity 之間的數據交換。如果必要,我們還可以使用 IPC 方式。

Activity 的 Intent Filter
Intent Filter 描述了一個組件願意接收什麼樣的 Intent 對象,Android 將其抽象爲 android.content.IntentFilter 類。在 Android 的 AndroidManifest.xml 配置文件中可以通過 <intent-filter >節點爲一個 Activity 指定其 Intent Filter,以便告訴系統該 Activity 可以響應什麼類型的 Intent。
當程序員使用 startActivity(intent) 來啓動另外一個 Activity 時,如果直接指定 intent 了對象的 Component 屬性,那麼 Activity Manager 將試圖啓動其 Component 屬性指定的 Activity。否則 Android 將通過 Intent 的其它屬性從安裝在系統中的所有 Activity 中查找與之最匹配的一個啓動,如果沒有找到合適的 Activity,應用程序會得到一個系統拋出的異常。這個匹配的過程如下:

圖 4. Activity 種 Intent Filter 的匹配過程

Action 匹配
Action 是一個用戶定義的字符串,用於描述一個 Android 應用程序組件,一個 Intent Filter 可以包含多個 Action。在 AndroidManifest.xml 的 Activity 定義時可以在其 <intent-filter >節點指定一個 Action 列表用於標示 Activity 所能接受的"動作",例如:
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<action android:name="com.zy.myaction" />
......
</intent-filter>

如果我們在啓動一個 Activity 時使用這樣的 Intent 對象:
Intent intent =new Intent();
intent.setAction("com.zy.myaction");

那麼所有的 Action 列表中包含了"com.zy.myaction"的 Activity 都將會匹配成功。
Android 預定義了一系列的 Action 分別表示特定的系統動作。這些 Action 通過常量的方式定義在 android.content. Intent中,以"ACTION_"開頭。我們可以在 Android 提供的文檔中找到它們的詳細說明。
URI 數據匹配
一個 Intent 可以通過 URI 攜帶外部數據給目標組件。在 <intent-filter >節點中,通過 <data/>節點匹配外部數據。
mimeType 屬性指定攜帶外部數據的數據類型,scheme 指定協議,host、port、path 指定數據的位置、端口、和路徑。如下:
<data android:mimeType="mimeType" android:scheme="scheme"
android:host="host" android:port="port" android:path="path"/>

如果在 Intent Filter 中指定了這些屬性,那麼只有所有的屬性都匹配成功時 URI 數據匹配纔會成功。
Category 類別匹配
<intent-filter >節點中可以爲組件定義一個 Category 類別列表,當 Intent 中包含這個列表的所有項目時 Category 類別匹配纔會成功。

一些關於 Activity 的技巧
鎖定 Activity 運行時的屏幕方向
Android 內置了方向感應器的支持。在 G1 中,Android 會根據 G1 所處的方向自動在豎屏和橫屏間切換。但是有時我們的應用程序僅能在橫屏 / 豎屏時運行,比如某些遊戲,此時我們需要鎖定該 Activity 運行時的屏幕方向,<activity >節點的 android:screenOrientation屬性可以完成該項任務,示例代碼如下:
<activity android:name=".EX01"
android:label="@string/app_name"
android:screenOrientation="portrait">// 豎屏 , 值爲 landscape 時爲橫屏
............
</activity>

全屏的 Activity
要使一個 Activity 全屏運行,可以在其 onCreate()方法中添加如下代碼實現:
// 設置全屏模式
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 去除標題欄
requestWindowFeature(Window.FEATURE_NO_TITLE);

在 Activity 的 Title 中加入進度條
爲了更友好的用戶體驗,在處理一些需要花費較長時間的任務時可以使用一個進度條來提示用戶"不要着急,我們正在努力的完成你交給的任務"。如下圖:
在 Activity 的標題欄中顯示進度條不失爲一個好辦法,下面是實現代碼:
// 不明確進度條
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.main);
setProgressBarIndeterminateVisibility(true);

// 明確進度條
requestWindowFeature(Window.FEATURE_PROGRESS);
setContentView(R.layout.main);
setProgress(5000);


[轉]關於Activity和Task的設計思路和方法
2010-02-03 11:51
Activity和Task是Android Application Framework架構中最基礎的應用,開發者必須清楚它們的用法和一些開發技巧。本文用大量的篇幅並通過引用實例的方式一步步深入全面講解它們的基礎原理(underlying principles)和架構(mechanisms),例如:Navigation、Multitasking、activity re-use、intents和activity stack等...大部分與其相關的應用模塊。重點講解開發過程中如何更準確的體現用戶交互性的便捷和高效,同時也幫助分析Designers和Developers在開發期間所要面對的問題。
文中涉及到的實例有一部分是屬於平臺自帶的application(例如:撥號程序等),另外也有Google產品線中的一些有代表性的應用(例如:Google Map等)。建議大家親自利用Emulator或者Android-powered device測試實例中的效果,這樣可以幫助更加清晰的理解一些模塊的含義。(注意:可能會因爲硬件對於某些功能無法提供支持,所以有一些實例可能無法在你的測試機中正常瀏覽)
首先需要清楚一些基礎模塊:
• Applications
• Acitivities
• Activity Stack
• Tasks
以上這四個模塊對於理解這篇文章非常重要,下邊就來逐一的簡單介紹其具體的含義和用法(也可以通過其鏈接直接查看官方文檔)。
Applications
任何一個Android Application基本上是由一些Activities組成,當用戶與應用程序交互時其所包含的部分Activities具有緊密的邏輯關係,或者各自獨立處理不同的響應。這些Activities捆綁在一起成爲了一個處理特定需求的Application, 並且以".apk"作爲後綴名存在於文件系統中。Android平臺默認下的應用程序 例如:Email、Calendar、Browser、Maps、Text Message、Contacts、Camera和Dialer等都是一個個獨立的Apps。
Activities
上邊已經提到Activities是構成Applications的主要組成部分,其實可以更爲具體的理解爲Application僅僅是一個抽象的標籤,它將系統內一部分Activities關聯在一起,協同完成用戶的特定需求。安裝Application的過程也可以簡單理解爲將其所包裹的Activities導入到當前的系統中,如果系統中已經存在了相同的Activities,那麼將會自動將其關聯,而不會重複安裝相同的Activities,避免資源的浪費。Application卸載的過程也會檢查當前所關聯的Activities是否有被其它Application標籤所關聯,如果僅僅是提供當前的Application使用,那麼將會徹底被移除,相反則不做任何操作。
用戶與Application的交互行爲大部分都是通過GUI來完成,在Android平臺可以有兩種方式定義GUI,其中可以利用XML來預置靜態的GUI元素,或者在Activity類的內部動態定義GUI元素。這兩種不同的方法都是由Activity作爲驅動和響應用戶交互事件的主體。當啓動Application之後,至少需要一個包含有GUI信息的Activity實例被創建。
Activity的主體包括兩個主要部分,其中一個是Content(data),另外一個是響應用戶交互事件的行爲。列舉一個Dialer例子的截圖,其中包括四個部分:Dialer主界面、通訊錄、查看聯繫人信息和添加新聯繫人。

下面列舉了更多比較有代表性的Applications和其所包含的Activities:
• Email - activities to view folders, view list of messages, view a message, compose a message, and set up an account
• Calendar - activities to view day, view week, view month, view agenda, edit an event, edit preferences, and view an alert
• Camera - activities for running the camera, viewing the list of pictures, viewing a picture, cropping a picture, running the camcorder, viewing the list of movies, and viewing a movie
• Game - one activity to play the game, typically another for setup
• Maps - one activity to view a location on a map, a second for lists (such as turn list or friend list), and a third for details (friend location, status, photo)
Application基本上是由四個模塊組成:Activity、Service、Content Provider 和 Broadcast Receiver,其中Activity是實現應用的主體。
Activity Stack
操作應用程序時,有時需要調用多個Activities來完成需求,例如:發送郵件程序,首先是進入郵件主界面,然後啓動一個新的Activity用於填寫新郵件內容,同時可以調出聯繫人列表用於插入收件人信息等等。在這個操作過程中Android平臺有一個專門用於管理Activities堆棧的機制,其可以方便的線性記錄Activities實例,當完成某個操作時,可以通過這個導航功能返回之前的Activity(通過按操作檯的"Back")。
每次啓動新的Activity都將被添加到Activity Stack。用戶可以方便的返回上一個Activity直到Home Screen,到達Home Screen後,將無法再繼續查看堆棧記錄(俗話說:到頭了- Androidres.com)。如果當前Task被中止(Interrupting the task),返回到系統主界面後啓動了其它操作,當希望返回到前一個Task繼續執行時,只需要再次通過主界面的Application launcher或者快捷方式啓動這個Task的Root Activity便可返回其中止時的狀態繼續執行。
相對於Views、Windows、Menus和Dialogs而言,Activity是唯一可被記錄在History stack中的數據,所以當你所設計的應用程序需要用戶由A界面進入到次一級界面B,當完成操作後需要再次返回A,那麼必須考慮將A看作爲Activity,否則將無法從歷史堆棧中返回。
Tasks
在Android平臺上可以將Task簡單的理解爲由多個Activities共同協作完成某一項應用,而不管Activities具體屬於哪個Application。通過下邊的圖示可以更清晰的理解Applications、Tasks、Activities三者之間的關係 (Androidres.com提供):

Activities可以被看作爲是獨立存在於系統資源中,而且是作爲實現具體應用的主體,Task將一些Activity關聯起來實現一個更復雜的應用,單獨或者多個Tasks可以被定義爲一個Application。
通常實現一個Task都會存在一個Root Activity,但並不是所有情況都如此,通過Application launcher、Home screen 的快捷方式或者 由 "Recent Tasks"(長時間按住Home鍵) 最近使用過的Task記錄中啓動。當從一個Activity中啓動另外一個Activity時,Back鍵將作用於返回前一個Activity,與此同時新開啓的Activity將被添加到Activity Stack中。
這裏有兩個被表示爲Task的例子:
• 發送帶有附件的郵件
• 查看YouTube視頻,並且通過Email的方式共享給其他聯繫人。
- Interrupting the Task
這是Task一個非常重要的特性,用戶可以實時中止當前爲完成的Task,新開啓一個不同的Task,當新Task完成操作後,依然可以返回當上一次中止的Task繼續完成餘下操作。這個特性大大方便了同時運行多個Tasks,並且可以方便的在他們之間切換。這裏有兩種方式可以從當前Task跳轉爲其它Task(應用這兩種方式切換Task,都允許返回到Task最初中止前的狀態)。
• 系統拋出一個Notification,當前Task會被終止,跳轉爲Notification的Task。
• 用戶強制中止
當然,除了這兩種方式以外,還有另外一個特殊情況,算作爲第三種方式來啓動一個新的Task:Activity本身被定義爲一個Task。例如: Maps和Browser就是屬於第三種情況的Application,通過郵件中的一個地址來啓動Maps Activity作爲一個新的Task,或者通過郵件中的鏈接啓動Browser來啓動一個新的Task。當處在這種情況下,Back按鍵被觸發後,將返回到上一個Task(郵件),因爲這些新的Tasks並不是通過Home Screen中的Application launcher或者快捷方式來啓動。

瞭解Activities和Tasks的基本原理
請大家一定首先理解之前所提及的內容,如果對某些概念依然含混不清,請及時查閱更多資料(官方文檔是最好的學習資料),否則無法快速理解接下來將要講述的例子,甚至喪失閱讀興趣。
接下來,將通過一些有代表性的實例瞭解關於Applications、Activities、Activities stack、Tasks和Intent等一些模塊的最基本原理。從各個角度分析系統對於用戶在不同模式下操作的反應原理。
從Home啓動一個Activity
絕大部分的Application都由此啓動(也有一些Application是通過其它Application啓動)。具體的方式有兩種,其一是從系統的Application Launcher啓動,另一種是直接由Home Screen的快捷方式。啓動Application後,Root Activity會顯示在當前窗口,並可直接供用戶操作界面元素。官方給出了一個有關這個過程的圖示,其實我感覺這個描述的還不夠直觀,湊合着用吧。大體的過程是由Home下啓動Email Application,在這個應用程序中可以直接提供給用戶操作的是List Messages Activity,Home Activity切換爲後臺運行。

應用Back或Home鍵離開當前Activity的區別
應用Back或者Home都可以離開當前Activity(基於Application的Root Activity),Home activity重新切換到foreground,然而二者最根本的區別在於用戶是否還需要保留當前Activity的state。
- Back:
將會終止(Destroy)當前正在運行的Activity,返回到之前的Activity(如果是 Root Activity,那麼將會直接返回到Home Activity)。官方給出了一個相關過程的圖示,當用戶正在操作List Messages Activity時,下拉郵件列表(改變了Scrolling狀態),通過Back鍵返回到Home Activity之後,當再次通過Email Icon啓動 List Messages Activity時,將會看到列表處在初始位置。通過這個演示可以瞭解到通過Back鍵離開當前Activity時,無法暫時保留住其State數據,當再次啓動時相當於重新創建了一個實例。

-Home:
利用Home取代Back返回的方式,當前Activity將被切換到Background,而不是被Destroied。這樣的好處是可以暫時保留這個Activity的State信息,當再次通過Application launcher或者快捷方式啓動時,可以返回到最後離開的狀態。對比在Back中引用的例子,當再次由Home返回到Activity時,將會看到最後一次操作所記錄的Scroll狀態,而不是默認的初始位置。

Exception(例外情況)
前邊列舉了兩種典型的情況,同時還存在一些例外的情況,某些Activity從Background被"召喚"到foreground之後依然是相當於重新創建了新實例,其有區別於前邊所論述的結果。即便是暫時保存在Background模式下(沒有被Destroied),其State數據也將丟失。例如:Contacts 和 Gallery 等。當用戶啓動了Contact應用程序,並點選某個條目查看詳細信息,如果通過Home鍵返回後,再次重複啓動Contact應用程序時,看到的並不是之前所打開的特定條目的詳細信息,而是初始的默認界面。這個例子說明不是所有情況下通過Home鍵返回後都可以保存當前Activity的State信息。
另外一種是與Back鍵有關的特殊情況。前邊提及到大部分的Activity通過Back鍵返回到Home Activity時,其自身將被徹底銷燬,默認情況下Activity響應Back按鍵的方法被定義了Destroy行爲。但對於某些特別情況,開發者可以根據需求將相應Back按鍵事件的行爲重新"override",撤消默認的Destroy行爲。音樂播放器是與其相關的一個典型應用,當用戶在播放器的Root Activity中觸發Back按鍵後,轉爲Background模式下繼續播放當前的音樂,同時Home Activity轉爲Foreground。
Activity的複用
在多個不同的Applications中,當遇到有相同目的應用時,會涉及到Activity的複用性問題,這在開發過程中是一個非常普遍的情況。複用性一直被衆多開發機構強調爲節約成本,優化資源的最有效的機制。對於移動應用平臺更加看重資源的最優化利用,複用性的應用在Android平臺上無處不在,通過兩個比較基礎的例子來具體的說明。
- Contacts利用Gallery獲得圖像資源
衆所周知Contacts是手機中最常用的應用程序,主要用於存儲當前用戶的聯繫人信息,其中需要包含聯繫人的頭像信息。在Android平臺中的圖像信息是由Gallery管理,所以Contacts必然需要複用Gallery Activity來獲取相應的圖像信息。
針對於Android或者其它平臺開發應用程序都需要有良好的複用性意識,這個需要貫穿於項目的整個開發過程。包括如何利用當前系統的現有資源,或者考慮到將來可能會被其它應用程序用於完成特定的需求。當用戶正在調用的Intent filter不唯一時,系統將彈出一個供用戶選擇的對話框,這的確是一個完美的解決方法。

- 利用Messaging擴展Gallery共享功能
用戶通過Gallery查看當前系統中的圖像資源,每次單獨打開一幅圖像資源都可以通過Menu -> Share將當前的資源以附件形式插入新創建的Messaging中,並且以正常發送信息的方式將其共享給收件人。如果取消當前的共享行爲,只需要通過Back按鍵返回到Gallery Activity。相比較前一個例子的區別在於,Message Activity完成發送或者被取消操作,其不會返回任何信息。

以上兩個例子分別講解了利用一系列的Activities來完成某一項需求,並且它們都調用了外部的Application資源。
Replacing an Activity
目前要介紹的內容是關於在不同的Applications中,有相同Intent filter屬性的Activities可相互間替換,這對於習慣Windows等操作系統的用戶比較不容易理解。其實如果您足夠細心,就可以發現之前的例子中有關於這裏所提及情況。
通常遇到這種情況發生時,一般都是因爲外部具有相同功能的Activity A 在處理問題的能力方面要優於當前Application中默認的操作行爲Activity B,系統會拋出一個可供選擇的對話框,用戶根據主觀判斷來選擇最優的方式處理當前任務。通過一個比較容易理解的實例來說明整個過程,建議"動手能力強"的同學可以通過模擬器親自嘗試。
例如:用戶在當前系統下加載了最新的Phone Ringtone Activity,取名爲Rings Extended。如果用戶通過Setting -> Sounds&Display -> Phone Ringtone 來設置當前的鈴音屬性時,將會彈出一個包含有系統默認的Phone Ringtone Activity 和最新加載的Rings Extended兩種可供選擇的操作應用,同時在對話框中還提供了一種可以直接啓動系統默認的操作方式選項。如果用戶選擇了Rings Extended,那麼其將會被載入當前的線程中替代原有的默認操作行爲,可以根據下面的圖示來增強理解。

多任務同時運行(Multitasking)
在之前的版塊有專門提到關於Home和Back兩種切換到Home Screen的方法和它們之間的差異性,這個章節將會重點涉及到系統可以同時處理多個實時運行的任務。如果用戶正處於某個Application A開啓狀態時,通過Home按鍵切換回Home Activity的同時保留了此前Application A運行的狀態信息,可以開啓新程序的同時,也可以再次將Application A切換回Foreground。
接下來通過一個有關Map應用的實例更加具體的瞭解其所涵蓋的過程。
首先的起始階段分爲三個步驟,
第一步,由Application Launcher啓動Map應用程序,並且搜索一個具體的地理位置。假設當前的網絡環境非常不理想,需要花費一定的時間Download地圖數據。
第二步,當系統需要花費較長時間加載當前地圖信息數據時,保持當前Activity的狀態,返回Home Activity啓動其它的Applicaton,地圖Activity切換到Background,而並不會中斷加載進度(依然保持網絡連接)。
注意:以上是Activity在默認條件下的反應行爲,其切換爲Background狀態後直接觸發onStop()事件,開發者可以重新定義其方法。例如:強制Activity在轉爲Background狀態下,終止網絡連接。
第三步,當前Map activity已經切換到Background狀態下運行,Home Activity切換到Foreground。這時用戶啓動Calender activity,其將自動轉爲Foreground狀態,同時獲得操作焦點。
將以上三個步驟用圖示的方式表述:

最後,退出當前Calender activity返回到Home,再次通過Maps圖標將其處在Background狀態的實例切換到Foreground。

通過上邊的例子看出用戶通過Application Launcher同時運行多個Tasks,代表系統具備多任務處理機制 - Running multiple tasks。
啓動Application的兩種不同方式
每個App都需要提供至少一個Entry point(翻譯成"入口點"有點彆扭,乾脆保留原樣)供用戶或者系統調用其所關聯的Activities,Application launcher中的小圖標就是每個單獨App的Entry Point。另外App也可以相互間通過Activity作爲Entry Point來啓動,可以將App所包含的每個Activity看作爲潛在的Entry point。
系統中的Phone Application同樣具有兩個Entry Points:Contacts和Dialer。下邊的圖示中可以瞭解到用戶通過Application launcher啓動Contacts Activity,選擇其中某一個聯繫人之後,調用Dialer Activity撥打其所提供的電話號碼。

Intents
在現實世界中大家每時每刻都會與周圍的環境發生互動,這個互動的過程首先要確定一種意識,例如:感覺到口渴,需要水分補充。這種意識會引導自己以習慣的方式解決口渴問題,採用的方式可以多種多樣,吃冰淇淋、喝水、嚼樹葉等。類似於口渴的意識形態被抽象爲Intent,並將其看作是一種對象,這就是Android響應"意識"的方式。
在Android平臺上,用戶的操作行爲是由各種不同的事件組成,系統會將每個事件都抽象爲Intent對象,尋找解決這項需求的具體方法。抽象的Intent對象有兩種形式,第一種是"明確"的Intent(Explicit Intent),在初始化的時候已經爲這個Intent關聯了特定的Activity。第二種是"不明確"的Intent(Implicit Intent),代表這個Intent沒有明確關聯Activity,當它被拋出後,系統在衆多Activities中根據Intent filter來尋找與其匹配的處理方法。如果存在多個結果,用戶可以根據需要選擇合適的處理方法。
引用一個具體的例子,單擊一個mailto:[email protected]鏈接後,這個被拋出的Intent屬於 Implicit Intent ,系統抓取了解決這個Intent的結果,將所有的結果供用戶選擇(Gmail或者Email):

下邊給出更多系統默認的Intent關聯列表:
• View the list of contacts - resolves to a contact list viewer activity
• View a particular contact - resolves to a contact viewer activity
• Edit a particular contact - resolves to a contact editor activity
• Send to a particular email - resolves to an email activity
• Dial a phone number - resolves to a phone dialer activity
• View the list of images - resolves to an image list viewer activity
• View a particular image - resolves to an image viewer activity
• Crop a particular image - resolves to an image cropper activity
Intent對象包含兩個元素:
1)Action :例如 查看、編輯、撥打電話、查看圖像資源等等。
2)Data:提供給某種行爲的具體數據。加工果汁飲料,需要提供水果(黑心店除外)。
參照官網的解釋:Intent Class 和 Intent Filters。
Tasks相互間切換
依然是應用實例來說明這個切換的過程。在這個例子中,用戶編輯一個短消息,並且插入圖像附件,但是在發送之前啓動Calendar,隨後切換回短消息編輯界面,最後發送信息。
1)啓動第一個Task:Messaging App,Home > Messaging > New Message > Menu > Attach > Picture。插入圖片的步驟需要調用Gallery Activity,它是一個獨立的外部程序。

接下來啓動另外一個Task,由於沒有直接從當前的Activity運行Calendar,所以需要切換到Home。
2)啓動另外一個Application(Calendar):Home > Calendar

3)查看Calendar完成後,將Messaging由Background切換到Foreground模式,其中還包括了添加附件,並最終發送消息。

至此,對於Android平臺中兩個比較核心元素: Activities和Tasks 的介紹基本告一段落,以後也許會有更多關於這方面的討論,希望得到您的關注。另外,有些朋友或許已經看過官方的原文,而本站也再次有幸得到了您的通讀,如果在某些概念或者論述內容上存在遺漏或者誤解,那麼真誠的希望能夠獲得指正和幫助。


查看文章
Android Wifi模塊分析(總覽)
2010-02-04 10:37
這兩天通過對Android源碼中Wifi模塊相關代碼的理解,對Wifi模塊有了一個全新的認識。簡單記錄在這裏,就算是爲以後的學習留個記錄。
總覽:
1,Wifi介紹(百度百科)
2,Android中Wifi模塊的初始化
3,Wifi模塊的啓動(使能)
4,Wifi掃描流程
5,Wifi配置AP參數流程
6,Wifi啓動連接流程
7,Wifi配置IP地址

Android Wifi模塊分析(一)
2010-02-04 10:44
一:Wifi介紹
概述
WIFI就是一種無線聯網的技術,以前通過網線連接電腦,而現在則是通過無線電波來連網;常見的就是一個無線路由器,那麼在這個無線路由器的電波覆蓋的有效範圍都可以採用WIFI連接方式進行聯網,如果無線路由器連接了一條ADSL線路或者別的上網線路,則又被稱爲"熱點"。
運作原理
Wi-Fi 的設置至少需要一個Access Point(ap)和一個或一個以上的client(hi)。AP每100ms將SSID(Service Set Identifier)經由beacons(信號臺)封包廣播一次,beacons封包的傳輸速率是1 Mbit/s,並且長度相當的短,所以這個廣播動作對網絡效能的影響不大。因爲Wi-Fi規定的最低傳輸速率是1 Mbit/s ,所以確保所有的Wi-Fi client端都能收到這個SSID廣播封包,client 可以藉此決定是否要和這一個SSID的AP連線。使用者可以設定要連線到哪一個SSID。Wi-Fi系統總是對用戶端開放其連接標準,並支援漫遊,這就是Wi-Fi的好處。但亦意味着,一個無線適配器有可能在性能上優於其他的適配器。由於Wi-Fi通過空氣傳送信號,所以和非交換以太網有相同的特點。 近兩年,出現一種WIFI over cable的新方案。此方案屬於EOC(ethernet over cable)中的一種技術。通過將2.4G wifi射頻降頻後在cable中傳輸。此種方案已經在中國大陸小範圍內試商用。

Android Wifi模塊分析(二)
2010-02-04 10:51
二:Wifi模塊的初始化::
在 SystemServer 啓動的時候,會生成一個ConnectivityService 的實例,

try {
Log.i(TAG, "Starting Connectivity Service.");
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, new
ConnectivityService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Connectivity Service", e);
}

ConnectivityService 的構造函數會創建WifiService,
if (DBG) Log.v(TAG, "Starting Wifi Service.");
mWifiStateTracker = new WifiStateTracker(context, handler);
WifiService wifiService = new WifiService(context, mWifiStateTracker);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);

WifiStateTracker 會創建WifiMonitor 接收來自底層的事件,WifiService 和WifiMonitor 是整
個模塊的核心。WifiService 負責啓動關閉wpa_supplicant、啓動關閉WifiMonitor 監視線程
和把命令下發給wpa_supplicant,而WifiMonitor 則負責從wpa_supplicant 接收事件通知。
具體流程圖如下:

DBUS簡介(轉)
2010-02-05 10:18
DBus 介紹
dbus 是freedesktop下開源的Linux IPC通信機制,本身Linux 的IPC通信機制包括,管道(fifo),共享內存,信號量,消息隊列,Socket等。 像現在流行的moblin平臺就使用了DBUS通信,還有我最近看的bluez 4 也是通過DBUS來交互的。

它是個3層架構的進程間通信系統,包括:
1. 函數庫libdbus,用於兩個應用程序呼叫聯繫和交互消息。
2. Message bus daemon,總線守護進程可同時與多個應用程序相連,並能把來自一個應用程序的消息路由到0或者多個其他程序。
3. 一系列基於特定應用程序框架的Wrapper庫。 比如libdbus-glib, libdbus-python.
參看圖1-1, Bus Daemon Process就是運行在linux的daemon(dbus-daemon, 用戶可以在/etc/init.d/dbus 操作,stop, start等等), dbus-daemon運行時會調用libdus的庫。 在Application Process1裏面就是應用層的東西了,應用程序調用特定的應用程序框架的Wrapper庫與dbus-daemon進行通信。

我前段時間就是用Python寫程序與dbus-daemon通信,所以就需要libdbus-python,後來又用c寫程序,又裝了libdus-glib。實質上在dbus主頁上(http://www.freedesktop.org/wiki/Software/dbus) 提供了很多Wrapper庫, for QT4, JAVA, Perl, C++, Pascal, QT3, .NET, Ruby等等。這個Wrapper庫呢其實就是對dbus下層調用做了封裝,給上層暴露一個友好的接口。dbus的底層其實也是通過socket通信的

圖 1-1
我再給一張bluez的例子讓大家更理解dbus; 有四個應用想與bluz的damon通信,bluez註冊到dbus中,其它的應用只需要向dbus要bluez的數據,
dbus負責再和bluez溝通了,但是bluez一定要把接口告訴其它應用。


Android Wifi模塊分析(三)
2010-02-05 17:13
第三部分:Wifi模塊的啓動(使能)

WirelessSettings 在初始化的時候配置了由WifiEnabler 來處理Wifi 按鈕,

private void initToggles() {
mWifiEnabler = new WifiEnabler(this,
(WifiManager) getSystemService(WIFI_SERVICE),
(CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI));

當用戶按下Wifi 按鈕後,Android 會調用WifiEnabler 的onPreferenceChange,再由WifiEnabler
調用WifiManager 的setWifiEnabled 接口函數,通過AIDL,實際調用的是WifiService 的
setWifiEnabled 函數,WifiService 接着向自身發送一條MESSAGE_ENABLE_WIFI 消息,在
處理該消息的代碼中做真正的使能工作:首先裝載WIFI 內核模塊(該模塊的位置硬編碼爲
"/system/lib/modules/wlan.ko" ), 然後啓動wpa_supplicant ( 配置文件硬編碼爲
"/data/misc/wifi/wpa_supplicant.conf"),再通過WifiStateTracker 來啓動WifiMonitor 中的監視
線程。

private boolean setWifiEnabledBlocking(boolean enable) {
final int eventualWifiState = enable ? WIFI_STATE_ENABLED :WIFI_STATE_DISABLED;
updateWifiState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING);
if (enable) {
if (!WifiNative.loadDriver()) {
Log.e(TAG, "Failed to load Wi-Fi driver.");
updateWifiState(WIFI_STATE_UNKNOWN);
return false;
}
if (!WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG, "Failed to start supplicant daemon.");
updateWifiState(WIFI_STATE_UNKNOWN);
return false;
}
mWifiStateTracker.startEventLoop();
}
// Success!
persistWifiEnabled(enable);
updateWifiState(eventualWifiState);
return true;
}

當使能成功後,會廣播發送WIFI_STATE_CHANGED_ACTION 這個Intent 通知外界WIFI
已經成功使能了。WifiEnabler 創建的時候就會向Android 註冊接收
WIFI_STATE_CHANGED_ACTION,因此它會收到該Intent,從而開始掃描。

private void handleWifiStateChanged(int wifiState) {
if (wifiState == WIFI_STATE_ENABLED) {
loadConfiguredAccessPoints();
attemptScan();
}

具體流程如下流程圖所示:

Android Wifi模塊分析(四)
2010-02-08 16:29
第四部分:查找熱點(AP)

上一節(第三部分:Wifi開啓)中講到Wifi模塊開啓後會對外發送WIFI_STATE_CHANGED_ACTION。WifiLayer中註冊了Action的Receiver。
當WifiLayer收到此Action後開始scan的流程,具體如下:

當wpa_supplicant 處理完SCAN 命令後,它會向控制通道發送事件通知掃描完成,從wifi_wait_for_event 函數會接收到該事件,由此WifiMonitor 中的MonitorThread 會被執行來出來這個事件:

Android Wifi模塊分析(五)
2010-02-08 16:32
第五部分:配置 AP 參數

當用戶在 WifiSettings 界面上選擇了一個AP 後,會顯示配置AP 參數的一個對話框:

Android Wifi模塊分析(六)
2010-02-08 16:34
第六部分:Wifi連接

具體流程參見以下流程圖:

Android Wifi模塊分析(七)
2010-02-08 16:35
第七部分:IP地址的配置

流程如圖:

Linux進程知識
2010-02-22 14:56
1、進程可以看做程序的一次執行過程。在linux下,每個進程有唯一的PID標識進程。PID是一個從1到32768的正整數,其中1一般是特殊進程 init,其它進程從2開始依次編號。當用完32768後,從2重新開始。

2、linux中有一個叫進程表的結構用來存儲當前正在運行的進程。可以使用"ps aux"命令查看所有正在運行的進程。

3、進程在linux中呈樹狀結構,init爲根節點,其它進程均有父進程,某進程的父進程就是啓動這個進程的進程,這個進程叫做父進程的子進程。

4、fork的作用是複製一個與當前進程一樣的進程。新進程的所有數據(變量、環境變量、程序計數器等)數值都和原進程一致,但是是一個全新的進程,並作 爲原進程的子進程。


Andriod通話處理流程

一、總覽
1、從java端發送at命令的處理流程。

2、unsolicited 消息從modem上報到java的流程。

3、貓相關的各種狀態的監聽和通知機制。

4、通話相關的圖標變換的工作原理。

5、gprs撥號上網的通路原理。

6、通話相關的語音通路切換原理、震動接口。

7、通話相關的notification服務。

8、通話相關的各種server。

第一部分:從java端發送at命令的處理流程。
撥出電話流程:

1、contacts的androidmanifest.xml android:process="android.process.acore"說明此應用程序運行在acore進程中。

DialtactsActivity的intent-filter的action屬性設置爲main,catelog屬性設置爲launcher,所以此activity能出現

在主菜單中,並且是點擊此應用程序的第一個界面。dialtactsactivity包含四個tab,分別由TwelveKeyDialer、

RecentCallsListActivity,兩個activity-alias DialtactsContactsEntryActivity和DialtactsFavoritesEntryActivity分別

表示聯繫人和收藏tab,但是正真的聯繫人列表和收藏是由ContactsListActivity負責。

2、進入TwelveKeyDialer OnClick方法,按住的按鈕id爲: R.id.digits,執行

placecall()

Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,

Uri.fromParts("tel", number, null));

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(intent);

3、intert.ACTION_CALL_PRIVILEGED實際字符串爲android.intent.action.CALL_PRIVILEGED,通過查找知道了packegs/phone

下面的androidmanifest.xml中PrivilegedOutgoingCallBroadcaster activity-alias設置了intent-filter,所以需要找到其

targetactivity爲OutgoingCallBroadcaster。所以進入OutgoingCallBroadcaster的

onCreate()

//如果爲緊急號碼馬上啓動intent.setClass(this, InCallScreen.class); startActivity(intent);

Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);

if (number != null) broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);

broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);

broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, intent.getData().toString());

if (LOGV) Log.v(TAG, "Broadcasting intent " + broadcastIntent + ".");

sendOrderedBroadcast(broadcastIntent, PERMISSION, null, null,

Activity.RESULT_OK, number, null);

4、Intent.ACTION_NEW_OUTGOING_CALL實際字符串爲android.intent.action.NEW_OUTGOING_CALL,通過查找知道了packegs/phone

下面的androidmanifest.xml中OutgoingCallReceiver Receiver接收此intent消息。找到OutgoingCallReceiver,執行

onReceive()函數

Intent newIntent = new Intent(Intent.ACTION_CALL, uri);

newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);

newIntent.setClass(context, InCallScreen.class);

newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

5、請求撥號的java部分流程

onCreate(第一次)/onNewIntent(非第一次)

internalResolveIntent

placeCall(intent);

PhoneUtils.placeCall(mPhone, number, intent.getData());

phone.dial(number);

mCT.dial(newDialString);

dial(dialString, CommandsInterface.CLIR_DEFAULT);

cm.dial(pendingMO.address, clirMode, obtainCompleteMessage());//obtainCompleteMessage(EVENT_OPERATION_COMPLETE);

send(rr);

msg = mSender.obtainMessage(EVENT_SEND, rr);

acquireWakeLock();

msg.sendToTarget();

RILSender.handleMessage()

case EVENT_SEND:

...

s.getOutputStream().write(dataLength);

s.getOutputStream().write(data);//從這裏流程跑到下面ril.cpp中監聽部份

6、請求撥號的c/c++部分流程

6.1、初始化事件循環,啓動串口監聽,註冊socket監聽。

rild.c->main()

(1)、RIL_startEventLoop

//建立事件循環線程

ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);

//註冊進程喚醒事件回調

ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,

processWakeupCallback, NULL);

rilEventAddWakeup (&s_wakeupfd_event);

//建立事件循環

ril_event_loop

for (;;) {

...

n = select(nfds, &rfds, NULL, NULL, ptv);

// Check for timeouts

processTimeouts();

// Check for read-ready

processReadReadies(&rfds, n);

// Fire away

firePending();

}

(2)、funcs = rilInit(&s_rilEnv, argc, rilArgv);//實際是通過動態加載動態庫的方式執行reference-ril.c中的RIL_Init

//單獨啓動一個線程讀取串口數據

ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);

fd = open (s_device_path, O_RDWR);

ret = at_open(fd, onUnsolicited);

ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);

RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);

在initializeCallback中執行的程序:

setRadioState (RADIO_STATE_OFF);

at_handshake();

/* note: we don't check errors here. Everything important will

be handled in onATTimeout and onATReaderClosed */

/* atchannel is tolerant of echo but it must */

/* have verbose result codes */

at_send_command("ATE0Q0V1", NULL);

/* No auto-answer */

at_send_command("ATS0=0", NULL);

...

//註冊rild socket端口事件監聽到事件循環中

(3)、RIL_register(funcs);

s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);

ret = listen(s_fdListen, 4);

ril_event_set (&s_listen_event, s_fdListen, false,

listenCallback, NULL);//將此端口加入事件select隊列

rilEventAddWakeup (&s_listen_event);

如果rild socket端口有數據來了將執行listencallback函數

listencallback

//爲此客戶端連接創建新的監聽句柄,s_fdListen繼續監聽其他客戶端的連接。

s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);

ril_event_set (&s_commands_event, s_fdCommand, 1,

processCommandsCallback, p_rs);//將此端口加入事件select隊列

rilEventAddWakeup (&s_commands_event);

6.2、socket監聽,收到dial的socket請求

processCommandsCallback

//讀數據到p_record中

ret = record_stream_get_next(p_rs, &p_record, &recordlen);

processCommandBuffer(p_record, recordlen);

p.setData((uint8_t *) buffer, buflen);

// status checked at end

status = p.readInt32(&request);

status = p.readInt32 (&token);//請求隊列中的序號

pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));

pRI->token = token;

/*

包含#include "ril_commands.h"語句,結構體如下:

typedef struct {

int requestNumber;

void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);

int(*responseFunction) (Parcel &p, void *response, size_t responselen);

} CommandInfo;

*/

pRI->pCI = &(s_commands[request]);

pRI->p_next = s_pendingRequests;

s_pendingRequests = pRI;

pRI->pCI->dispatchFunction(p, pRI);

//假設是接收了dial指令,pRI->PCI->dispatchFunction(p,pRI),調用dispatchDial (p,pRI)

dispatchDial (p,pRI)

s_callbacks.onRequest(pRI->pCI->requestNumber, &dial, sizeof(dial), pRI);

in reference-ril.c onRequest()

...

switch (request) {

case RIL_REQUEST_DIAL:

requestDial(data, datalen, t);

asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);

ret = at_send_command(cmd, NULL);

err = at_send_command_full (command, NO_RESULT, NULL, NULL, 0, pp_outResponse);

err = at_send_command_full_nolock(command, type, responsePrefix, smspdu,timeoutMsec, sponse);

err = writeline (command);

//此處等待,直到收到成功應答或失敗的應答,如:ok,connect,error cme等

err = pthread_cond_wait(&s_commandcond, &s_commandmutex);

waiting....

waiting....

/* success or failure is ignored by the upper layer here.

it will call GET_CURRENT_CALLS and determine success that way */

RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);

p.writeInt32 (RESPONSE_SOLICITED);

p.writeInt32 (pRI->token);

errorOffset = p.dataPosition();

p.writeInt32 (e);

if (e == RIL_E_SUCCESS) {

/* process response on success */

ret = pRI->pCI->responseFunction(p, response, responselen);

if (ret != 0) {

p.setDataPosition(errorOffset);

p.writeInt32 (ret);

}

}

sendResponse(p);

sendResponseRaw(p.data(), p.dataSize());

blockingWrite(fd, (void *)&header, sizeof(header));

blockingWrite(fd, data, dataSize);

6.4、串口監聽收到atd命令的應答"OK"或"no carrier"等

readerLoop()

line = readline();

processLine(line);

handleFinalResponse(line);

pthread_cond_signal(&s_commandcond);//至此,前面的等待結束,接着執行RIL_onRequestComplete函數

6.5、java層收到應答後的處理,以dial爲例子.

ril.java->RILReceiver.run()

for(;;)

{

...

length = readRilMessage(is, buffer);

p = Parcel.obtain();

p.unmarshall(buffer, 0, length);

p.setDataPosition(0);

processResponse(p);

type = p.readInt();

if (type == RESPONSE_SOLICITED) {

processSolicited (p);

serial = p.readInt();

rr = findAndRemoveRequestFromList(serial);

rr.mResult.sendToTarget();

......

}

CallTracker.java->handleMessage (Message msg)

switch (msg.what) {

case EVENT_OPERATION_COMPLETE:

ar = (AsyncResult)msg.obj;

operationComplete();

cm.getCurrentCalls(lastRelevantPoll);

第二部分:unsolicited 消息從modem上報到java的流程。
c++部份

readerLoop()

line = readline();

processLine(line);

handleUnsolicited(line);

if (s_unsolHandler != NULL) {

s_unsolHandler (line1, line2);//實際執行的是void onUnsolicited (const char *s, const char *sms_pdu)

if (strStartsWith(s,"+CRING:")

|| strStartsWith(s,"RING")

|| strStartsWith(s,"NO CARRIER")

|| strStartsWith(s,"+CCWA")

)

RIL_onUnsolicitedResponse (RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, NULL, 0);

p.writeInt32 (RESPONSE_UNSOLICITED);

p.writeInt32 (unsolResponse);

ret = s_unsolResponses[unsolResponseIndex].responseFunction(p, data, datalen);

ret = sendResponse(p);

sendResponseRaw(p.data(), p.dataSize());

ret = blockingWrite(fd, (void *)&header, sizeof(header));

blockingWrite(fd, data, dataSize);

java部份

ril.java->RILReceiver.run()

for(;;)

{

...

length = readRilMessage(is, buffer);

p = Parcel.obtain();

p.unmarshall(buffer, 0, length);

p.setDataPosition(0);

processResponse(p);

processUnsolicited (p);

response = p.readInt();

switch(response) {

...

case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break;

...

}

switch(response) {

case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:

if (RILJ_LOGD) unsljLog(response);

mCallStateRegistrants

.notifyRegistrants(new AsyncResult(null, null, null));

...

}
第三部分、第四部分:貓相關的各種狀態的監聽和通知機制/通話相關的圖標變換的工作原理。
網絡狀態,edge,gprs圖標的處理
a、註冊監聽部分
==>SystemServer.java
init2()
Thread thr = new ServerThread();
thr.setName("android.server.ServerThread");
thr.start();
ServerThread.run()
com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);
sInstance = new StatusBarPolicy(context, service);
// phone_signal
mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
mPhoneData = IconData.makeIcon("phone_signal",
null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
mPhoneIcon = service.addIcon(mPhoneData, null);
// register for phone state notifications.
((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SERVICE_STATE
| PhoneStateListener.LISTEN_SIGNAL_STRENGTH
| PhoneStateListener.LISTEN_CALL_STATE
| PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
| PhoneStateListener.LISTEN_DATA_ACTIVITY);
//實際是調用的是TelephonyRegistry.listen,此listen函數會將Iphonestatelistener添加到對應的的handler數組中,到時來了事件會輪詢回調。

// data_connection
mDataData = IconData.makeIcon("data_connection",
null, com.android.internal.R.drawable.stat_sys_data_connected_g, 0, 0);
mDataIcon = service.addIcon(mDataData, null);
service.setIconVisibility(mDataIcon, false);

b、事件通知部分
==>PhoneFactory.java
makeDefaultPhones()
sPhoneNotifier = new DefaultPhoneNotifier();
useNewRIL(context);
phone = new GSMPhone(context, new RIL(context), sPhoneNotifier);

for example
==>DataConnectionTracker.java
notifyDefaultData(String reason)
phone.notifyDataConnection(reason);
mNotifier.notifyDataConnection(this, reason);
==>DefaultPhoneNotifier.java
mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()),
sender.isDataConnectivityPossible(), reason, sender.getActiveApn(),
sender.getInterfaceName(null));

第五部分:gprs撥號上網的通路原理。
上層java程序調用gprs流程:
=>PhoneApp.java
onCreate()
PhoneFactory.makeDefaultPhones(this);
phone = new GSMPhone(context, new SimulatedCommands(), sPhoneNotifier);
mDataConnection = new DataConnectionTracker (this);
createAllPdpList();//建立缺省pdpconnection
pdp = new PdpConnection(phone);
dataLink = new PppLink(phone.mDataConnection);
dataLink.setOnLinkChange(this, EVENT_LINK_STATE_CHANGED, null);

//某個條件觸發執行
trySetupData(String reason)
setupData(reason);
pdp = findFreePdp();
Message msg = obtainMessage();
msg.what = EVENT_DATA_SETUP_COMPLETE;
msg.obj = reason;
pdp.connect(apn, msg);
phone.mCM.setupDefaultPDP(apn.apn, apn.user, apn.password,
obtainMessage(EVENT_SETUP_PDP_DONE));

//收到EVENT_SETUP_PDP_DONE消息
=>pdpconnection.java
handleMessage()
case EVENT_SETUP_PDP_DONE:
dataLink.connect();//dataLink是pppLink.java
SystemService.start(SERVICE_PPPD_GPRS);//啓動pppd_grps服務
poll.what = EVENT_POLL_DATA_CONNECTION;
sendMessageDelayed(poll, POLL_SYSFS_MILLIS);//啓動輪詢,看是否成功連接gprs
checkPPP()//每隔5秒輪詢,看是否連接成功,或斷開
//如果已經連接
mLinkChangeRegistrant.notifyResult(LinkState.LINK_UP);

//執行到pdpconnection.handleMessage()
case EVENT_LINK_STATE_CHANGED
onLinkStateChanged(ls);
case LINK_UP:
notifySuccess(onConnectCompleted);
onCompleted.sendToTarget();

//執行dataConnectionTracker.java的handleMessage()
case EVENT_DATA_SETUP_COMPLETE
notifyDefaultData(reason);
setupDnsProperties();
setState(State.CONNECTED);
phone.notifyDataConnection(reason);
startNetStatPoll();
resetPollStats();
1、讀取發送出去的包數和接受到的包數
2、如果發送的數據包且沒有收到應答包數n大於等於看門狗追蹤的限定包數。
2.1、開始輪詢pdp context list,嘗試恢復網絡連接
2.2、如果輪詢24次後還沒有聯通網絡則停止網絡狀態輪詢,進行一次ping實驗。
2.2.1、如果ping成功則,重新進行網絡狀態輪詢,否則發送EVENT_START_RECOVERY事件。

// reset reconnect timer
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;

着重c++部分代碼的角度分析
=>DataConnectionTracker.java
trySetupData(String reason)
setupData(reason);
=>PdpConnection.java
pdp.connect(apn, msg);
=>RIL.JAVA
phone.mCM.setupDefaultPDP(apn.apn, apn.user, apn.password,
obtainMessage(EVENT_SETUP_PDP_DONE));
send(rr);
//send socket to RIL

//enter c++ layer
=>ril.cpp
processCommandsCallback (int fd, short flags, void *param)
processCommandBuffer(p_record, recordlen);
status = p.readInt32(&request);
pRI->pCI = &(s_commands[request]);
pRI->pCI->dispatchFunction(p, pRI);
dispatchStrings();
s_callbacks.onRequest(pRI->pCI->requestNumber, pStrings, datalen, pRI);
=>reference-ril.c
onRequest();
requestSetupDefaultPDP(data, datalen, t);
err = write_at_to_data_channel("ATD*99***1#",1);

//after a while.get "connect" from data channel,so need to send socket message to java layer.
p.writeInt32 (RESPONSE_SOLICITED);
p.writeInt32 (pRI->token);//the serial No in the request list.
errorOffset = p.dataPosition();
p.writeInt32 (e);
if (e == RIL_E_SUCCESS) {
/* process response on success */
ret = pRI->pCI->responseFunction(p, response, responselen);

/* if an error occurred, rewind and mark it */
if (ret != 0) {
p.setDataPosition(errorOffset);
p.writeInt32 (ret);
}
}
sendResponse(p);
sendResponseRaw(p.data(), p.dataSize());
ret = blockingWrite(fd, (void *)&header, sizeof(header));
blockingWrite(fd, data, dataSize);

=>RIL.JAVA
RILReceiver.run();
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p);
processSolicited (p);
serial = p.readInt();
error = p.readInt();
rr = findAndRemoveRequestFromList(serial);
ret = responseStrings(p);
if (rr.mResult != null) {
AsyncResult.forMessage(rr.mResult, ret, null);
rr.mResult.sendToTarget();
}

=>pdpConnection.java
handleMessage()
case EVENT_SETUP_PDP_DONE:
...
dataLink.connect();
=>pppLink.java
SystemProperties.set(PROPERTY_PPPD_EXIT_CODE, "");
SystemService.start(SERVICE_PPPD_GPRS);//啓動pppd_grps服務

poll.what = EVENT_POLL_DATA_CONNECTION;
sendMessageDelayed(poll, POLL_SYSFS_MILLIS);
dataConnection.state = State.CONNECTING;
handleMessage()
case EVENT_POLL_DATA_CONNECTION
checkPPP();
if (ArrayUtils.equals(mCheckPPPBuffer, UP_ASCII_STRING, UP_ASCII_STRING.length)
|| ArrayUtils.equals(mCheckPPPBuffer, UNKNOWN_ASCII_STRING,
UNKNOWN_ASCII_STRING.length)
&& dataConnection.state == State.CONNECTING)
if (mLinkChangeRegistrant != null) {
mLinkChangeRegistrant.notifyResult(LinkState.LINK_UP);
=>pdpConnection.java
handleMessage()
case EVENT_LINK_STATE_CHANGED:
DataLink.LinkState ls = (DataLink.LinkState) ar.result;
onLinkStateChanged(ls);
case LINK_UP:
notifySuccess(onConnectCompleted);
AsyncResult.forMessage(onCompleted);
onCompleted.sendToTarget();

=>DataConnectionTracker.java
handleMessage()
case EVENT_DATA_SETUP_COMPLETE:
...
SystemProperties.set("gsm.defaultpdpcontext.active", "true");
notifyDefaultData(reason);
setupDnsProperties();//設置dns,gw,我們的實現方式是在pppd中設置的,不用pppd撥號的適用。
setState(State.CONNECTED);
phone.notifyDataConnection(reason);
mNotifier.notifyDataConnection(this, reason);
=>DefaultPhoneNotifier.java
//mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));構造函數中初始化了mRegistry
mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()),
sender.isDataConnectivityPossible(), reason, sender.getActiveApn(),
sender.getInterfaceName(null));
startNetStatPoll();

}
第六部分:通話相關的語音通路切換原理、震動接口
6、語音通路

6.1、設置語音通路的路由

目前我們有兩處處理:

a、CallTracker.java中的

handlePollCalls()

檢測到+clcc返回的電話列表中有狀態爲DriverCall.State.ALERTING(表示撥打電話後,對方已經振鈴),此時需要設置語音通路爲MODE_IN_CALL

b、PhoneUtils.java中setAudioMode()函數

c、調用通路分析

AudioManager audioManager = (AudioManager) context.getSystemService

(Context.AUDIO_SERVICE);

audioManager.setMode(mode);

AudioManager.setMode(mode);

AudioService.setMode(mode);

AudioSystem.setMode(mode);(native function)

android_media_AudioSystem.cpp==>android_media_AudioSystem_setMode()

AudioSystem.cpp==>setMode()

const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();

binder = sm->getService(String16("media.audio_flinger"));

...

gAudioFlinger = interface_cast<IAudioFlinger>(binder);

...

return gAudioFlinger;

通過查找"media.audio_flinger"發現AudioFlinger.cpp==>instantiate()//Main_mediaserver.cpp中被實例化。

defaultServiceManager()->addService(String16("media.audio_flinger"), new AudioFlinger());

mAudioHardware = AudioHardwareInterface::create();

LOGV("Creating Vendor Specific AudioHardware");

hw = createAudioHardware();

return new AudioHardwareMarvell();

return af->setMode(mode);

AudioHardwareLittle.cpp==>setMode(mode)

doRouting();

enable_incall_headphone()//or others...

system("alsactl -f /etc/alsactl/asound.state_none restore");

system("alsactl -f /etc/alsactl/asound.state_headset_r_s restore");

6.2、來電播放振鈴,掛斷或接聽停止振鈴。

==>Phone.app

onCreate()

ringer = new Ringer(phone);

Vibrator mVibrator = new Vibrator();

mService = IHardwareService.Stub.asInterface(ServiceManager.getService("hardware"));

notifier = new CallNotifier(this, phone, ringer, mBtHandsfree);

mPhone.registerForIncomingRing(this, PHONE_INCOMING_RING, null);

mPhone.registerForPhoneStateChanged(this, PHONE_STATE_CHANGED, null);

mPhone.registerForDisconnect(this, PHONE_DISCONNECT, null);

...

case PHONE_INCOMING_RING:

mRinger.ring();

mHardwareService.setAttentionLight(true);

mVibratorThread.start();

while (mContinueVibrating) {

mVibrator.vibrate(VIBRATE_LENGTH);

SystemClock.sleep(VIBRATE_LENGTH + PAUSE_LENGTH);

}

...

makeLooper();

mRingHandler.sendEmptyMessage(PLAY_RING_ONCE);

...

case PLAY_RING_ONCE:

PhoneUtils.setAudioMode(mContext, AudioManager.MODE_RINGTONE);

r.play();

...

case PHONE_DISCONNECT:

case PHONE_STATE_CHANGED:

...

mRinger.stopRing();

Message msg = mRingHandler.obtainMessage(STOP_RING);

msg.obj = mRingtone;

mRingHandler.sendMessage(msg);

case STOP_RING:

r.stop();

getLooper().quit();

...

mVibrator.cancel();
第七部分:通話相關的notification服務
7、通話相關的notification服務。

7.1、NotificationMgr

==>PhoneApp.java

onCreate()

NotificationMgr.init(this)//NotificationMgr.java//此類主要負責電話通知的具體表現(通知和取消通知),未接圖標、通話中、藍牙激活中、保持中,靜音、免提等。封裝了簡單的瞬間顯示文本消息的功能。提供漫遊數據連接禁止的通知封裝和漫遊數據連接允許時取消通知

sMe = new NotificationMgr(context);

mNotificationMgr = (NotificationManager)

context.getSystemService(Context.NOTIFICATION_SERVICE);

mStatusBar = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); //主要用於顯示靜音和speaker狀態的圖表(在狀態條右邊顯示)

sMe.updateNotifications();//主要功能是:

1、查詢是否有未讀的未接聽電話,並顯示到狀態欄圖標,和通知列表

2、根據是否是電話狀態,更新狀態欄圖表和通知列表(可能是激活,藍牙,保持等)

7.2、CallNotifier

==>PhoneApp.java

onCreate()

notifier = new CallNotifier(this, phone, ringer, mBtHandsfree);//此類主要是監聽通話相關的事件,然後進行例如來電播放鈴聲,震動。掛斷、接聽停止振鈴等(調用Ringer類實現此功能),根據不同的狀態調用調用NotificationMgr進行具體的通知和取消通知。
第八部分: 通話相關的各種server
電話通信相關的服務:
(1)、從ServiceManager得到的:
a、wifiService
b、PhoneInterfaceManager
c、PhoneSubInfo
d、SimPhoneBookInterfaceManager
e、SimSmsInterfaceManager
f、TelephonyRegistry
g、NetStatService
h、ConnectivityService

(2)、從ApplicationContext得到的:
a、TelephonyManager

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/wenyuepeng2006/archive/2010/03/24/5407781.aspx

發表於 @ 2010年08月30日 09:24:00 | 評論( 1

發佈了1 篇原創文章 · 獲贊 4 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章