(二十八)Android GDI之Surface&Canvas
最后更新于:2022-04-01 11:41:16
# Surface&Canvas
Canvas为在画布的意思。Android上层的作图几乎都通过Canvas实例来完成,其实Canvas更多是一种接口的包装。drawPaints ,drawPoints,drawRect,drawBitmap ...
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2cba92c.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276524262qcXC.gif)
### 1 Canvas与Surface之间本质关系
对于本节,我们不去研究Skia图形引擎本身,我们需要了解的我们的所做的图形到底放置到了那个地方,并且这个Canvas如何与Surface连接在一起的。
Canvas(Java)在C++Native层有一个Native Canvas的C++对象所对应。
lockCanvas()@java
Surface_lockCanvas@android_view_Surface.cpp
SurfaceControl->new Surface(control) @Surface.cpp
Surface: lock操作:
[](http://hi.csdn.net/attachment/201006/14/0_1276524263FdtA.gif)[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2ccb054.gif "image")](http://hi.csdn.net/attachment/201006/14/0_1276524264rrrr.gif)
GraphicBuffer :lock
getBufferMapper().lock GraphicBufferMapper ::lock
mAllocMod->lockgralloc_module_t::lock
通过SurfaceLock可取得Surface(mLockedBuffe)所对应的图形缓冲区地址。
(1) 建立与SkCanvas连接的位图设备,而该位图使用上面取得的图形缓冲区地址做自己的位图内存。
(2) 设置SkCanvas的作图目标设备为该位图。
通过该过程就建立起了SurfaceControl与Canvas之间的联系。
[](http://hi.csdn.net/attachment/201006/14/0_1276524266rhNg.gif)[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2cdc00c.gif "image")](http://hi.csdn.net/attachment/201006/14/0_1276524267jPRF.gif)
### 2 View:OnDraw的本源
不是使用OpenGL绘制时,Android在View属性发生变化,新建View时,或者Z-order发生变化时,需要对系统屏幕上的View重新绘制,此时我们的View会执行OnDraw(canvas),这个根源在哪里呢?
~~~
ViewRoot.Java
performTraversals(..)
…
draw()
canvas = surface.lockCanvas(dirty);
…
mView.draw(canvas);
draw(cavas)@view.java
background.draw(canvas);
onDraw(cavas)
dispatchDraw(cavas)
onDrawScrolbars(cavas)
surface.unlockCanvasAndPost(canvas);
~~~
(二十七)Android GDI 之SurfaceFlinger之动态结构示意图
最后更新于:2022-04-01 11:41:13
# SurfaceFlinger对象建立过程示意
### 1 SurfaceSession的建立
客户端请求建立Surface时,首先在要与SurfaceFlinger建立一个Session,然后再Session上建立一个Connection通过概念返回Bclient对象。WindowManagerService在添加第一个窗口前会检查SurfaceSession是否建立,如何没有建立,将会新建立一个实例来代表与SurfaceFlinger的一个连接。
new SurfaceSession()@windowAddedLocked() @WindowManagerService.java。
SurfaceSession的建立过程大部分是在C++ Native空间中完成的,表现在SurfaceSession的初始化函数:init()本地函数上。从下面的初始化函数可以看到:
Init()[SurfaceSession_init@android_view_Surface.cpp](#)
new SurfaceComposerClient
SurfaceSession在C++Native空间建立一个SurfaceComposerClient实例。而该实例的建立实现了如下的与SurfaceFlinger通讯基础:
(1)建立了代理SurfaceFlinger服务的代理服务端
(2)建立了IsurfaceFlingerClient连接,在SurfaceFlinger端建立了对应的Client,并将BClient返回给WindowManagerService。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2c37366.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276524101W877.gif)
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2c47a27.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276524104Q1xk.gif)
### 2 Surface的建立
在WindowManagerService中WindowState类中,我们知道每个主窗口子啊需要是都需要建立一个Surface与之对应。win.createSurfaceLocked()@relayoutWindow
Surface.java
Init()Surface_init(….,session,pid,dpy,w,h,format)@android_view_Surface.cpp
SurfaceControl surface(client->createSurface
在mClient的连接上:建立ISurface接口:
M_Client->greateSurface(...)@
Bclient ::createSurface(mId...)@SurfaceFlinger.cpp
mFlinger->createSurface(clientid....)
createNormalSurfaceLocked
createNormalSurfaceLocked:建立一个Layer分配显示内存
createPushBuffersSurfaceLocked:建立一个LayBuffer但是不分配显示内存。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2c660ae.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276524110bgH4.gif)
(二十六)Android GDI之SurfaceFlinger
最后更新于:2022-04-01 11:41:11
### Android GDI之SurfaceFlinger
SurfaceFinger按英文翻译过来就是Surface投递者。SufaceFlinger的构成并不是太复杂,复杂的是他的客户端建构。SufaceFlinger主要功能是:
1) 将Layers (Surfaces) 内容的刷新到屏幕上
2) 维持Layer的Zorder序列,并对Layer最终输出做出裁剪计算。
3) 响应Client要求,创建Layer与客户端的Surface建立连接
4) 接收Client要求,修改Layer属性(输出大小,Alpha等设定)
但是作为投递者的实际意义,我们首先需要知道的是如何投递,投掷物,投递路线,投递目的地。
### 1 SurfaceFlinger的基本组成框架
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2a8abcb.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276518584xvSo.gif)
SurfaceFlinger管理对象为:
mClientsMap:管理客户端与服务端的连接。
ISurface,IsurfaceComposer:AIDL调用接口实例
mLayerMap:服务端的Surface的管理对象。
mCurrentState.layersSortedByZ :以Surface的Z-order序列排列的Layer数组。
graphicPlane 缓冲区输出管理
OpenGL ES:图形计算,图像合成等图形库。
gralloc.xxx.so这是个跟平台相关的图形缓冲区管理器。
pmem Device:提供共享内存,在这里只是在gralloc.xxx.so可见,在上层被gralloc.xxx.so抽象了。
### 2 SurfaceFinger Client和服务端对象关系图
[[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2aa243c.gif "image")](http://hi.csdn.net/attachment/201006/14/0_12765192776tBE.gif)](http://hi.csdn.net/attachment/201006/14/0_12765192744dUQ.gif)
Client端与SurfaceFlinger连接图:
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2b32428.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276518598A6OZ.gif)
Client对象:一般的在客户端都是通过SurfaceComposerClient来跟SurfaceFlinger打交道。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2b44483.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276518601fTft.gif)
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2b5b4e7.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276518603bAHB.gif)
### 3 主要对象说明
#### 3.1 DisplayHardware &FrameBuffer
首先SurfaceFlinger需要操作到屏幕,需要建立一个屏幕硬件缓冲区管理框架。Android在设计支持时,考虑多个屏幕的情况,引入了graphicPlane的概念。在SurfaceFlinger上有一个graphicPlane数组,每一个graphicPlane对象都对应一个DisplayHardware.在当前的Android(2.1)版本的设计中,系统支持一个graphicPlane,所以也就支持一个DisplayHardware。
SurfaceFlinger,Hardware硬件缓冲区的数据结构关系图。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2b713b8.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276518606owJM.gif)
#### 3.2 Layer
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2b89104.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276518608eft1.gif)
method:setBuffer 在SurfaceFlinger端建立显示缓冲区。这里的缓冲区是指的HW性质的,PMEM设备文件映射的内存。
1) layer的绘制
~~~
void Layer::onDraw(const Region& clip) const
{
int index = mFrontBufferIndex;
GLuint textureName = mTextures[index].name;
…
drawWithOpenGL(clip, mTextures[index]);
}
~~~
#### 3.2 mCurrentState.layersSortedByZ
以Surface的Z-order序列排列的LayerBase数组,该数组是层显示遮挡的依据。在每个层计算自己的可见区域时,从Z-order 顶层开始计算,是考虑到遮挡区域的裁减,自己之前层的可见区域就是自己的不可见区域。而绘制Layer时,则从Z-order底层开始绘制,这个考虑到透明层的叠加。
### 4 SurfaceFlinger的运行框架
我们从前面的章节<Android Service>的基本原理可以知道,SurfaceFlinger的运行框架存在于:threadLoop,他是SurfaceFlinger的主循环体。SurfaceFlinger在进入主体循环之前会首先运行:SurfaceFlinger::readyToRun()。
#### 4.1 SurfaceFlinger::readyToRun()
(1)建立GraphicPanle
(2)建立FrameBufferHardware(确定输出目标)
初始化:OpenGL ES
建立兼容的mainSurface.利用eglCreateWindowSurface。
建立OpenGL ES进程上下文。
建立主Surface(OpenGL ES)。 DisplayHardware的Init()@DisplayHardware.cpp函数对OpenGL做了初始化,并创建立主Surface。为什么叫主Surface,因为所有的Layer在绘制时,都需要先绘制在这个主Surface上,最后系统才将主Surface的内容”投掷”到真正的屏幕上。
(3) 主Surface的绑定
1)在DisplayHandware初始完毕后,hw.makeCurrent()将主Surface,OpenGL ES进程上下文绑定到SurfaceFlinger的上下文中,
2)之后所有的SurfaceFlinger进程中使用EGL的所有的操作目的地都是[mSurface@DisplayHardware](#)。
这样,在OpenGL绘制图形时,主Surface被记录在进程的上下文中,所以看不到显示的主Surfce相关参数的传递。下面是Layer-Draw,Hardware.flip的动作示意图:
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2b9bc43.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276518609tWF6.gif)
#### 4.2 ThreadLoop
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2bb19a5.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_12765186106SH3.gif)
(1)handleTransaction(…):主要计算每个Layer有无属性修改,如果有修改着内用需要重画。
(2)handlePageFlip()
computeVisibleRegions:根据Z-Order序列计算每个Layer的可见区域和被覆盖区域。裁剪输出范围计算-
在生成裁剪区域的时候,根据Z_order依次,每个Layer在计算自己在屏幕的可显示区域时,需要经历如下步骤:
1)以自己的W,H给出自己初始的可见区域
2)减去自己上面窗口所覆盖的区域
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2bc3d8d.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276518612eyG3.gif)
在绘制时,Layer将根据自己的可将区域做相应的区域数据Copy。
(3)handleRepaint()
composeSurfaces(需要刷新区域):
根据每个Layer的可见区域与需要刷新区域的交集区域从Z-Order序列从底部开始绘制到主Surface上。
(4)postFramebuffer()
(DisplayHardware)hw.flip(mInvalidRegion);
eglSwapBuffers(display,mSurface) :将mSruface投递到屏幕。
### 5 总结
现在SurfaceFlinger干的事情利用下面的示意图表示出来:
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2bd7cb1.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276518615Bgfw.gif)
(二十五)Android GDI之共享缓冲区机制
最后更新于:2022-04-01 11:41:09
# Androird GDI之共享缓冲区机制
### 1 native_handle_t对private_handle_t 的包裹
private_handle_t是gralloc.so使用的本地缓冲区私有的数据结构,而Native_handle_t是上层抽象的可以在进程间传递的数据结构。在客户端是如何还原所传递的数据结构呢?首先看看native_handle_t对private_handle_t的抽象包装。
[](http://hi.csdn.net/attachment/201006/14/0_12765041031rsn.gif)[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a299cb8d.gif "image")](http://hi.csdn.net/attachment/201006/14/0_1276504107ekdp.gif)
numFds= sNumFds=1;
numInts= sNumInts=8;
这个是Parcel中描述句柄的抽象模式。实际上是指的Native_handle所指向句柄对象的具体内容:
numFds=1表示有一个文件句柄:fd/
numInts= 8表示后面跟了8个INT型的数据:magic,flags,size,offset,base,lockState,writeOwner,pid;
由于在上层系统不要关心buffer_handle_t中data的具体内容。在进程间传递buffer_handle_t(native_handle_t)句柄是其实是将这个句柄内容传递到Client端。在客户端通过Binder读取readNativeHandle @Parcel.cpp新生成一个native_handle。
~~~
native_handle* Parcel::readNativeHandle() const
{
…
native_handle* h = native_handle_create(numFds, numInts);
for (int i=0 ; err==NO_ERROR && i
h->data[i] = dup(readFileDescriptor());
if (h->data[i]
}
err = read(h->data + numFds, sizeof(int)*numInts);
….
return h;
}
~~~
这里需要提到的是为在构造客户端的native_handle时,对于对方传递过来的文件句柄的处理。由于不是在同一个进程中,所以需要dup(…)一下为客户端使用。这样就将Native_handle句柄中的,客户端感兴趣的从服务端复制过来。这样就将Private_native_t的数据:magic,flags,size,offset,base,lockState,writeOwner,pid;复制到了客户端。
客户端利用这个新的Native_buffer被Mapper传回到gralloc.xxx.so中,获取到native_handle关联的共享缓冲区映射地址,从而获取到了该缓冲区的控制权,达到了客服端和Server间的内存共享。从SurfaceFlinger来看就是作图区域的共享。
### 2 Graphic Mapper是干什么的?
服务端(SurfaceFlinger)分配了一段内存作为Surface的作图缓冲,客户端怎样在这个作图缓冲区上工作呢?这个就是Mapper(GraphicBufferMapper)y要干的事情。两个进程间如何共享内存,如何获取到共享内存?Mapper就是干这个得。需要利用到两个信息:共享缓冲区设备句柄,分配时的偏移量。Mapper利用这样的原理:
客户端只有lock,unlock,实质上就是mmap和ummap的操作。对于同样一个共享缓冲区,偏移量才是总要的,起始地址不重要。实际上他们操作了同一物理地址的内存块。我们在上面讨论了native_handle_t对private_handle_t 的包裹过程,从中知道服务端给客户端传递了什么东西。
进程1在共享内存设备上预分配了8M的内存。以后所有的分配都是在这个8M的空间进行。对以该文件设备来讲,8M物理内存提交后,就实实在在的占用了8M内存。每个每个进程都可以同个该内存设备共享该8M的内存,他们使用的工具就会mmap。由于在mmap都是用0开始获取映射地址,所以所有的客户端进程都是有了同一个物理其实地址,所以此时偏移量和size就可以标识一段内存。而这个偏移量和size是个数值,从服务进程传递到客户端直接就可以使用。
[](http://hi.csdn.net/attachment/201006/14/0_1276504116JHZ8.gif)[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a29b0ae6.gif "image")](http://hi.csdn.net/attachment/201006/14/0_1276504121th7n.gif)
### 3 GraphicBuffer(缓冲区代理对象)
~~~
typedef struct android_native_buffer_t
{
struct android_native_base_t common;
int width;
int height;
int stride;
int format;
int usage;
…
buffer_handle_t handle;
…
} android_native_buffer_t;
~~~
关系图表:
GraphicBuffer :EGLNativeBase :android_native_buffer_t
GraphicBuffer(parcel &)建立本地的GraphicBuffer的数据native_buffer_t。在通过接收对方的传递的native_buffer_t 构建GraphicBuffer。我们来看看在客户端Surface::lock获取操作缓冲区的函数调用:
~~~
Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
{int Surface::dequeueBuffer(android_native_buffer_t**buffer)(Surface)
{status_t Surface::getBufferLocked(int index, int usage)
{
sp buffer = s->requestBuffer(index, usage);
{
virtual sp requestBuffer(int bufferIdx, int usage)
{ remote()->transact(REQUEST_BUFFER, data, &reply);
sp buffer = new GraphicBuffer(reply);
~~~
Surface::Lock建立一个在Client端建立了一个新的GraphicBuffer 对象,该对象通过(1)描述的原理将SurfaceFlinger的buffer_handle_t相关数据构成新的客户端buffer_handle_t数据。在客户端的Surface对象就可以使用GraphicMapper对客户端buffer_handle_t进行mmap从而获取到共享缓冲区的开始地址了。
### 4 总结
Android在该节使用了共享内存的方式来管理与显示相关的缓冲区,他设计成了两层,上层是缓冲区管理的代理机构GraphicBuffer,及其相关的native_buffer_t,下层是具体的缓冲区的分配管理及其缓冲区本身。上层的对象是可以在经常间通过Binder传递的,而在进程间并不是传递缓冲区本身,而是使用mmap来获取指向共同物理内存的映射地址。
(二十四)Android GDI之显示缓冲管理
最后更新于:2022-04-01 11:41:07
### Android GDI之屏幕设备管理-动态链接库
万丈高楼从地起,从最根源的硬件帧缓冲区开始。我们知道显示FrameBuffer在系统中就是一段内存,GDI的工作就是把需要输出的内容放入到该段内存的某个位置。我们从基本的点(像素点)和基本的缓冲区操作开始。
### 1 基本知识
### 1.1点的格式
对于不同的LCD来讲,FrameBuffer的二进制格式不一样,并且可以分为两部分:
####1)点的格式:通常将Depth,即表示多少位表示一个点。
1位表示一个点
2位表示一个点
16位表示一个点
32位表示一个点(Alpha通道)
####2) 点内格式:RGB分量分布表示。
例如对于我们常见的16位表示一个点
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2802efe.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_127649370468hY.gif)
### 1.2.格式之间的转换
所以屏幕输出实际上是一个值映射的关系。我们可以有如下的点格式转换,
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a28170b6.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276493706k5xC.gif)
源格式可能来自单色位图和彩色位图,对于具体的目标机来讲,我们的目标格式可能就是一种,例如16位(5/6/5)格式。其实就只存在一种格式的转换,即从目标格式都是16位格式。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a282a40c.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276493707JNOB.gif)
但是,在设计GDI时,基本要求有一个可移植性好,所以我们还是必须考虑对于不同点格式LCD之间的转换操作。所以在GDI的驱动程序中涉及到如下几类主要操作:
区域操作(Blit):我们在显示缓冲区上做的最多的操作就是区块搬运。由此,很多的应用处理器使用了硬件图形加速器来完成区域搬运:blit.从我们的主要操作的对象来看,可以分为两个方向:
1)内存区域到屏幕区域
2)屏幕区域到屏幕区域
3)屏幕区域到内存区域
4)内存区域到内存区域
在这里我们需要特别提出的是,由于在Linux不同进程之间的内存不能自由的访问,使得我们的每个Android应用对于内存区域和屏幕缓冲区的使用变得很复杂。在Android的设计中,在屏幕缓冲区和显示内存缓冲区的管理分类很多的层次,最上层的对象是可以在进程间自由传递,但是对于缓冲区内容则使用共享内存的机制。
基于以上的基础知识,我们可以知道:
(1)代码中Config及其Format的意义所在了。也就理解了兼容性的意义:采用同硬件相同的点的描述对象
(2)所有屏幕上图形的移动都是显示缓冲区搬运的结果。
### 1.2图形加速器
应用处理器都可能带有图形加速器,对于不同的应用处理器对其图形加速器可能有不同的处理方式,对于2D加速来讲,都可归结为Blit。多为数据的搬运,放大缩小,旋转等。
### 2 Android的缓冲区抽象定义
不同的硬件有不同的硬件图形加速设备和缓冲内存实现方法。Android Gralloc动态库抽象的任务就是消除不同的设备之间的差别,在上层看来都是同样的方法和对象。在Moudle层隐藏缓冲区操作细节。Android使用了动态链接库gralloc.xxx.so,来完成底层细节的封装。
2.1 本地定义@hardware/libhandware/modules/gralloc
每个动态链接库都是用相同名称的调用接口:
1)硬件图形加速器的抽象:BlitEngine,CopyBit的加速操作。
2)硬件FrameBuffer内存管理
3)共享缓存管理
从数据关系上我们来考察..动态链接库的抽象行为:在层次:[Hardware.c@hardware/libhardware](#) 中对动态链接库中的内容作了全新的包装。/system/lib/hw/gralloc.xxx.so动态库文件。从文件Gralloc.h(handware/libhardware/include/hardware)是抽象的结果:hw_get_module从gralloc.xxx.so提取了HAL_MODULE_INFO_SYM(SYM变量)
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a283cb22.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276493710BnSi.gif)
从展露在外部的数据结构,[我们在@Gralloc.cpp](#)看到到了这样的布局:
~~~
static struct hw_module_methods_t gralloc_module_methods = {
open: gralloc_device_open
};
> struct private_module_t HAL_MODULE_INFO_SYM = {
base: {
common: {
tag: HARDWARE_MODULE_TAG,
…
id: GRALLOC_HARDWARE_MODULE_ID,
name: "Graphics Memory Allocator Module",
author: "The Android Open Source Project",
methods: &gralloc_module_methods
},
registerBuffer: gralloc_register_buffer,
unregisterBuffer: gralloc_unregister_buffer,
lock: gralloc_lock,
unlock: gralloc_unlock,
},
framebuffer: 0,
flags: 0,
numBuffers: 0,
bufferMask: 0,
…
};
~~~
我们建立了什么对象来支撑缓冲区的操作?
buffer_handle_t:外部接口。
methods.open,registerBuffer,unregisterBuffer,lock,unlock
下面是外部接口和内部对象的结构关系,该类型的结构充分利用C Struct的数据排列特性:基本结构体放置在最前面,本地私有放置在后面,满足了抽象的需要。
typedef const native_handle* buffer_handle_t;
private_module_t HAL_MODULE_INFO_SYM 向往暴露的动态链接库接口,通过该接口,我们直接可以使用该对象。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a285b080.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276493716565n.gif)
看不清楚上面图,可以偏一下头横着看:
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a289ffdc.gif "image")
](http://hi.csdn.net/attachment/201006/14/0_1276493727LN9N.gif)
几个接口函数的解释:
(1)fb_post
对于帧缓冲区实际地址并不需要向上层报告,所有的操作都是通过fb_post了完成。
fp_post的任务就是将一个Buffer的内容传递到硬件缓冲区。其实现方式有两种:
![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a28d00ed.gif "image")
(方式1)无需拷贝动作,是把Framebuffer的后buffer切为前buffer,然后通过IOCTRL机制告诉FB驱动切换DMA源地地址。这个实现方式的前提是Linux内核必须分配至少两个缓冲区大小的物理内存和实现切换的ioctrol,这个实现快速切换。
(方式2)利用Copy的方式。不修改内核,则在适配层利用从拷贝的方式进行,但是这个是费时了。
(2)gralloc的主要功能是要完成:
1)打开屏幕设备 "/dev/fb0",,并映射硬件显示缓冲区。
2)提供分配共享显示缓存的接口
3)提供BiltEngine接口(完成硬件加速器的包装)
(3)gralloc_alloc输出buffer_handle_t句柄。
这个句柄是共享的基本依据,其基本原理在后面的章节有详细描述。
### 3 总结
总结一下,/system/lib/hw/gralloc.xxx.so是跟硬件体系相关的一个动态链接库,也可以叫做Android的硬件抽象层。他实现了Android的硬件抽象接口标准,提供显示内存的分配机制和CopyBit等的加速实现。而如何具体实现这些功能,则跟硬件平台的配备有关系,所以我们看到了对于与不同的硬件架构,有不同的配置关系。
(二十三)Andoird GDI之基本原理及其总体框架
最后更新于:2022-04-01 11:41:04
# Android GDI基本框架
在Android中所涉及的概念和代码最多,最繁杂的就是GDI相关的代码了。但是本质从抽象上来讲,这么多的代码和框架就干了一件事情:对显示缓冲区的操作和管理。
GDI主要管理图形图像的输出,从整体方向上来看,GDI可以被认为是一个物理屏幕使用的管理器。因为在实际的产品中,我们需要在物理屏幕上输出不同的窗口,而每个窗口认为自己独占屏幕的使用,对所有窗口输出,应用程序不会关心物理屏幕是否被别的窗口占用,而只是关心自己在本窗口的输出,至于输出是否能在屏幕上看见,则需要GDI来管理。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a270c0cb.gif "image")](http://hi.csdn.net/attachment/201006/13/0_127644050310jf.gif)
从最上层到最底层的数据流的分析可以看到实际上GDI在上层为GUI提供一个抽象的概念,就好像操作系统中的文件系统所提供文件,目录等抽象概念一样,GDI输出抽象成了文本,画笔,位图操作等设备无关的操作,让应用程序员只需要面对逻辑的设备上下文进行输出操作,而不要涉及到具体输出设备,以及输出边界的管理。GDI负责将文本、线条、位图等概念对象映射到具体的物理设备,所以GDI的在大体方向上可以分为以下几大要素:
画布
字体
文本输出
绘画对象
位图输出
Android的GDI系统
Android的GDI系统所涉及到概念太多,加之使用了OpenGL使得Android的层次和代码很繁杂。但是我们对于Android的GDI系统需要了解的方面不是他的静态的代码关系,而是动态的对象关系,在逻辑运行的架构上理解GDI。我们首先还是需要从代码结构开始我们的理解。
Frameworks/Libs/Surfaceflinger
Frameworks/base/core/jni/android_view_Surface.cpp
Frameworks/base/core/java/android/view/surface.java
Frameworks/base/Graphics:绘图接口
Frameworks/Libs/Ui
External/Skia
其中External/Skia是一个C++的2D图形引擎库,Android的2D绘制系统都是建立在该基础之上.Skia完成了:文本输出,位图,点,线,图像解码等功能。
我在这里给出Android GDI的基本框架示意图。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a271c4d9.gif "image")](http://hi.csdn.net/attachment/201006/13/0_1276440506aSNE.gif)
对于上面的GDI架构图我们只是一个大概的了解,我们有太多的问题需要解决,有太多的疑问需要得到答案,我就一直在想,为什么设计者有提出如此众多的概念,这个概念的背景是什么?他要管理什么,他要抽象什么?从前面知道,Android的整个设计理念就是无边界化,他是如何穿透Linux进程这个鸿沟来达到无边界的?Surface,Canvas, Layer,LayerBase, NativeBuffer,SurfaceFlinger,SurfaceFlingerClient这些到底是一个什么东西?如何管理,传递的是什么?创建的是什么?这些都是抽象的概念,绘画的终极的缓冲区到底是如何管理的?缓冲区到底在哪里?
我们还是看看做终极的,最本质的设计概念,在从这些概念出发,来探讨这些概念的形成过程,是否有必要去生成写概念。SurfaceFlinger本质上干什么的?SurfaceFlinger的确就是这个意义:应用程序通过SurfaceFlinger将自己的“Surface”投掷到屏幕缓冲区。至于如何投掷的,我们将会在后面详细描述。
(二十二)Android应用框架之Activity
最后更新于:2022-04-01 11:41:02
# 3 Activity设计框架
### 3.1 外特性空间的Activity
我们先来看看,Android应用开发人员接触的外特性空间中的Activity,对于AMS来讲,这个Activity就是客服端的Activity。应用程序员在建立Android应用时,构建Activity的子类就是Andoid外特性空间展现的接口。我们可以从下面的简单的例子描述看看Activity,到底如何建立的。
~~~
DemoActivity extend Activity
{
onCreate
onResume
onPause
onStop
}
~~~
在Android的外特性空间(SDK)中,Android应用程序员根本不知道进程是什么时候起来的,系统消息是如何传递过来的。这个DemoActivity是如何实例化的呢?并且该Activity是托管在哪个进程的呢?本节的分析将给出答案。
我们从ActivityThread中可以看到在应用进程中的Activity都被放置在mActivities中。
[![image_thumb3](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a25bf03b.gif "image_thumb3")](#)
这些ActivityRecord记录了应用进程中,程序员建立的Activity子类的实例,我们称之为外特性空间的Activity。这些Activity类实例是放在应用程序端进行实际交互的Activity,而为了管理这些Activity,AMS内核中还有一个影子Activity,被称为HistoryRecord。
3.2 Activity与HistoryRecord的关系
在整个系统中,Activity实际上有两个实体。一个在应用进程中跟应用程序员打交道的Activity,一个是在AMS的中具有管理功能的History Record。应用进程中的Activity都登记ActivityThread实例中的mActivity数组中,而在AM端,HistroytRecord实例放置在mHistroy栈中。mHistory栈是Android管理Activity的场所,放置在栈顶的就是User看到的处于活动状态的Activity。
Activity与HistrotyRecord的关系图可以表示如下:
[![image_thumb6](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a25d3511.gif "image_thumb6")](#)
Activity的内核实体是依靠在ProcessRecord的成员变量中,通过ProcessRecord我们可以访问到所有的属于该Process的Activity。而在ProcessRecord记录了与应用进程之间的联系:IActivtityThread接口。通过该接口,可以访问到所对应的Activity的方法。在Launch Activity时,AMS将对应的HistoryRecord作为token传递到客服端和客服端的Activity建立联系。在AMS中Activity状态变化时,将通过该联系找到客服端的Activity,从而将消息或者动作传递应用程序面对的接口:xxxActivity。
### 3.3 Actvity的Launch过程
1)发起请求startActivity(intent)
2)Activity Service Manager接收到请求执行StartActivity函数。
建立:HistoryRecord实例r.
将r 加入到mHistory顶。
(3)通过app.thread.scheduleLaunchActvity( [app,r)@ActivityThread.java](#)
(4)在App应用中建立新的ActivityRecord。
(5)建立新的Activity对象并放入到ActivityRecord中。
(6)将ActivityRecord加入到mActivites@ActivityThread
(7)发起Activity.onCreate(..),,该onCreate就是在你的应用程序XXXActivity中的onCreate。
[![image_thumb10](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a25edd5b.gif "image_thumb10")](#)
### 3.4 Activity的Resume
(1)Activity什么时候被Resume
[![image_thumb13](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2613625.gif "image_thumb13")](#)
(2)Rusume的过程
通过该过程的研究我们会进一步的了解到AMS与应用进程的交互过程。
在AMS端,满足resume条件都会调用:Resume的核心函数:[resumeTopActivityLocked@ActivityManagerService](#)
XXX当前栈顶的HistroyRecord
1)窗口切换:隐藏前一个Activity的窗口,
2)更新LRUList,(LRUList是淘汰应用程序的依据之一)
3) XXX.app.thread.scheduleResumeActivity(XXX,
isNextTransitionForward());
4)completeResumeLocked
setFocusedActivityLocked
mFocusActivity=xxx //此时焦点Actvitiy切换了。
WM.setFocusedApp(xxx,
mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
在应用程序端:
(5)scheduleResumeActivity
handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
ActivityRecord r = performResumeActivity(token, clearHide);
ActivityRecord r = mActivities.get(token);
r.activity.performResume()
performResume
整个Resume的过程如下:
[![image_thumb16](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a262dd95.gif "image_thumb16")](#)
(二十一)Android应用框架之AndroidApplication
最后更新于:2022-04-01 11:41:00
# Android Application
Android提供给开发程序员的概念空间中Application只是一个松散的表征概念,没有多少实质上的表征。在Android实际空间中看不到实际意义上的应用程序的概念,即使有一个叫Application的类,这个也就是个应用程序上下文状态,是一个极度弱化的概念。Application只是一个空间范畴的概念,Application就是Activity,Service之类的组件上下文描述。Application并不是Android的核心概念,而Activity才是Android的核心概念。
从Android的SDK文档中,我们知道一般情况Android应用程序是由以下四种组件构造而成的:Activity,Broadcast Intent Receiver,服务(Service),内容提供器(Content Provider)。我们可以使用下面的图来表示一下Android的概念空间。这些组件依附于应用程序中,应用程序并不会一开始就建立起来,而是在这些组件建立起来后,需要运行时,才开始建立应用程序对象。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a24d5c1b.gif "image")](http://hi.csdn.net/attachment/201005/24/0_1274715036pPGc.gif)
### 2.1应用进程名称
为什么要从应用进程名称开始?作为内核研究,我们还是回到问题的最本质处:不管Activity,Service等组件如何设计和运行,它要提供服务,就必须要依附在Linux的进程上,建立消息循环,组件才能够真正的运作。Activity实例是如何Hosting在Linux进程上的?这个是我们首先想要弄明白的。
我们在[](http://androidappdocs.appspot.com/guide/topics/manifest/manifest-element.html)的项目中看到android:[process](#)="string"这个定义。
[allowClearUserData](#)=["true" | "false"]
android:[allowTaskReparenting](#)=["true" | "false"]
android:[backupAgent](#)="string"
…
android:[label](#)="string resource"
android:[manageSpaceActivity](#)="string"
android:[name](#)="string"
android:[permission](#)="string"
android:[persistent](#)=["true" | "false"]
android:[process](#)="string"
android:[restoreAnyVersion](#)=["true" | "false"]
android:[taskAffinity](#)="string"
android:[theme](#)="resource or theme" >
. . .
在SDK用已经描述的很清楚到了。
android:process
The name of a process where all components of the application should run. Each component can override this default by setting its own process attribute.
By default, Android creates a process for an application when the first of its components needs to run. All components then run in that process. The name of the default process matches the package name set by the [](http://androidappdocs.appspot.com/guide/topics/manifest/manifest-element.html) element.
By setting this attribute to a process name that's shared with another application, you can arrange for components of both applications to run in the same process — but only if the two applications also share a user ID and be signed with the same certificate.
为什么要提出这么一个定义?android:process名称。
默认状态下,Activity Manager Service在应用程序的第一个组件需要运行时将会为应用程序建立一个进程,而这个进程的名字就是android:process=”string”所指定,缺省的是应用程序包的名字。该进程一旦建立,后面的该应用的组件都将运行在该进程中,他们绑定的根据就是这个Android:Process指定的名称,因为在他们都在同一个应用程序包里,也就具有了同样的进程名字,于是他们都托管在了同一进程中。组件将通过ClassLoader从Package中获取到应用程序的信息。
在建立Actvitiy时,如果在应用进程端没有应用对象,系统在该过程中利用makeApplication建立一个Application对象,实例化"android.app.Application",建立一个应用程序上下文完成例如资源,package等信息管理。
### 2.2 ActivityThread运行框架
在分析中,我们可以看到真正对应应用进程的不是Application而是ActivityThread。我们从实际的应用堆栈可以看到:
~~~
NaiveStart.main()
ZygoteInit.main
ZygoteInit$MethodAndArgsCall.run
Method.Invoke
method.invokeNative
ActivityThread.main()
Looper.loop()
....
~~~
每个应用程序都以ActivityThread.main()为入口进入到消息循环处理。对于一个进程来讲,我们需要这个闭合的处理框架。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a24ea0ae.gif "image")](http://hi.csdn.net/attachment/201005/24/0_1274715037oaa7.gif)
ActivitiyThread是应用程序概念空间的重要概念,他建立了应用进程运行的框架,并提供了一个IActivityThread接口作为与Activity Manager Service的通讯接口.通过该接口AMS可以将Activity的状态变化传递到客户端的Activity对象。
### 2.3 ActivitiyThread的建立
为了叙述的方便我将Actvitiy Manager Service简写成AMS。
在AMS中关于应用程序的概念是ProcessRecord,请求都是从Activity,Service…等开始的,在Activity需要Resume时,此时如果与Activity相关的应用进程没有起来,AM则启动应用进程。
AMS与应用进程的绑定分为两个部分,第一部分就是AM建立应用进程,第二部分就是应用进程Attach到AM,与AM建立通讯通道。
1)创建建立进程:startProcessLocked(processName,Appinfo.uid)。该函数在StartSecificActivityLocked等调用。
(1)建立ProcessRecord对象app,并将该对象添加到mProcessNames中。应用对象在mProcessNames中使用应用名字和uid来标识自己。如果在同一个Package中的Activity,如果都使用默认设置,那么这些Activity都会托管在同一个进程中,这是因为他们在带的ApplicationInfo中的ProcessName都是一样的。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2506ffb.gif "image")](http://hi.csdn.net/attachment/201005/24/0_1274715038X0aT.gif)
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2518aa1.gif "image")](http://hi.csdn.net/attachment/201005/24/0_12747150398uU3.gif)
mPidsSelfLocked数组记录了PID,这个将会在应用进程跑起来后,将自己Attach到AM时,根据pid找到自己的前世:ProcessRecord.
2)android.app.ActivityThread进程启动
Android.app.ActivityThread进程建立后,将跳入到ActivityThread的main函数开始运行,进入消息循环。[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a252d9fa.gif "image")](http://hi.csdn.net/attachment/201005/24/0_1274715040RR9z.gif)
应用进程使用thread.attach()发起AMS的AttachApplicationLocked调用,并传递 ActvitiyThread对象和CallingPid。AttachApplicationLocked将根据CallingPid在mPidsSelfLocked找到对应的ProcessRecord实例app,将ActvitiyThread放置app.thread中。这样应用进程和AMS建立起来双向连接。AM可以使用AIDL接口,通过app.thread可以访问应用进程的对象。
应用程序通过ActivityThread提供的框架,建立消息循环Looper和Handler。从前面的相关章节我们知道有Looper和Handler,整个系统就可以运作了。
为了更为系统的了解应用程序的建立时序及其涉及到数据操作,我给出了应用进程的建立过程示意图:
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a253dd97.gif "image")](http://hi.csdn.net/attachment/201005/24/0_12747150430h53.gif)
(二十)Android应用程序框架之无边界设计意图
最后更新于:2022-04-01 11:40:58
# Android应用程序框架
### 1 无边界设计理念
Android的应用框架的外特性空间的描述在SDK文档([http://androidappdocs.appspot.com/guide/topics/fundamentals.html#acttask](#))有十分清楚的描述,Android应用的基本概念,组件生命周期等等有详细的描述。在外特性空间中,Android提供了Activity,Service,Broadcast receivers,Content Provider,Intent,task等概念,我在这里不讨论这些概念定义,因为SDK文档已经讲得够详细。
在阅读SDK文档和研究Activity这个概念时,我感觉到了在Android中若隐若现的Android自由无边界这个设计意图。Android的应用只是一个虚的概念,并没有实际的入口,这个不像Window平台上的应用程序的概念,Android更多的是提供组件(Components)的概念。为什么要虚化应用的概念?我想这个虚化就是自由无边界设计意图的直接体现。突出请求和服务,突出组件个体,弱化边界,系统的各个组件可以自由的无边界的交流,服务请求者直接发出请求,不论这个对象在何处和属于谁的,组件是自由独立的个体,一个应用程序可以直接请求使用其他的应用的的组件,这个是Android应用框架设计的核心理念,其他的一切都是在为这个核心理念服务。
让程序员忽略应用的概念,甚至彻底的抛弃进程这样的概念,程序员看到的就是一个一个的组件,应用程序员利用这些组件来架构成一个所谓的应用,那么设计者首先要考虑的是什么呢?我想应该是一个抽象的应用模型,在这个模型下产生概念和接口。
我们知道MicroSoft提出了Application,Windows的概念,有前景应用(Foreground Application)概念,MicroSoft的应用模型中用户交互则完全交给了Window,各种界面的呈现都是属于这个应用的是孤立的,应用程序之间的各个构成对象不能相互访问,最多提供一个进程间通讯机制,那个也是应用程序层面的。虽然Microsoft后来也提出了组件,分布式组件等概念,但是这些不是根植在Windows系统中,而Android则是彻底的组件化思想构建,一开始的应用程序概念就是Activity,Service,Broadcast receivers,Content Provider,Intent,Task。这些概念体现了一个人机交互的模型本质:
界面呈现
发起请求,响应请求
内容交互
消息接收处理
Activity是Android应用的核心概念,简而言之Activity为用户交互管理者,有一个可视界面呈现,而Service跟Activity的区别是他在后台运行,没有界面呈现。而Intent的意义是意图,他在Android的概念空间中,代表消息,这个消息代表了请求的意图。
Activity可以到处存在,提供服务,消除空间差别,Activity是一个独立的个体,更能表现面向对象的实质。这个个体需要接受另外的个体的消息,可以随时发起对另外一个个体的请求。个体是自由的,Android中你可以开始一个Activity,但是没有权利消灭一个Activity,这是个体权利的体现,个体的消灭是由系统决定的,这个就是Android中Activity蕴含的人文意义。
(十九)电话系统之GSMCallTacker
最后更新于:2022-04-01 11:40:55
# Android电话系统之GSMCallTracker
通话连接管理
GSMCallTracker在本质上是一个Handler。
[![wps_clip_image-65](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2379092.gif "wps_clip_image-65")
](http://hi.csdn.net/attachment/201005/14/0_1273850076CzIU.gif)
GSMCallTracker是Android的通话管理层。GSMCallTracker建立了ConnectionList来管理现行的通话连接,并向上层提供电话调用接口。
[![wps_clip_image-1](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a238bd2d.gif "wps_clip_image-1")
](http://hi.csdn.net/attachment/201005/14/0_1273850080TJjj.gif)
在GSMCallTracker中维护着通话列表:connections。顺序记录了正连接上的通话,这些通话包括:ACTIVE,DIALING,ALERTING,HOLDING,INCOMING,WAITING等状态的连接。GSMCallTracker将这些连接分为了三类别进行管理:
RingingCall: INCOMING ,WAITING
ForegourndCall: ACTIVE, DIALING ,ALERTING
BackgroundCall: HOLDING
上层函数通过getRingCall(),getForegrouandCall()等来获得电话系统中特定通话连接。
为了管理电话状态,GSMCallTracker在构造时就将自己登记到了电话状态变化通知表中。RIL-Java一收到电话状态变化的通知,就会使用EVENT_CALL_STATE_CHANGE通知到GSMCallTacker
在一般的实现中,我们的通话Call Table是通过AT+CLCC查询到的,CPI可以通知到电话的改变,但是CPI在各个Modem的实现中差别比较大,所以参考设计都没有用到CPI这样的电话连接改变通知,而是使用最为传统的CLCC查询CALL TABLE。在GSMTracker中使用connections来管理Android电话系统中的通话连接。每次电话状态发生变化是GSMTracker就会使用CLCC查询来更新connections内容,如果内容有发生变化,则向上层发起电话状态改变的通知。
### 1 RIL-JAVA中发起电话连接列表操作
在RIL-JAVA中涉及到CurrentCallList查询的有以下几个操作:
(1)hangup
(2)dial
(3)acceptCall
(4)rejectCall
在GSMcallTracker在发起这些调用的时候都有一个共同的ResultMessage构造函数:obtainCompleteMessage()。obtainCompleteMessage()实际上是调用:
obtainCompleteMessage(EVENT_OPERATION_COMPLETE)
这就意味着在这些电话操作后,GSMCallTracker会收到EVENT_OPERATION_COMPLETE消息,于是我们将目光转移到handleMessage()@GSMCallTracker的EVENT_OPERATION_COMPLETE事件处理:operationComplete@GSMCallTracker。
operationComplete()操作会使用cm.getCurrentCalls(lastRelevantPoll)调用,向RILD发起RIL_REQUEST_GET_CURRENT_CALLS调用,这个最终就是向Modem发起AT+CLCC,获取到真正的电话列表。
### 2 在RILD中,引起getCurrentCalls调用
(1)在RILD中,收到URC消息:
+CRING
RING
NO CARRIER
+CCWA
将会使用RIL_onUnsolicitedResponse( RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED),主动向ril-java上报RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息。
(2) 在处理requestCurrentCalls时,使用CLCC查询通话连接(CALL TABLE)后,如何发现有call Table不为空则开启一个定时器,主动上报RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息,直到没有电话连接为止。
在RIL-Java层收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED这个URC,并利用mCallStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null))来通知电话状态的变化,此时GSMTracker会接收到EVENT_CALL_STATE_CHANGE消息,并使用
pollCallsWhenSafe()-> cm.getCurrentCalls(lastRelevantPoll);
来发起查询,并更新JAVA层的电话列表。
### 3 handlePollCalls电话列表刷新
首先我们来看看是什么引起了handlePollCalls的调用。
上面的1,2分析了,Android电话系统中所有引起电话连接列表更新的条件及其处理。他们共同的调用了cm.getCurrentCalls(lastRelevantPoll) 来完成电话列表的获取。
lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT)
我们这里就从可以看到获取到的电话列表Result使用handlePollCalls进行了处理。Result实际上是一个DriverCall列表,handlePollCalls的工作就是将当前电话列表与RIL-Java的电话列表对比,使用DriverCall列表更新CallTracker的电话列表connections,并向上传递电话状态改变的通知。
(十八)Android电话系统之RIL-Java
最后更新于:2022-04-01 11:40:53
# Android RIL-Java
[![wps_clip_image-0](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a21b5251.gif "wps_clip_image-0")](http://hi.csdn.net/attachment/201005/14/0_1273849467WMhm.gif)
RIL-Java在本质上就是一个RIL代理,起到一个转发的作用,是Android Java概念空间中的电话系统的起点。在RIL-D的分析中,我们知道RILD建立了一个侦听套接口,等待RIL-Java的连接。一旦连接成功,RIL-JAVA就可发起一个请求,并等待应答,并将结构发送到目标处理对象。在RIL-Java中,这个请求称为RILRequest。为了直观起见,我还是不厌其烦的给出RIL-Java的框架图。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a21c7283.gif "image")](http://hi.csdn.net/attachment/201005/14/0_1273849470BuxR.gif)
RIL-Java的大框架包含了四个方面:
Receiver,Sender,CommandInterface,异步通知机制
### (1) Command Interface
在ril.java源代码中,我们可以看到RIL-JAVA对象提供了如下的Command Interface:
…
getlccCardStatus
getCurrrentCalls
dial
acceptCall
rejectCall
sendDTMF
sendSMS
setupDataCall
setRadioPower
…
为什么要定义这些接口呢?这函数接口不是凭空捏造出来的,这些都是电话的基本功能的描述,是对Modem AT指令的提炼抽象。大多数Modem都是根据通讯协议提供接口,我们如果不熟悉通讯协议,请参阅3GPP的相关文档,以及自己使用的Modem的SPEC说明。
V.25ter AT Commands
3GPP 07.07 AT Comamnds-General commands
3GPP 07.07 AT Comamnds-Call Control commans
3GPP 07.07 AT Comamnds-Network Service related commands
3GPP 07.07 AT Comamnds-MT control and status command
3GPP 07.07 AT Comamnds-GPRS Commands
3GPP 07.07 Mobile Termination Errors
3GPP 07.05 SMS AT Commands
### (2)Receiver
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a21dc487.gif "image")](http://hi.csdn.net/attachment/201005/14/0_1273849487pQG9.gif)
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a22001e9.gif "image")](http://hi.csdn.net/attachment/201005/14/0_127384949370fT.gif)
Receiver连接到RILD的服务套接口,接收读取RILD传递过来的Response Parcel。Response分为两种类型,一种是URC,一种是命令应答。对于URC将会直接分发到通知注册表中的Handler。而命令应答则通过Receiver的异步通知机制传递到命令的发送者进行相应处理。
### (3)Sender
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2215e1c.gif "image")](http://hi.csdn.net/attachment/201005/14/0_1273849499m0zQ.gif)
Sender应该分为两部分架构,
上层函数调用Command Interface将请求消息发送到Sender的架构。
Sender接收到EVENT_SEND消息后,将请求发送到RILD的架构。
### (4)异步应答框架
对于异步应答来讲,命令的发起者发送后,并不等待应答就返回,应答的回应是异步的,处理结果通过消息的方式返回。站在设计者的角度思考如何设计合适的框架来完成异步通讯的功能呢?对于异步系统我们首先应该考虑的是如何标识命令和结果,让命令和结果有一个对应关系,还有命令没有响应了,如何管理命令超时?让我们来看看Android设计者如何完成这些工作。
Android设计者利用了Result Message 和RILRequest对象来完成Request和Result的对应对于关系。在上层做调用的时候生成Result Message对象传递到ril_java,并在Modem有应答后,通过Result Message对象带回结果。如何保证该应答是该RILRequest的呢?Android设计者还提供了一个Token(令牌)的概念。在源代码中RILRequest的mSerail就用作了Token。Token用来唯一标识每次发送的请求,并且Token将被传递到RILD,RILD在组装应答是将Token写入,并传回到ril-java,ril-java根据该Token找到相应的Request对象。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a222b81a.gif "image")](http://hi.csdn.net/attachment/201005/14/0_1273849509k0fS.gif)
#### (4.1)RIL命令的发送模式
协议的真正实现是在rild中,RIL-JAVA更多的是一个抽象和代理,我们在研究源代码的过程中就会体会到到RIL-JAVA中的命令函数都有一个共同的框架。
SendXxxCmd(传入参数Data,传出参数result){
组合RILRequest(请求号,result,mSerail)
Data->RR
send(RILRequest): Message
}
1)RILRequest
[![wps_clip_image-230](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a225b018.gif "wps_clip_image-230")
](http://hi.csdn.net/attachment/201005/14/0_1273849515vVv7.gif)
请求号:
request将传递到RILD用以标识命令,request代表某个功能。例如拨叫的request号为:RIL_REQUEST_DIAL。在libs/telephony/ril_commands.h有定义。RILRequest.obtain@RILRequest根据命令请求号,传入参数Result Message,mSerail构造了一个RILRequest。Result Message将带回应答信息回到命令的发起者。
mSerail:
Android使用了一个RILRequest对象池来管理Andoird RILRequest。mSerail是一个递增的变量,用来唯一标识一个RILRequest。在发送时正是用了该变量为Token,在rild层看到的token就是该mSerail。
EVENT_END:
[EVENT_END@handleMessage@RILSender@RIL.java](#)
[![wps_clip_image-730](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a226d29d.gif "wps_clip_image-730")
](http://hi.csdn.net/attachment/201005/14/0_1273849518Q7af.gif)[![wps_clip_image-731](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a22821d8.gif "wps_clip_image-731")
](http://hi.csdn.net/attachment/201005/14/0_1273849521q2nK.gif)
2)发送步骤:
第一步:
生成RILRequest,此时将生成m_Serial(请求的Token)并将请求号,数据,及其Result Message 对象填入到RILRequest中
第二步:
使用send将RILRequest打包到EVENT_SEND消息中发送到到RIL Sender Handler,
第三步:
RilSender 接收到EVENT_SEND消息,将RILRequest通过套接口发送到RILD,同时将RILRequest保存在mRequest中以便应答消息的返回。
(4.2) 接收模式
第一步:分析接收到的Parcel,根据类型不同进行处理。
[![wps_clip_image-1016](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2294143.gif "wps_clip_image-1016")
](http://hi.csdn.net/attachment/201005/14/0_1273849525557D.gif)
第二步:根据数据中的Token(mSerail),反查mRequest,找到对应的请求信息。
第三步:将是数据转换成结果数据。
第四步:将结果放在RequestMessage中发回到请求的发起者。
### 4.3)详细的GSMCallTracker,RIL-Java函数对照
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a22a75bd.gif "image")](http://hi.csdn.net/attachment/201005/14/0_12738495343nGL.gif)
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a22d8fdd.gif "image")](http://hi.csdn.net/attachment/201005/14/0_1273849561CKSQ.gif)
(十七)电话系统之rilD
最后更新于:2022-04-01 11:40:51
# Android电话系统之-rild
Rild是Init进程启动的一个本地服务,这个本地服务并没有使用Binder之类的通讯手段,而是采用了socket通讯这种方式。RIL(Radio Interface Layer)
Android给出了一个ril实现框架。由于Android开发者使用的Modem是不一样的,各种指令格式,初始化序列都可能不一样,GSM和CDMA就差别更大了,所以为了消除这些差别,Android设计者将ril做了一个抽象,使用一个虚拟电话的概念。这个虚拟电话对象就是GSMPhone(CDMAPhone),Phon对象所提供的功能协议,以及要求下层的支撑环境都有一个统一的描述,这个底层描述的实现就是靠RIL来完成适配。
Andoid将RIL层分为两个代码空间:RILD管理框架,AT相关的xxxril.so动态链接库。将RIL独立成一个动态链接库的好处就是Android系统适应不同的Modem,不同的Mode可以有一个独立的Ril与之对应。从这个层面上看,Rild更多是一个管理框架。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2051d7e.gif "image")](http://hi.csdn.net/attachment/201005/10/0_1273502394d94y.gif)
而ril是具体的AT指令合成者和应答解析者。从最基本的功能来讲,ril建立了一个侦听Socket,等待客户端的连接,然后从该连接上读取RIL-Java成传递来的命令并转化成AT指令发送到Modem。并等待Modem的回应,然后将结果通过套接口传回到Ril-Java层。下图是Ril-D的基本框架:
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a20620d3.gif "image")](http://hi.csdn.net/attachment/201005/10/0_1273502396C9X1.gif)
下面的数据流传递描述图表描述了RIL-JAVA层发出一个电话指令的5 步曲。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2076bf2.gif "image")](http://hi.csdn.net/attachment/201005/10/0_1273502399hSwI.gif)
在AT通讯的过程中有两类响应:一种是请求后给出应答,一种是通知类,即为不请自来的,例如短信通知达到,我们称该类通知为URC。在Rild中URC和一般的Response是分开处理的,概念上URC由handleUnsolicited@Atchannel.c处理,而Response由handleFinalResponse来处理。
### 1 Event Loop
Rild管理的真正精髓在ril.cpp,ril_event.cpp中,在研究的过程中,可以看到设计者在抽象上所下的功夫,设计得很优美。Event Loop的基本工作就是等待在事件端口(串口,Socket),一旦有数据到达就根据登记的Event回调函数进行处理。现在来看Ril设计者是如何建立一套管理框架来完成这些工作的?
### 1.1 Event对象
Event对象构成:(fd,index,persist,func,param)
| fd | 事件相关设备句柄。例如对于串口数据事件,fd就是相关串口的设备句柄 |
|-----|-----|
| index | |
| persist | 如果是保持的,则不从watch_list中删除。 |
| func | 回调事件处理函数 |
| param | 回调时参数 |
为了统一管理事件,Android使用了三个队列:watch_list,timer_list,pending_list,并使用了一个设备句柄池readFDS。
readFDS:是Linux的fd_set,readFDS保存了Rild中所有的设备文件句柄,以便利用select函数统一的完成事件的侦听。
watch_list:监测时间队列。需要检测的事件都放入到该队列中。
timer_list:timer队列
pending_list:待处理事件队列,事件已经触发,需要所回调处理的事件。
事件队列队列的操作:ril_event_add,ril_event_del, ril_timer_add
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a2090918.gif "image")](http://hi.csdn.net/attachment/201005/10/0_12735024032eeO.gif)
在添加操作中,有两个动作:
(1) 加入到watch_list
(2) 将句柄加入到readFDS事件句柄池。
### 1.2 ril_event_loop()
我们知道对于Linux设备来讲,我们可以使用select函数等待在FDS上,只要FDS中记录的设备有数据到来,select就会设置相应的标志位并返回。readFDS记录了所有的事件相关设备句柄。readFDS中句柄是在在AddEvent加入的。所有的事件侦听都是建立在linux的select readFDS基础上。[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a20aa309.gif "image")](http://hi.csdn.net/attachment/201005/10/0_1273502408D9MO.gif)
ril_event_loop 利用select等待在readFDS(fd_set)上,当select设备有数据时,ril_event_loop会从select返回,在watch_list中相应的Event放置到pend_list,如果Event是持久性的则不从watch_list中删除。然后ril_event_loop遍历pengding_list处理Event事件,发起事件回调函数。
### 1.3 几个重要的Event
上面分析了ril-d的框架,在该框架上跑的事件有什么
(1)s_listen_event- (s_fdListen,listenCallback)
listenCallback处理函数,
接收客户端连接:s_fdCommand=accepte(..)
添加s_commands_event()
重新建立s_listen_event,等待下一次连接
(2) s_command_event(s_fdCommand,ProcessCommandsCallback)
从fdCommand Socket连接中读取StreamRecord
使用ProcessCommandBufer处理数据
s_listen_event在大的功能上处理客户端连接(Ril-JAVA层发起的connect),并建立s_commands_event去处理Socket连接发来的Ril命令。ProcessCommandBufer实际上包含了Ril指令的下行过程。
### 1.4 下行命令翻译及其组织@ProcessCommandBuffer
RIL_JAVA传递的命令格式:Parcel ,由命令号,令牌,内容组成。RIL_JAVA到达RIL_C时转为构建本地RequestInfo,并将被翻译成具体的AT指令。由于每条AT命令的参数是不同的,所以对不同的AT指令,有不同的转换函数,在此Android设计在这里做了一个抽象,做了一个分发框架,通过命令号,利用sCommand数组,获得该命令的处理函数。
~~~
sComand[]={
}
sComand 存在于Ril_command.h中。
&sComand[]=
{RIL_REQUEST_GET_IMEI, dispatchVoid, responseString},
{RIL_REQUEST_DIAL, dispatchDial, responseVoid},
{….}
~~~
dispatchXxx函数一般都放在在Reference-ril.c中,Reference-ril.c这个就是我们需要根据不同的Modem来修改的文件。
### 1.5 send_at_command框架
send_at_command是同步的,命令发送后,send_at_command将等待在s_commandcond,直到有sp_response->finalResponse。
### 2 read loop@Atchannel.c
Read loop是解决的问题是:解析从Modem发过来的回应。如果遇到URC则通过handleUnsolicited上报的RIL_JAVA。如果是命令的应答,则通过handleFinalResponse通知send_at_command有应答结果。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a20bf350.gif "image")](http://hi.csdn.net/attachment/201005/10/0_1273502410mntq.gif)
对于URC,Rild同样使用一个抽象数组@Ril.CPP.
~~~
static UnsolResponseInfo s_unsolResponses[] = {
#include "ril_unsol_commands.h"
};
~~~
并利用RIL_onUnsolicitedResponse将URC向上层发送。
### 3 Ril-d的整体数据流及其控制流示意图
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a20d1485.gif "image")](http://hi.csdn.net/attachment/201005/10/0_1273502416R2rv.gif)
(十六)Android电话系统-概述篇
最后更新于:2022-04-01 11:40:48
# Android电话系统之概述篇
首先抛开Android的一切概念来研究一下电话系统的最基本的描述。我们的手机首先用来打电话的,随后是需要一个电话本,随后是PIM,随后是网络应用,随后是云计算,随后是想我们的手机无所不能,替代PC。但是作为一个电话的基本功能如下:
0)拨叫电话,接听电话,挂断电话,发送短信,网络连接,PIM管理
1)由于电话运营商为我们提供了呼叫等待,电话会议等补充业务,所以我们的手机需要管理多路通话,如何管理?
2)来电时,我们要播出来电铃声,接通时我们需要切换语音通道,这个又跟多媒体系统打上了交道,例如有耳机插上了,有蓝牙耳机连上了,系统该做如何的管理和切换?
3)上网的网络通路建立(例如GSM GPRS),如何PPP连接并连接到LinuxSocket通道上的?系统如何管理数据连接?
4)AP跟Modem通讯时通过AT指令的,如何将AT指令变成一个个具体的操作函数,如何管理Modem发给我们的回应,AT命令通道,数据通道如何管理?
5)sim卡的电话本如何管理?
上面的关于手机的基本问题,Android电话系统设计者必须要解答的问题。该设计如何的管理框架,提出什么概念来表达?所以要分析Android的电话部分,还是需要理解电话实现的背景知识,通讯协议,大体框架。
我们回到电话系统基本构成上,先从整体上去把握一下电话模块的大体框架,先从空中俯瞰。我给出的图是一般的智能手机的框架图,该框架基本能够概括所有手机电话模块的构成,当然也包括Android的电话系统构成。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1f106a1.gif "image")](http://hi.csdn.net/attachment/201005/10/0_1273499989K5fj.gif)
智能机架构一般是应用处理器+Modem。应用处理器与Modem的连接使用串口或者USB。在一个硬件串口通路上实现为了要同时实现数据传输并同时实现控制Modem,就需要实现多路复用协议(GSM TS07.10),在底层我们在多路复用的基础上虚拟了两个串口,一个用于CMD通道,一个用于DATA通道。电话的所有控制通路都是在这连个通道上。
RIL,Radio Interface Layer。本层为一个协议转换层,手机框架需要适应多类型的Modem接入到系统中,而对于不同的Modem有不同的特性,AT指令的格式或者回应有所不同,但是这种特性在设计应用时不可能完全考虑和兼容。所以设计者在设计电话系统时,建立了一个虚拟电话系统,为该虚拟电话系统规定了标准的功能,上层的电话管理都是建立在这些标准的功能基础之上。而RIL则是将虚拟电话系统的标准功能转换成实际的所使用的Modem的AT指令。
Android设计者将电话系统设计成了三部分。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1f39af4.gif "image")](http://hi.csdn.net/attachment/201005/10/0_1273499996s44N.gif)
Andoird的Phone Service其实是PhoneApp。GSMPhone(CDMAPhone)是Phone Service核心的对象,他包含了如下的相关对象。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1f4b9f7.gif "image")](http://hi.csdn.net/attachment/201005/10/0_1273499998A12D.gif)
我们的分析任务就是要把这些对象的相互关系,及其对象间数据传递关系弄清楚。首先我们给出以下的Android电话系统的框架,以便对Android电话系统有个概要的认识,然后从数据流的角度,以及对象的引用关系来分析系统。下面是android电话系统整体框架图。
[![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1f5cac9.gif "image")](http://hi.csdn.net/attachment/201005/10/0_127350000175a1.gif)
(十五)Android输入系统之输入路径详解
最后更新于:2022-04-01 11:40:46
# Android用户事件输入路径
### 1 输入路径的一般原理
按键,鼠标消息从收集到最终将发送到焦点窗口,要经历怎样的路径,是Android GWES设计方案中需要详细考虑的问题。按键,鼠标等用户消息消息的处理可分为不同的情况进行判定:
(1)用户输入根据系统状况是否应该派送。如在ScreenOff的情况下,在按键属于特殊按键的情况下等
(2)是否有拦截Listener
(3)对按键事件来讲,是否存在输入法
(4)是否是焦点终点
(5)是否为焦点切换按相关键
这些情况都是设计输入路径需要考虑的基本条件。
### 1.1一般的输入路径设计
该输入路径实际上是指的按键消息(MSG_KEYDOWN,MSG_KEYUP, MSG_LongPress)的输入路径,即从活动主窗口到焦点窗口所经历的路程。
[![inputNormal](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1dd5670.gif "inputNormal")](http://hi.csdn.net/attachment/201005/5/0_1273072314A4ZR.gif)
将信息输入路径分为两步:
Step 1)窗口管理器将信息发送到活动窗口
Step 2)活动窗口通过缺省处理函数将该消息一层层的传递到焦点。
这样应用程序可以在活动View的处理函数中来预先处理用户输入信息,从而增强应用对用户信息的控制力。
[![inputxxx1](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1de9a7e.gif "inputxxx1")](http://hi.csdn.net/attachment/201005/5/0_1273072315h9Yf.gif)
传递路径是通过View的缺省处理函数Onxxx来完成。通过ActiveView ->focus->focus->focus的链条关系,一级一级的将按键消息MSG_KEYDOWN,MSG_KEYUP, MSG_CHAR等传递到focus窗口。
[![inputxxx2](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1e0add6.gif "inputxxx2")](http://hi.csdn.net/attachment/201005/5/0_1273072316MABo.gif)
此时用户按键输入先发送到输入法窗口,经过输入法管理器处理,过滤后将输入法产生的结果放置到焦点View。
### 1.3输入系统整体流程
下面示意图是Android输入系统的数据流途径,通过WM的输入系统线程收集消息,分发到Focus Activity消息队列,然后通过消息系统派发。
[![InputSystemPATH](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1e1dbd1.gif "InputSystemPATH")](http://hi.csdn.net/attachment/201005/5/0_1273072319mmM9.gif)
### 2 Android输入路径详细描述
### 2.1 第一步:用户数据收集及其初步判定
KeyInputQ在WindowMangerService中建立一个独立的线程InputDeviceReader,使用Native函数readEvent来读取Linux Driver的数据构建RawEvent,放入到KeyQ消息队列中。
[![inputDeviceReader](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1e37e4c.gif "inputDeviceReader")](http://hi.csdn.net/attachment/201005/5/0_1273072322wE2w.gif)
preProcessEvent()@KeyInptQ@KeyInputQueue.java这个是在输入系统中的第一个拦截函数原型。KeyQ重载了preProcessEvent()@WindowManagerService.java。在该成员函数中进行如下动作:
(1) 根据PowerManager获取的Screen on,Screen off状态来判定用户输入的是否WakeUPScreen。
(2) 如果按键式应用程序切换按键,则切换应用程序。
(3) 根据WindowManagerPolicy觉得该用户输入是否投递。
### 2.2 第二步 消息分发第一层面
InputDispatcherThread从KeyQ中读取Events,找到Window Manager中的Focus Window,通过Focus Window记录的mClient接口,将Events专递到Client端。
[![inputDispatcherThread](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1e48299.gif "inputDispatcherThread")](http://hi.csdn.net/attachment/201005/5/0_12730723237679.gif)
如何将KeyEvent对象传到Client端:
在前面的章节(窗口管理ViewRoot,Window Manager Proxy)我们已经知道:在客户端建立Window Manager Proxy后,添加窗口到Window Manager service时,带了一个跟客户ViewRoot相关的IWindow接口实例过去,记录在WindowState中的mClient成员变量中。通过IWindow这个AIDL接口实例,Service可以访问客户端的信息,IWindow是Service连接View桥梁。
[![IWindow](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1e5a7e7.gif "IWindow")
](http://hi.csdn.net/attachment/201005/5/0_1273072324aNrZ.gif)
看看在Client ViewRootKeyEvent的分发过程
IWindow:dispatchKey(event)
dispatchKey(event)@W@ViewRoot@ViewRoot.java
ViewRoot.dispatchKey(event)@ViewRoot.java
<message>
sendMessageAtTime(msg)@Handler@Handler.java
至此我们通过前面的Looper,Handler详解章节的分析结论,我们可以知道Key Message已经放入到应用程序的消息队列。
### 2.3第三步:应用消息队列分发
消息的分发,在Looper,Handler详解章节我们分析了Looper.loop()在最后后面调用了handleMesage.
…
ActivityThread.main()
Looper.loop()
ViewRoot$RootHandler().dispatch()
handleMessage
....
注意到在分发的调用msg.target.dispatch(),而这个target在第二层将消息sendMessageAtTime到消息队列时填入了mag.target=this即为msg.target=ViewRoot实例。所有此时handleMessage就是ViewRoot重载的handleMessage函数。
handlerMessage@ViewRoot@ViewRoot.java
deliverkeyEvent
如果输入法存在,dispatchKey到输入法服务。
否则[deliverKeyEventToViewHierarchy@ViewRoot.java](#)
在这里需要强调的是,输入法的KeyEvent的拦截并没有放入到Window Manager Service中,而是放入到了客户端的RootView中来处理。
### 2.4第四步:向焦点进发,完成焦点路径的遍历。
[](#)[![focuspath1](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1e704ce.gif "focuspath1")
](http://hi.csdn.net/attachment/201005/5/0_12730723266xTM.gif)
分发函数调用栈
[deliverKeyEventToViewHierarchy@ViewRoot.java](#)
mView.dispatchKeyEvent:mView是与ViewRoot相对应的Top-Level View.如果mView是一个ViewGroup则分发消息到他的mFocus。
mView.dispatchKeyEvent @ViewGroup (ViewRoot@root)
Event.dispatch
mFocus.dispatchKeyEevnet
如果此时的mFocu还是一个ViewGroup,这回将事件专递到下一层的焦点,直到mFocus为一个View。通过这轮调用,就遍历了焦点Path,至此,用户事件传递完成一个段落。
### 2.5第五步 缺省处理
如果事件在上述Focus View没有处理掉,并且为方向键之类的焦点转换相关按键,则转移焦点到下一个View。
(十四)Android GWES之输入系统
最后更新于:2022-04-01 11:40:44
# Android输入系统
依照惯例,在研究Android输入系统之前给出输入系统的本质描述:从哲学的观点来看,输入系统就是解决从哪里来又将到哪里去问题。输入的本质上的工作就是收集用户输入信息并放置到目标位置。
Android在源代码分类上,并没有输入系统分类。本章的输入系统研究是一个综合的分析,前面的GWES的分析,特别是View的Focus Path以及Window Manager Proxy是本章分析的基础,如果没有理解,请参阅前面的窗口管理的相关章节。
Android输入系统的组成
[![InputSystem](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1d0f6ee.gif "InputSystem")](http://hi.csdn.net/attachment/201005/5/0_1273072198B9oO.gif)
输入系统由如下几部分组成:
1. 1)后台窗口管理服务
1. 2)Focus Activity
1. 3)Focus Window
1. 4)Focus View:用来接收键盘消息
从输入系统这个角度去看Android的Window Manager服务解决了用户信息输入收集,而FocusActvitiy,Focus Window、Focus View这些概念的设计是为了解决用户输入应该放到哪里去这个问题。在整个Android系统中,同时只有一个一个Focus Window,而属于该Window的Focus View才是真正的Focus View。
在Android系统中,在设计上要求多个Actvitiy同时存在运行。在实现中,每次把Actvitiy变成Focused Actvitiy时([setFocusedActivity@ActivityManagerService.java](#))激活程序的时候,就把该Activity的主窗口设置成前景窗口,即系统中的顶层窗口,AppToken概念的引进就是为了解决窗口对象的归属问题。在这个过程中,在逻辑上看,我们挑选了一个Activity作为了Focus Activity来接收系统的消息,实质上这个Focus Activity的Focus窗口就是前景窗口。
Focus窗口的改变将改变焦点View,前景窗口的改变也将引起焦点View的变化。焦点和光标的概念用于管理输入设备和输入事件的传送。光标是一个绘制在屏幕之上的小位图,指示当前的输入位置。键盘输入有类似的输入焦点和键盘输入插入符的概念。只有具有输入焦点的窗口才能获取键盘事件。改变窗口的焦点通常由特殊的按键组合或者TouchEvent事件完成。具有输入焦点的窗口通常绘制有一个键盘插入符。该插入符的存在、形式、位置,以及该插入符的控制完全是由窗口的事件处理例程完成的。
现在站在更宏观的位置来看Actvitiy的输入系统,可以从Linux Driver开始到输入框结束的整个链条,我这里给出大输入系统的概念,Android大输入系统包含:Linux driver, Window Manager, Message System, View Focus Path,Focus View。
> Android输入系统架构图
[![InputSystemALL](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1d1fd32.gif "InputSystemALL")](http://hi.csdn.net/attachment/201005/5/0_12730722030o6j.gif)
现在从Android的代码分析的角度,来看看输入系统的组成。这个过程从代码中分析处理:
在Window Manager Service端
readEvent@com_android_server_KeyInputQueue.cpp
KeyQ@WindowMangerService.java
KeyInputQ@KeyInputeQueue.java
InputDispatcherThread@WindowMangerService.java
在Client端
IWindow@ViewRoot.Java
ViewRoot@ViewRoot.java
KeyInputQ在WindowMangerService中建立一个独立的线程InputDeviceReader,使用Native函数readEvent来读取Linux Driver的数据构建RawEvent,并放入到KeyQ消息队列中。
InputDispatcherThread从KeyQ中读取Events,找到Window Manager中的Focus Window,通过Focus Window记录的mClient接口,将Events专递到Client端。Client端在根据自己的Focus Path传递事件,直到事件被处理。
(十三)Android GWES之Android窗口管理
最后更新于:2022-04-01 11:40:42
# Android GWES之Android窗口管理
### 1基本构架原理
Android的窗口管理是C/S模式的。Android中的Window是表示Top Level等顶级窗口的概念。DecorView是Window的Top-Level View,这个View我称之为主View,DecorView会缺省的attach到Activity的主窗口中。主View被加入到WindowManager中,WM使用WindowState与这个主View对应。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1be582a.gif)
Activity建立一个主窗口后,在将主窗口添加到WindowManager时,首先要建立WindowManager代理对象,并打开一个会话(实现IWindowSession AIDL接口),并维持该会话。Activity将通过该会话与WindowManager建立联系,这个Session是C/S体系的基础,Client通过WindowSession将window加入到Window Manager中。
一个完整的窗口概念横跨了View,ViewRoot,WindowManager Service。Window,DecorView,View,IWindow ,ISession,WindowState之间的关系如下
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1c0acb0.gif)
Client端的Activity通过Session会话与WindowManager建立对话,而WindowManager则通过IWindow接口访问Client,将消息传递到Client端,通过消息分发渠道,将消息传递到处理函数OnXXX。
后面我们将通过Client,WM Service分别加以分析。
### 2 Client端
我一致认为在Android中Window的概念并不是个很重要的概念。他的Window类,只是在PhoneWindow和MidWindow中使用。而PhoneWindow只是做了一个具体跟手机功能相关的公用事件的处理,所以在Android中PhoneWindow并不是一个抽象的纯正概念,而是一个跟手机系统相关的一个特别窗口概念,例如按键的默认动作处理,按键音的发出等等。
##### 2.1 View
在Activity中真正重要的概念是View,以下是Google官方对View的定义:
This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for <em>widgets</em>, which are used to create interactive UI components (buttons, text fields, etc.). The {@link android.view.ViewGroup} subclass is the base class for <em>layouts</em>, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.
我对View不做翻译,翻译成视图好像不太佳,View在Android中,View比视图具有广的外延。View包含了用户交互,包含了显示,视图在中文中仅仅表示了静态的显示。对于View的理解应该从最容易的理解开始。我们使用过编辑器,在Android中这个编辑器就是一个View,这个编辑器需要显示文字,需要接收用户的键盘输入和鼠标选择,但是一个屏幕上有多个编辑器,如何管理,如何切换焦点编辑器,这些都是需要管理的。
客户端的组成:(Window,View,ViewRoot,WindowManager Proxy)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1c2319b.gif)
在Activity在performLaunchActivity时,会使用Activity.attach()建立一个PhoneWindow主窗口。这个主窗口的建立并不是一个重点。handleResumeActivity真正要启动一个Activity时候,将主窗口加入到WindowManager,当然并不是将主窗口本身,而是将主窗口的DecorView加入到WindowManager中。
真正Window核心的抽象概念存在于View,ViewRoot,WindowManger中的WindowState。为了描述概念的方便性,我特别提出主View这个概念,这个主View就是Top-Level View of the window. 主View与View想对,突出主View是attatch到主窗口上的。而一般的View则是存在于主View中的。主窗口这个概念,我讲的主窗口实际上就是Android提到的Top Level Window。
我们所提到的概念:View,GroupView,DecorView,ViewRoot都是存在于Client端,只有WindowState这个概念存在于Window Manager Service端。
DecorView实际上是一个ViewGroup。在依存关系上来讲,对看个主窗口来讲,DecorView是Top-Level View.View并不是关注的重点,重要的是我们如何需要知道分发路径是建立在什么关系上的。View的成员变量mParent用来管理View上级关系的。而ViewGroup顾名思义就是一组View的管理,于是在ViewGroup构建了焦点管理和子View节点数组。这样通过View的mParent和ViewGroup的mChildren构建了Android中View直接的关系网。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1c33040.gif)
### 2.2 Focus Path
所谓的Foucs Path就是我们的KeyEvent传递的路线。一般的我们的KeyEvent在主循环中主View通过View的焦点记录关系传递到焦点View上。例如下图,View22是焦点,我们从最顶层的View通过mFcous的关系链找到最后所形成的路径就是Focus Path。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1c47884.gif)
### 2.3 ViewRoot,Window Manager Proxy
ViewRoot与Window Manager的核心是IWindowSession和IWindow。ViewRoot通过IWindowSession添加窗口到Window Manager。而IWindow这是Window Manager分发消息给Client ViewRoot的渠道。利用AIDL接口进行进程间通信。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1c5c8f1.gif)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1c6c844.gif)
ViewRoot实际是一个Handler,ViewRoot建立主View与WindowsManger通讯的桥梁。ViewRoot在本质上一个Handler。我们知道Handler的基本功能就是处理回调,发送消息。
Activity在使用getSystemService获取WindowManagerImpl ,建立了一个WindowManagerImpl实例,即Window Manager服务的代理:
wm=(WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE);并调用wm.addview添加窗口到WMService中。
这个过程在客户端建立了什么样的管理框架,并如何这个会话?在Window Manager Proxy中建立了View,Layout ,ViewRoot三者的对应关系表。构造一个ViewRoot就会打开一个session,并利用IWindowSession建立会话上下文。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1c7d444.gif)
### 4 Window Manager Service
本次对于Window Manager Service的研究仅限于FocusWindow,消息系统。其他的部分将在后面的专门章节讨论。
Window Manager管理的窗口是应用程序的Top-level窗口,我这里参照Window的概念叫主窗口。主窗口为什么要放在在Service这边来管理呢?为什么不放在Client那边?主窗口放置在一起管理是为了计算Z-order序列,根据应用程序的状态来显隐应用程序的窗口。我想Android设计者在考虑设计窗口系统的时候,一定首先考虑:
- 窗口z-order序的管理
- 活动窗口的计算,及其变化通知
- 窗口归属(属于哪个应用)
- 输入法管理
Window Service大体上实现了如下的功能:,
(1)Z-ordered的维护函数
(2)输入法管理
(3)AddWindow/RemoveWindow
(4)Layerout
(5)Token管理,AppToken
(6)活动窗口管理(FocusWindow)
(7)活动应用管理(FocusAPP)
(8)转场动画
(9)系统消息收集线程
(11)系统消息分发线程
在服务端的窗口对象叫做WindowState。在Service维护了一个mWindow数组,这个mWindow就是Window的Z-order序数组。mWindowMap用于记录<Client:Binder,WindowState对象>。
WindowState有一个叫做mClient成员变量来记录客户端IWindow实例,通过IWindow接口实例,Service可以访问客户端的信息,说以IWindow是Service连接View桥梁。
(1) FocusWindow活动窗口如何计算?
基本原理就是查找前景应用(FousActivity),并同Z-Order序中找出属于该FousActivity(AppToken)的主窗口,这个窗口就是计算出来的Focus Window。
(2)为什么要提出Token这个概念呢?
一个应用程序要管理自己的窗口,那么如何来标识该窗口是属于某个Activity,Andoid设计者提出了AppToken这个概念。AppToken在本质上的描述:<Token:IBinder,allWindows>,通过Token找到属于该Token的allWindows。使用Token开始完成该应用程序的所有窗口的显示和隐藏。
(3)系统消息收集与处理
我们下面重点研究Service中的系统消息收集模式及其分发模式。Service使用KeyQ作为专门的消息队列。
- KeyEvent
- TouchEvent
- TrackballEvent
系统有两个线程:
KeyQ线程,通过Navite函数readEvent轮询设备,将读取的结果放置在KeyQ队列中。
系统dispatcher 等待在KeyQ消息队列上,一旦从消息队列中获取到消息,就通过分发函数通过mClient传递到Client端。
(十二)Android GEWS窗口管理之基本架构原理
最后更新于:2022-04-01 11:40:39
# Android GWES之窗口管理之基本构架原理
Android的窗口管理是C/S模式的。Android中的Window是表示Top Level等顶级窗口的概念。DecorView是Window的Top-Level View,这个View我称之为主View,DecorView会缺省的attach到Activity的主窗口中。主View被加入到WindowManager中,WM使用WindowState与这个主View对应。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1ae6d85.gif)
Activity建立一个主窗口后,在将主窗口添加到WindowManager时,首先要建立WindowManager代理对象,并打开一个会话(实现IWindowSession AIDL接口),并维持该会话。Activity将通过该会话与WindowManager建立联系,这个Session是C/S体系的基础,Client通过WindowSession将window加入到Window Manager中。一个完整的窗口概念横跨了View,ViewRoot,WindowManager Service。Window,DecorView,View,IWindow ,ISession,WindowState之间的关系如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1b08795.gif)
客户端的Activity通过Session会话与WindowManager建立对话,而WindowManager则通过IWindow接口访问Client,将消息传递到Client端,通过消息分发渠道,将消息传递到处理函数OnXXX。
后面我们将通过Client,WM Service分别加以分析。
(十一)Android GWES之消息系统
最后更新于:2022-04-01 11:40:37
# Android GWES之Android消息系统
Looper,Handler,View
我们要理解Android的消息系统,Looper,Handle,View等概念还是需要从消息系统的基本原理及其构造这个源头开始。从这个源头,我们才能很清楚的看到Android设计者设计消息系统之意图及其设计的技术路线。
### 消息系统的基本原理
从一般的系统设计来讲,一个消息循环系统的建立需要有以下几个要素:
- 消息队列
- 发送消息
- 消息读取
- 消息分发
- 消息循环线程
首先来研究一下消息驱动的基本模型,我使用如下的图形来表示一个消息系统最基本构成:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1a54116.gif)
上面的模型代表应用程序一直查询自己的消息队列,如果有有消息进来,应用消息处理函数中根据消息类型及其参数来作相应的处理。
消息系统要运作起来,必定有消息的产生和消费。我们可以从下图看到消息生产和消费的一个基本的链条,这是一个最基本的,最简单的消息系统。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1a643c6.gif)
生产线程将消息发送到消息队列,消息消费者线程从消息队列取出消息进行相应的处理。但是这样简单的模型对实际运行的系统来说是不够的,例如对系统资源的消耗等不能很好的处理,我们就需要一个有旗语的消息系统模型,在上面的消息系统模型中加入了一个旗语,让消息消费者线程在没有消息队列为空时,等待旗语,进入到挂起状态,而有消息到达时,才被唤醒继续运行。当然生产者同时也可以是消费者。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1a74e30.gif)
### 2 Android的消息模型
Android要建立一个消息系统使用了Looper,MessageQueue,Handler等概念,从上节的原理我们可以知道这些都是概念包装,本质的东西就是消息队列中消息的分发路径的和消息分发处理方式的设计。Android巧妙的利用了对象抽象技术抽象出了Looper和Handler的概念。在Looper和Handler两个概念的基础上,通过View的处理函数框架,Android十分完美的达到消息分发的目的。
参照基本消息系统描述模型,我给出了Android消息系统整体框架,表示如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1a86c45.gif)
Android消息系统消息分发框架
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1a9643a.gif)
### 3 Looper,Handler详解
Looper只是产生一个消息循环框架,首先Looper创建了消息队列并把它挂接在Linux的线程上下文中,进入到取消息,并分发消息的循环当中。Handler对象在同一个线程上下文中取得消息队列,对消息队列进行封装操作,最主要的就是SendMessage和担当起dispatchMessage这个实际工作。外部系统需要向某个Android线程发送消息,必须通过属于该AndroidThread的Handler这个对象进行。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1aa983e.gif)
Handler属于某个线程,取决Handlerd对象在哪个线程中建立。Handler在构建时做了如下的默认动作:
- l 从线程上下文取得Looper。
- l 通过Looper获取到消息队列并记录在自己的成员mQueue变量中
Handler使用消息队列进行对象封装,提供如下的成员函数:
- l 通过 post(Runnable r)发送。Runnable是消息处理的回调函数,通过该消息的发送,引起Runable 的回调运行,Post消息放置消息队列的前面。Message.callback=Runable.
- l 通过 sendMessage发送。放置在所有的Post消息之后,sendMessage发送消息.
- l dispatchMessage分发消息。消息带有回调函数,则执行消息回调函数,如何没有则使用默认处理函数:handleMessage。而handleMessage往往被重载成某个继承Handler对象的新的特定的handleMessage。
几乎所有的Message发送时,都指定了target。Message.target=(this).
Looper运行在Activity何处?我们现在可以从代码堆栈中纵观一下Looper的位置。
NaiveStart.main()
ZygoteInit.main
ZygoteInit$MethodAndArgsCall.run
Method.Invoke
method.invokeNative
ActivityThread.main()
Looper.loop()
ViewRoot$RootHandler().dispatch()
handleMessage
....
这样我们就更清楚的了解到Looper的运行位置。
(十)Android GWES之基本原理篇
最后更新于:2022-04-01 11:40:35
# Android GWES
### 基本框架篇
我这里的GWES这个术语实际上从Microsoft 的Window上移植过来的,用GWES来表示Android的窗口事件系统不是那么准确,在Android中Window是个弱化了的概念,更多的表现在View这个概念上。在很大程度上,Android的View的概念可以代替Microsoft Window这个概念,有点和Microsof暗中较劲的意味,你用过的概念我就偏不用,这个也是我以为的设计者意图。
### 原始GUI基本框架
首先我们从Android的SDK外特性空间开始,在编写Actvitiy时,我们都是面对的处理函数:OnXXXX(),例如有按键按下就是OnKeyDown等,在这个过程中系统做了怎样的处理?要详细的理解这个过程,我们就需要理解Andoid的View管理,窗口系统,消息系统和输入系统。我们还是从最本质的地方开始,Android作为一种嵌入式的图形用户界面系统,它的基本原理与一般GUI的原理是相同的,同时也是遵循GWES(图形窗口事件系统)的一般规律,总体上Android就是管理用户输入和系统屏幕输出的一个系统。其实GWES这个名称更能体现GUI的基本实质要素:图形、窗口、事件。
#### 1. 一般GUI的基本组成
GUI的实现就是对上面提到的三个基本要素的管理,根据这这三个要素的特性及其涉及的范围,GUI在总体上可以分为三部分:
- 事件管理器
- 窗口管理器
- GDI(绘制与GDI逻辑对象管理)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1999343.gif)
(1) 事件管理器
- 收集系统消息,转换并分发系统消息和用户消息给各个窗口对象。
- 消息队列管理
(2)窗口管理器:
- 管理窗口的创建,销毁
- 窗口的绘制
- 活动窗口,输入焦点的切换
- 窗口间关系的管理
- 控件,菜单实现
(3)GDI
- 上下文设备管理
- 上下文设备对象管理:字体,画笔等
- 图形绘制:点、线,填充等
- 图象操作:位传送、位图操作
#### 2 系统体系构架及其数据流的大体走向
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a19b4f05.gif)
在本质上GUI就是管理用户输入和屏幕输出,我们从上面的体系结构可以看到GUI的这两大数据流的基本流向,这也决定了Android GWES设计的最基本的着眼点。
Android弱化了窗口的概念,着重使用View的概念。所以Android的基本组成可以从上面的图修改成如下的组成:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a19c93f5.gif)
(九)Zygote Service
最后更新于:2022-04-01 11:40:32
# Zygote Service
在本章我们会接触到这两个单词:
- Zygote [生物] 受精卵, 接合子, 接合体
- Spawn:产卵
通过这两个单词,我们就可以大体知道Zygote是干什么的了,就是叫老母鸡下蛋。通过“Zygote”产出不同的子“Zygote”。从大的架构上讲,Zygote是一个简单的典型C/S结构。其他进程作为一个客服端向Zygote发出”孵化”请求,Zygote接收到命令就“孵化”出一个Activity进程来。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a193cd13.gif)
Zygote系统代码组成及其调用结构:
- Zygote.java
提供访问Dalvik “zygote”的接口。主要是包装Linux系统的Fork,以建立一个新的VM实例进程。
- ZygoteConnection.java
Zygote的套接口连接管理及其参数解析。其他Actvitiy建立进程请求是通过套接口发送命令参数给Zygote。
- ZygoteInit.java
Zygote的main函数入口。
Zygote系统代码层次调用
~~~
main()
startSystemServer()…
runSelectLoopMode()
Accept socket connection
Conntecion.RunOnce()
Read argument
folkAndSpecialize
folkAndSpecialize使用Native函数Dalvik_dalvik_system_Zygote_forkAndSpecialize
//native 的获取
dalvik/vm/native
//dalvik_system_Zygote.c
const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
{ "fork", "()I",
Dalvik_dalvik_system_Zygote_fork },
{ "forkAndSpecialize", "(II[II[[I)I",
Dalvik_dalvik_system_Zygote_forkAndSpecialize },
{ "forkSystemServer", "(II[II[[I)I",
Dalvik_dalvik_system_Zygote_forkSystemServer },
{ NULL, NULL, NULL },
};
~~~
在这里我们就有了Zygote服务的全貌理解,也在Code中印证了。【应[yk_hu0621](http://hi.csdn.net/yk_hu0621)修正】{由于Android中没有具体应用程序的入口,都是通过启动Actvity来启动相关的Android应用,而这个 Android应用则对应着Linux进程,Activity便Host在这个应用程序上。}
{原文:Activity在本质上是个什么东西,就是一个Linux进程}
从分析中我们可以看到,Android使用了Linux的fork机制。在Linux中Fork是很高效的。
一个Android的应用实际上一个Linux进程,所谓进程具备下面几个要素,
a.要有一段程序供该进程运行,程序是可以被多个进程共享的。
b..进程专用的系统堆栈空间。
c.进程控制块,在linux中具体实现是task_struct
d.有独立的存储空间。
fork 创造的子进程复制了父亲进程的资源,包括内存的内容task_struct内容,在复制过程中,子进程复制了父进程的task_struct,系统堆栈空间和页面表,而当子进程改变了父进程的变量时候,会通过copy_on_write的手段为所涉及的页面建立一个新的副本。所以只有子进程有改变变量时,子进程才新建了一个页面复制原来页面的内容,基本资源的复制是必须的,整体看上去就像是父进程的独立存储空间也复制了一遍。
再看看下面Google在讲解Dalvik虚拟机的图片,我们就大体有了Android系统中Actvitiy的实际映射状态有了基本的认识。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b1a1951eae.gif)