Android Radio Interface Layer

Android Radio Interface Layer

(2013-3-7 23:08)
 

1.简述

Radio Interface Layer,简称RIL,在手机上是Modem与AP通讯的桥梁,RIL扮演的角色非常重要,RIL被设计成能够可靠的高效的传输数据一个模块。以下是RIL在Modem与AP中的位置:

  

    Android RIL可以分成2个模块,一个部分RIL Demon(RILD),用于通过socketframework通讯;另一部分是第三方自己客制化的部分,在这里暂时称之为vendor RIL。之所以这样设计是因为不同的厂商使用的Modem不一样,而RIL又和Modem紧密联系,所以Android有把和Modem联系紧密的部分和公共部分剥离开,让不同的厂商可以客制化vendor RIL以适应厂商自己的ModemVendor RIL专门负责通过ATModem进行通讯。所以又可以细化称为:

 

了解RIL的用途之后,接下来看看RIL的文件结构。RIL有三个重要的目录: rild, librilreference-ril


2. 多路复用介绍

Androidril有开多个socket用于通讯,由于socket的部分函数为阻塞式的,如果某一个socket连接上发生阻塞,会影响到其他的socket连接的处理数据,所以androidril使用了select来实现同时接收socket的连接(多路复用模型)Selectlinux系统中实现多路复用的函数之一,它可以同时监听多个文件描述符(file descriptor, fd),有一个或多个fd上有数据可读、可写或者出现异常,该函数则返回并带回需要处理的socketfd以及个数。

A. 使用普通的无限循环轮询方式,一次接收与处理一个客户端:

  


B. 使用select实现多路复用,通过监听客户端的socket,同时服务。


 
3. Android Ril的事件及其处理方法


3.1 ril_event --- ril_event.cpp

过目一下事件的结构

 


int fd; // 文件描述符 pipe/socket,这就是要加到fdset集合中的玩意,select时会用到。

int index;//watch_table数组的索引,用于清空该数字对应的元素

Bool persist;//指示该ev在触发之后是否需要移出watch_table

Timeval timeout;//计时器触发的时间,微秒级

Ril_event_cb func;//事件触发时的回调函数

Void *param;//以上回调函数的参数

事件有两种:

(1)监听socketpipe的事件,若socketpipe可读、可写或者异常时会触发select返回。

(2)计时器事件,该类事件没有文件描述符,指定一个未来的时间,当时间到时select返回。

3.2 selectI/O多路复用

Android RIL使用Select来实现多路复用,一下是函数列表 


Select有五个参数

参数1int, 为所监听的fd数值的最大值+1

参数2fd_set类型的fd集合,此处参数为监听读的fd,当fd有数据需要读取时触发。

参数3fd_set类型的fd集合,此处为监听写的fd集合。

参数4fd_set类型的fd集合,此处为发生异常的fd的集合。

参数5:为timeval的时间,用于设定在规定时间到时select返回。当该值为NULL时,表示函数在有fd发生变化时才返回;为0,则立即返回。

返回值为发生可读、可写或者异常的fd的个数。

默认情况下,select可以同时监听1024fd,参见宏定义

#define FD_SETSIZE __FD_SETSIZE  //1024 --- time.h

fd的操作时通过以下系列函数:

#define FD_SET(fd,fdsetp) __FD_SET(fd,fdsetp)  //fd设定到fdset集合中

#define FD_CLR(fd,fdsetp) __FD_CLR(fd,fdsetp)  //fdset集合中清除fd

#define FD_ISSET(fd,fdsetp) __FD_ISSET(fd,fdsetp)  //判断fd是否在fdset集合中

#define FD_ZERO(fdsetp) __FD_ZERO(fdsetp)  //清空fdset集合

3.3 RIL事件处理解析

在有了上面的基础之后,接下来去挖一挖RIL是如何控制事件的。RIL事件相关的操作在文件ril_event.cpp中。关于RIL事件处理的话,涉及到参数:

static struct ril_event * watch_table[MAX_FD_EVENTS];//所需要监听的事件,都放到这个数组中,长度为8

static struct ril_event timer_list; //如果监听的是计时器,这加到这个链表中。上面有讲到这是ril_event个具有双向链表性质的结构体。提问为什么这里使用到链表,而watch_table却是用数组呢???

static struct ril_event pending_list; //这玩意也是一个链表,装载的是已经触发了的事件或计时器。

3.3.1让我们从rild.c开始寻找,

Rild.c编译成rild的模块,在init.rc中以一个service的形式被创建。


在该文件的mian函数中RIL开始初始化,做了三件事:

(1)创建事件循环,其实就是呼叫RIL_startEventLoop()

(2)打开vendor RIL的链接库,并呼叫其中RIL_Init函数

(3)RIL注册(暂且这么说吧,其实也没注册什么东西),呼叫RIL_register()

A. 先看第一件事:呼叫RIL_startEventLoop(ril.cpp)的函数:

这个函数很简单,就是单单创建一个线程(线程id=s_tid_dispatch),等到线程创建并运行了再返回。

extern "C" void

RIL_startEventLoop(void) {

    ...

    ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL); //创建eventLoop线程

    while (s_started == 0) {<==等待eventLoop创建并运行

        pthread_cond_wait(&s_startupCond, &s_startupMutex);

    }

    ...

}

再接着看eventLoop的代码,创建一个匿名管道,并把管道的read端加到事件列表里面(这里在添加事件是有一个callback,在事件触发是执行该callbackprocessWakeupCallback里面很简单,就是读取管道上的数据),然后就呼叫ril_event_loop()进入循环。

static void *

eventLoop(void *param) {

    int ret;

    int filedes[2];

    ril_event_init();

    pthread_mutex_lock(&s_startupMutex);

    s_started = 1;

    pthread_cond_broadcast(&s_startupCond);

    pthread_mutex_unlock(&s_startupMutex);

    ret = pipe(filedes); //创建一个无名管道

    if (ret < 0) {

        ALOGE("Error in pipe() errno:%d", errno);

        return NULL;

    }

    s_fdWakeupRead = filedes[0];  //管道读端

    s_fdWakeupWrite = filedes[1];  //管道写段

    fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);

    ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,

                processWakeupCallback, NULL); //使用管道的读端生成一个事件,这个事件的用途下面会介绍到。

    rilEventAddWakeup (&s_wakeupfd_event);

    // Only returns on error

    ril_event_loop();

    ALOGE ("error in event_loop_base errno:%d", errno);

    // kill self to restart on error

    kill(0, SIGKILL);

    return NULL;

}

Ril_event_loop就是一个for的无限循环,在这个for内部看到了前面讲到的select函数了,其实select值监测readfd_set,所要监听的fd都存放在全局变量readFds中,ptv决定select block的形态,要么设定时间block直到到期,要么无限block直到有监听fd上数据可读,当select返回后就会查找是哪个事件的fd的触发的,然后通过firePending()呼叫该事件的callback。注意这是循环的内部,也就是说每当select返回并执行其他动作之后,又会重新把readFds加到select中。

void ril_event_loop()

{

    int n;

    fd_set rfds;

    struct timeval tv;

    struct timeval * ptv;

    for (;;) {

        // make local copy of read fd_set

        memcpy(&rfds, &readFds, sizeof(fd_set)); //因为在select返回时,rfds中仅仅是触发的fd,没有触发的不在里面。也就是说rfds是会被修改的。

        if (-1 == calcNextTimeout(&tv)) {

            // no pending timers; block indefinitely

            dlog("~~~~ no timers; blocking indefinitely ~~~~");

            ptv = NULL;//无限时的block

        } else {

            dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);

            ptv = &tv;//设定block

        }

        printReadies(&rfds);

        n = select(nfds, &rfds, NULL, NULL, ptv); //监听可读的fd

        printReadies(&rfds);

        dlog("~~~~ %d events fired ~~~~", n);

        if (n < 0) {

            if (errno == EINTR) continue;

            ALOGE("ril_event: select error (%d)", errno);

            // bail?

            return;

        }

        // Check for timeouts

        processTimeouts(); //捞到期的事件

        // Check for read-ready

        processReadReadies(&rfds, n); //捞触发可读的事件

        // Fire away

        firePending(); //发射~ orz

    }

}

整个RIL_startEventLoop的时间序列式这个样子的(不好意思,画的糟糕了)


 


所以可以看到,要添加一个事件很简单,呼叫以下两个函数就可以了:

ril_event_set()

rilEventAddWakeup()

那刚才有说,只有select返回,才会重新把事件的fd加到select来监听。所以在rilEventAddWakeup中会有两个动作,一个是添加event(eventfd会写到readFds),然后另外一个是triggerEvLoop,这个triggerEvLoop其实就是往刚才在eventLoop中创建的匿名管道写入一个空格字符而已,目的是让select返回,因为select返回后会再把已经改变的readFds重新加入到select中。那么为什么往匿名管道写入数据,select就会返回呢?是因为在eventLoop中有把管道的read端也加入监听了。到这里估计大家应该明白了RIL event的工作机制了吧~

B. 接下来看vendor RIL中的RIL_Init

Vendor RILlib的形似存在,其中会有名称是RIL_Init的函数,该函数是Vendor RIL的入口,android默认的vendor RILreference-ril.so,里面包含了reference-ril.c这支文件。里面会有函数名为RIL_Init的函数,该函数创建一个tids_tid_mainloop,处理函数为mainLoop的线程。这里还会把RILD丢进来的设备名称保存在全局参数s_device_patch中。(这个函数太长了,只贴片段)


mainLoop的作用比较简单:

(1)打开s_device_patch设备,如果打开失败则会一直尝试直到成功为止。S_device_patch路径指向是串口设备,用于和modem进行数据交换,如果此设备没办法打开,那手机就嗝屁了~mainLoop里主要做了三件事

I. 打开串口设备

II. 注册onUnsolicited,创建线程readLoop读取modem丢过来的unsolicited事件。

III. 添加一个计时器,发送ATModem进行握手通讯。

static void *

mainLoop(void *param)

{

 ...

    for (;;) {

        fd = -1;

        while  (fd < 0) {

            if (s_port > 0) {

                ...

            } else if (s_device_socket) {

                ...

            } else if (s_device_path != NULL) {

                fd = open (s_device_path, O_RDWR); //打开串口

  ...

            }

   ...

        }

        s_closed = 0;

        ret = at_open(fd, onUnsolicited); //创建readLoop线程,用于读取从modme丢过来的unsolicited ev

   ...

        RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0); //添加一个计时事件,

...

    }

}


 
 


RIL_Init是有返回值的s_callbacks,这个返回值RIL_register会使用到,rildvendor ril的衔接就是依靠这个参数返回值类型是:

typedef struct {

    int version;        /* set to RIL_VERSION */

    RIL_RequestFunc onRequest; //上层下指令的处理函数

    RIL_RadioStateRequest onStateRequest; //查询当前radio状态

    RIL_Supports supports; //查询指定的request是否支持

    RIL_Cancel onCancel; //母鸡干嘛的

    RIL_GetVersion getVersion; //ril的版本

} RIL_RadioFunctions;

RIL_Inits_callbacks是这摸样,感兴趣的同学可以看看函数的实现:


到此RIL_Init就分析道这里。

C. RIL_register()

RIL_Register中的参数就是RIL_Init返回的参数,vendor RILRILD的沟通就是通过这货,直接通过function call方式。

extern "C" void

RIL_register (const RIL_RadioFunctions *callbacks) {//这里的参数就是上面提到的s_callbacks

    ...

    memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions)); //保存下来,备之后呼叫

   ...

    for (int i = 0; i < (int)NUM_ELEMS(s_commands); i++) { //验证cmd,一个request对应一个整数值,由0往后递增,为什么要验证呢? 因为以后RIL Java发是根据这个request numbercmd的,这个request number就是作为数组s_command的索引,用来呼叫这个cmd的分发和处理本cmd的函数。一会介绍他们之间的关系

        assert(i == s_commands[i].requestNumber);

    }

    for (int i = 0; i < (int)NUM_ELEMS(s_unsolResponses); i++) {//同上

        assert(i + RIL_UNSOL_RESPONSE_BASE

                == s_unsolResponses[i].requestNumber);

    }

    ...

    s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);//获取socketfdsocket貌似在创建ril-demon进程时随之创建的。

    if (s_fdListen < 0) {

        ALOGE("Failed to get socket '" SOCKET_NAME_RIL "'");

        exit(-1);

    }

ret = listen(s_fdListen, 4);

/* note: non-persistent so we can accept only one connection at a time */

//这里的socketRILDRIL Java的连接,RILD作为service端,RIL Java 作为client端。s_fdListen只是servicesocket,目的是监听RIL Javasocket发过来的connect,在listenCallbackAccept返回的socket才是RIL Javasocket,监听这个socket才能能知道RIL Java发过来的请求了。

ril_event_set (&s_listen_event, s_fdListen, false,listenCallback, NULL);

rilEventAddWakeup (&s_listen_event);

#if 1

    // start debug interface socket

//用于android debugsocket,可以通过该socketRILoptions中的socket连接,可以进行开关radio,开启一通通话,挂断一通通话之类的。

    s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG);

    if (s_fdDebug < 0) {

        ALOGE("Failed to get socket '" SOCKET_NAME_RIL_DEBUG "' errno:%d", errno);

        exit(-1);

    }

    ret = listen(s_fdDebug, 4);

    ril_event_set (&s_debug_event, s_fdDebug, true,debugCallback, NULL);

    rilEventAddWakeup (&s_debug_event);

#endif

}

 

上面有提到,RILD和上层RIL Java的沟通是通过socket进行通讯,因为RIL Java属于Java层次,之间的数据传递使用Parcel。当RIL Java要让RILD帮忙做事时,协定好它们之间之间的沟通方式,RIL Java把一个cmd号码发给RILD,其需要发送的参数写到Parcel,在通过socket发送到RILD,然后RILD根据cmd码,呼叫就其绑定不同的发送与接收函数。

RIL有两种cmd

(1)一种是一来一回的requestRIL JavarequestRILD处理后回给RIL Java结果。例如获取手机IMEI号,RIL JavarequestRILD,再到modemmodem处理完后依次返回到RIL Java

(2)另一种只回的unsolicitedRILDmodem主动给过来的事件上报给RIL Java。例如,手机信号强度的改变,网络时间到达之类的。这些cmd不需要AP做下任何请求,当modem发现其需要报告状态给AP时,就会主动的发出。

这两种cmd对应的cmd格式

typedef struct {

    int requestNumber; //request number

    void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI); //request发送的函数

    int(*responseFunction) (Parcel &p, void *response, size_t responselen); //request处理完成之后回应

} CommandInfo;


每一个cmd id(也就上request number)对应一个发送和接收函数,如果cmd有带Sting参数的话,那就会选用dispatchString, 如果RILD有返回值Int数组需要回传给RILJ的话,就选用responseInts。当然如果想传自己定义类型参数的话,可以自己写一个dispatchMyData,其实就是把Parcel里面的值,按照存放的顺序读出来发送出去。

typedef struct {

    int requestNumber; //同上

    int (*responseFunction) (Parcel &p, void *response, size_t responselen); //unsolicited 的处理函数

    WakeType wakeType; //??? 母鸡这个的意思

} UnsolResponseInfo;


4. RIL Java解析

RIJ Java比较简单,就是负责RILD和上层Telephony的衔接。在PhoneFactory.java中根据不同的PhoneType创建不同的Phone,例如GSMPhone, CDMAPhone或者CDMALTEPhone。不管是哪一种Phone,都需要一个CommandsInterface作为参数,这个就是RILJ,每种Phone都需要RILJ帮忙和RILD进行通讯。RILJ就是在创建某一种Phone之前创建的,在Phone进程中运行。RILJ的源文件就是RIL.java,这个文件中有两个类RILRILRequestRIL class继承与BaseCommandsCommandsInterface


RILRequest主要功能就是把要发送的request打包,Parcel存放需要送到RILD的数据。成员变数mSerial (Token)为一个整型,每生产一个RILRequest其值递增1,用于记录一个Request的编号。因为request不是这个一来一回后再发下一个,允许本request没有返回之前就可以发送发一个,这就需要一个编号记录每个request,用于辨别回来的结果对应到确定的request

RILSender发送requestRILDRIL的内部类。

RILReceiver接送从rild丢过来的数据,RIL的内部类。

RIL重要的成员mSender/mReceiver,就是以上两内部类的实力。另外一个是mRequestList,是一个ArrayList,记录发送到RILDrequest,在RILD处理完成之后回传数据时根据Token,取出对应的Request,来把结果分发到Framework的呼叫者。mSocket就是和RILD连接的socket

RIL在初始化初期,会创建两个线程mSendmReceiver,在mReceiver中会创建一个socket并和RILdsocket连接(这个时候eventLoop中的s_listen_event就会触发,透过accept就可以获取到这个RILJsocket)。当RILJ要往RILD发请求时,会透过RILRequest创建一个request,并把需要携带的参数写到Request中,然后透过mSend去发送。

下面以一副数据流程图来解说一下RILJ的内部运作:

 

5. RIL Request过程分析

这小节将通过一个具体的Request介绍RILJRILDflow。以 public void

setRadioPower(boolean on, Message result) 为实力来介绍。这个方法是来开关手机的Radio的,参数on需要携带到RILD去,result为 response时通知该方法的呼叫者。首先开一下函数具体实现:


在来看看函数的绑定发送与接收函数:


因为这个Request需要携带一个参数下去,所以发送函数选择dispatchInts(ril.cpp),没有参数需要返回,所以是responseVoid(ril.cpp)。刚好可以接着上小节的request部分,当把数据写到socket的时候,触发s_commands_event,执行processCommandsCallback(ril.cpp)

Request的数据依次是

TypeRIL_REQUEST_RADIO_POWER

Token:这个值是运行时的,具体是多少不管他。

RequestData:数组元素个数1,以及具体值。

在这之前需要介绍一下一个结构体:


其中的pCI就是

下面开始分析,因为RILJ Request过程上面小节已经介绍,这里就不再累赘,直接从RILD接数据开始:

a. 跑到processCommandBuffer函数,创建一个Parcel装载RILJ数据,读出TypeToken


b. 在来看dispatchInts里的是怎么处理的,读取出RILJ丢过来的数组长度,本case1,然后呼叫onRequest




c. Referencr-ril中的onRequest(),针对request type下不同的指令。


caserequestRIL_REQUEST_RADIO_POWER,看看这个case是怎么处理的

呼叫到了requestRadioPower函数,在该函数中依照RILJ传过来的参数想modem发送AT cmd。在modem处理完成之后返回,然后呼叫RIL_onRequestCompleate结束本次request
 


d. RIL_onRequestCompleate,完成数据装载,把结果回传给RILJ,完成本次onRequest


e. sendReponse, 把数据通过socket回传给RILJ,这个时候RILJmReceiver就读取这些数据了。之后的RILJ如何处理的情参考第四节的讲述。


6. RIL Unsolicited过程分析

RILD或者modem察觉有数据或状态变化时,需要把这些信息告诉framework,所以会丢出一个事件,这中就是Unsolicited eventFramework可以选择自己感兴趣的事件来监听,当被监听的事件触发时,就会被通知到。这里有一个疑问,因为modem只是打回一个AT名称给RILD,怎么知道这个AT是该让谁来处理呢?怎么知道哪些ATRequest下下去的AT的回应,哪些是ATmodem主动上报的呢?带着这个问题来分析一下unsolicited的处理过程。正如上面分析RIL_Init是所讲到的,mainLoop(reference-ril.c)中透过at_open来创建一个线程readLoop(atchannel.c)来读取modem丢回来的ATAt_open(atchannel.c)这个函数有两个参数,一个是串口设备的fd,另一个是一个函数指针,当modem有丢回来数据时,就是透过这个回调函数来处理滴。onUnsolicited是实现在reference-ril.c.


在进入正题之前先看一个结构体ATResponse


当透过request下的ATAT返回的结果就是存放在这个结构体里面。

其中p_intermediates存放的就是AT的字符串。

a. readLoop线程中无限循环的读取modem丢过来的AT,读到数据之后会然后呼叫processLine(atchannel.c)来处理AT

b. processLine中有个参数sp_response,当requestATmodem的时候,都会创建ATResponse这个结构,用于存放AT返回的结果;如果是modem主动丢回来的AT,那sp_response就是空的。就是透过这个来区分哪些ATmodem主动丢回来的,哪些是request回来的。


c. handleUnsolicited函数呼叫的s_unsolHandlerreference-ril.c中的onUnsolicited()


d. onUnsolicited(),根据AT返回的数值确定该谁来处理,比如遇到%CTZV那么其对应的unsolicited就是RIL_UNSOL_NITZ_TIME_RECEIVED


e. RIL_onUnsolicitedResponse中有些地方还不是很明白:( ~

这个函数做的事情(函数篇幅太大,就不全贴了):

确定这个unsolicited在数值在s_unsolResponses中的索引


先把Typeunsolicited写到Parcel中,然后很据索引取出这个unsolicited对应的response函数,其实就是把这个unsolicited带回的数据写到Parcel


最后就是呼叫sendResponse,把Parcel里的数据送给RILJ


余下的部分可以参考第四节。

PS: 本文提到的RILD == RIL Daemon, RILJ = RIL Java,在介绍RILJ是提到的RIL指得就是RILJ(因为RILJ的类名称为RIL)。估计会造成困扰,实在抱歉! 52RD的文字框编辑功能太烂了,没办法调间距,visio画的图上传上来全都是黑的


文章出自:http://www.52rd.com/Blog/Detail_RD.Blog_Lexucs_66161.html

展开阅读全文

Android User Interface的疑惑

08-08

[color=#FF6600]In an Android application, the user interface is built using View and ViewGroup objects. There are many types of views and view groups, each of which is a descendant of the View class. rnrnView objects are the basic units of user interface expression on the Android platform. The View class serves as the base for subclasses called "widgets," which offer fully implemented UI objects, like text fields and buttons. The ViewGroup class serves as the base for subclasses called "layouts," which offer different kinds of layout architecture, like linear, tabular and relative. rnrnA View object is a data structure whose properties store the layout parameters and content for a specific rectangular area of the screen. A View object handles its own measurement, layout, drawing, focus change, scrolling, and key/gesture interactions for the rectangular area of the screen in which it resides. As an object in the user interface, a View is also a point of interaction for the user and the receiver of the interaction events.[/color]rnrn以上一段是google官方对android user interface的介绍。rnrn首先看[color=#008000]In an Android application, the user interface is built using View and ViewGroup objects.[/color] rn我的理解是这样的: rnrn用户界面是构建在"View"和"ViewGroup"对象之上的。这里我的疑问是[color=#FF0000]:View 对象是何物?[/color]是不是可以这样理解:rnrn[code=Java]rnclass View()rnrn//propertiesrn....rn//methodsrn....rnrn[/code]rn这只是一个类,按照我对面向对象模糊的认识:应该由这个类生产一个对象rn[code=Java]rnView view = new View();rn[/code]rn这样,似乎就做出了一个view对象,不知可否这样理解呢?我觉得这样的理解肯定是不对的。换句话说:这里的“对象”二字该如何解释? rn对于"ViewGroup",有着同样的疑惑。rn[color=#008000]接着往下看:There are many types of views and view groups, each of which is a descendant of the View class.[/color]rn这句话说的是什么呢?View类的后代有很多类型的"views"和"view groups"?我无法理解。rn继续看:[color=#FF6600]View objects are the basic units of user interface expression on the Android platform. The View class serves as the base for subclasses called "widgets," which offer fully implemented UI objects, like text fields and buttons. The ViewGroup class serves as the base for subclasses called "layouts," which offer different kinds of layout architecture, like linear, tabular and relative. [/color]rn这一段的大概意思是说:View对象是Android平台用户界面的基本单元,“View”类是“widgets”类的基类,基本用来提供界面上的小对象,比如按钮之类的。"ViewGroup"类是"layouts"类的基类,是用来提供布局架构的。不知道对不对,还请各位斧正 rnrn接下来看最后一段: rnrn[color=#FF6600]A View object is a data structure whose properties store the layout parameters and content for a specific rectangular area of the screen. A View object handles its own measurement, layout, drawing, focus change, scrolling, and key/gesture interactions for the rectangular area of the screen in which it resides. As an object in the user interface, a View is also a point of interaction for the user and the receiver of the interaction events [/color]rnrn我的理解是这样的:“View” 对象是一数据结构:其"properties(性质)"存储“布局”参数。“and content for a specific rectangular area of the screen”这紧接着的一句,我不知是何意。 rn"A View object handles its own measurement,layout,drawing,focus change, scrolling ...." rn这句的意思是否是:"View"对象"handle"(掌管?这里的handle如何理解才合理?)着自己的很多性质“比如 layout,drawing,等等”,最后一句大概是说:View还是用户和事件交互的纽带。 rnrn还有一个问题:第二段明明说:"View Class"是负责界面上的小玩意的,比如按钮等等,而“ViewGroup Class”则是负责界面布局的。但第三段中说明了"View object"中的"property"存储了布局参数。这我就不理解了?那还用ViewGroup Class干嘛? rnrn深深感觉小弟理解能力有问题,看Android 的 SDK ,很难理解。 rn看Android的书,更是不好理解.... rn这里,请大家帮我看看....谢谢啦 论坛

Android User Interface问题

08-07

[color=#FF6600]In an Android application, the user interface is built using View and ViewGroup objects. There are many types of views and view groups, each of which is a descendant of the View class.rnrnView objects are the basic units of user interface expression on the Android platform. The View class serves as the base for subclasses called "widgets," which offer fully implemented UI objects, like text fields and buttons. The ViewGroup class serves as the base for subclasses called "layouts," which offer different kinds of layout architecture, like linear, tabular and relative.rnrnA View object is a data structure whose properties store the layout parameters and content for a specific rectangular area of the screen. A View object handles its own measurement, layout, drawing, focus change, scrolling, and key/gesture interactions for the rectangular area of the screen in which it resides. As an object in the user interface, a View is also a point of interaction for the user and the receiver of the interaction events.[/color]rnrn以上一段是google官方对android user interface的介绍。rnrn[color=#008000]首先看In an Android application, the user interface is built using View and ViewGroup objects.[/color]rn我的理解是这样的:rnrn用户界面是构建在"View"和"ViewGroup"对象之上的。[color=#FF0000]这里我的疑问是:View 对象是何物?[/color]是不是可以这样理解:rn[code=Java]rnclass View()rnrn//propertiesrn....rn//methodsrn....rnrn[/code]rn这只是一个类,按照我对面向对象模糊的认识:应该由这个类生产一个对象rn[code=Java]rnView view = new View();rn[/code]rn这样,似乎就做出了一个view对象,不知可否这样理解呢?我觉得这样的理解肯定是不对的。换句话说:这里的“对象”二字该如何解释?rn对于"ViewGroup",有着同样的疑惑。rn[color=#008000]接着往下看:There are many types of views and view groups, each of which is a descendant of the View class.rn[/color]rn这句话说的是什么呢?View类的后代有很多类型的"views"和"view groups"?我无法理解。rnrn继续看:[color=#FF9900]View objects are the basic units of user interface expression on the Android platform. The View class serves as the base for subclasses called "widgets," which offer fully implemented UI objects, like text fields and buttons. The ViewGroup class serves as the base for subclasses called "layouts," which offer different kinds of layout architecture, like linear, tabular and relative.[/color]rn这一段的大概意思是说:View对象是Android平台用户界面的基本单元,“View”类是“widgets”类的基类,基本用来提供界面上的小对象,比如按钮之类的。"ViewGroup"类是"layouts"类的基类,是用来提供布局架构的。不知道对不对,还请各位斧正rnrn接下来看最后一段:rnrn[color=#FF6600]A View object is a data structure whose properties store the layout parameters and content for a specific rectangular area of the screen. A View object handles its own measurement, layout, drawing, focus change, scrolling, and key/gesture interactions for the rectangular area of the screen in which it resides. As an object in the user interface, a View is also a point of interaction for the user and the receiver of the interaction events[/color]rnrn我的理解是这样的:“View” 对象是一数据结构:其"properties(性质)"存储“布局”参数。“and content for a specific rectangular area of the screen”这紧接着的一句,我不知是何意。rn"A View object handles its own measurement,layout,drawing,focus change, scrolling ...."rn这句的意思是否是:"View"对象"handle"(掌管?这里的handle如何理解才合理?)着自己的很多性质“比如 layout,drawing,等等”,最后一句大概是说:View还是用户和事件交互的纽带。rnrn还有一个问题:第二段明明说:"View Class"是负责界面上的小玩意的,比如按钮等等,而“ViewGroup Class”则是负责界面布局的。但第三段中说明了"View object"中的"property"存储了布局参数。这我就不理解了?那还用ViewGroup Class干嘛?rnrn深深感觉小弟理解能力有问题,看Android 的 SDK ,很难理解。rn看Android的书,更是不好理解....rn这里,请大家帮我看看....谢谢啦rn 论坛

没有更多推荐了,返回首页