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 还定义了一些

android phone 模块分析相关推荐

  1. Android GNSS 模块分析(四)HAL 层

    紧接着上一篇(Android GNSS 模块分析(三)JNI 层),继续来分析下 Android GNSS HAL 层的功能,本篇准备先介绍下 HIDL 层的封装.至于后面的 HAL 层的功能,由于使 ...

  2. Android GNSS 模块分析(五)NMEA 协议

    紧接着上一篇<Android GNSS 模块分析(四)HAL 层>,本篇简述下导航硬件设备与卫星导航系统之间的通信协议. NMEA 协议 简介: NMEA(National Marine ...

  3. android wifi模块分析

    声明:本文纯属网上资料收集,版权归源作者所有,转载时请标明为转载文章 现在对android平台的wifi模块了解了一段时间,现在做一些简要总结,以便以后查阅和与修正,上正文. [Wifi模块学习流程] ...

  4. android camera fragment,Android Camera 模块分析(三)

    第三部分 Camera的主要实现分析 3.1 JAVA程序部分 在packages/apps/Camera/src/com/android/camera/ 目录的Camera.java文件中,包含了对 ...

  5. android挂载usb设备,android usb挂载分析---MountService启动

    在android usb挂载分析----vold启动,我们的vold模块已经启动了,通信的机制也已经建立起来了,接下来我们分析一下MountService的启动,也就是我们FrameWork层的启动, ...

  6. android recovery 模块知识需求汇总

    关于android recovery的一个目录,后续继续更新,未完待续...... Android recovery模块介绍: (一)android为什么需要recovery升级? (二)androi ...

  7. Android架构实例分析之编写hello驱动的HAL层代码

    Android架构实例分析之编写hello驱动的HAL层代码 摘要: HAL层中文名称又叫硬件抽象层,可以理解我Linux驱动的应用层.本文实现了一个简单的hello HAL的代码,衔接hello驱动 ...

  8. Android 系统(74)--Android重启原因分析

    Android重启原因分析 重启原因分类 1.上层造成重启 system_server被杀 watchdog重启 重要线程阻塞 2.kernel造成重启 空指针 非法地址 3.kernel watch ...

  9. Android 系统性能优化(43)---Android OOM案例分析

    Android OOM案例分析 在Android(Java)开发中,基本都会遇到java.lang.OutOfMemoryError(本文简称OOM),这种错误解决起来相对于一般的Exception或 ...

  10. Android源码分析(一)-----如何快速掌握Android编译文件

    一 : Android.mk文件概述 主要向编译系统指定相应的编译规则.会被解析一次或多次.因此尽量减少源码中声明变量,因为这些变量可能会被多次定义从而影响到后面的解析.这个文件的语法会把源代码组织成 ...

最新文章

  1. ae中合成设置的快捷键_AE项目工作流程讲解及项目与合成设置
  2. Python 中文分词(结巴分词)
  3. Android中ListView的使用以及使用适配器设置数据源
  4. RxSwift之UI控件UISlider与UIStepper扩展的使用
  5. 45 WM配置-作业-库存盘点-清除差异(库存管理接口)
  6. 运行loadrunner自带的webtour项目无法启动
  7. android 开发种子文件,IT之家学院:如何制作种子文件和磁力链接
  8. 智能配电房综合监控系统的探讨
  9. 在VS中生成汇编语言程序(.asm文件)的方法
  10. Windows环境下ODAC安装
  11. 使用pyenv和virtualenv搭建python虚拟环境实践总结
  12. mysql 错误问题_Mysql常见的几个错误问题及解决方法
  13. getc()、gets()、getchar()、scanf()的区别
  14. 【爆笑】句经典流行俏皮语【爆笑】[
  15. Java具有哪些语言特点
  16. Python 画星星图案
  17. 语音识别(ASR)论文优选:Icassp 2022 M2MeT方案总结
  18. 如何在网站中添加音乐
  19. excel表格如何打斜杠
  20. 移动CM201-2机顶盒系统设置apk

热门文章

  1. poi操作PPT读取模板流,生成新PPT文件
  2. 怎么查看php配置信息,Wampserver查看php配置信息
  3. 对当前计算机应用的理解论文,计算机应用的现状、主要对策及今后发展方向
  4. 才在enom注册了10多个域名,就有人想盗我邮箱
  5. 数据中台全面分析总结
  6. 视频网站套上CDN是什么效果?
  7. 使用tcpdump抓取DNS包
  8. Flutter生命周期
  9. 一个读者大佬精心总结的阿里、腾讯、宇宙条大厂 Offer 面经和硬核面试攻略
  10. Adversarial Machine Learning 经典算法解读(FGSM, DeepFool)