原文:http://blog.csdn.net/luoshengyang/article/details/8303098。原文代码比較老了,但是核心不变。在原文基础上改动了一些代码,以及增长本身少许的理解。java
在上一篇博客中。咱们分析了应用程序窗体链接到WindowManagerService服务的过程。android
在这个过程当中,WindowManagerService服务会为应用程序窗体建立过一个到SurfaceFlinger服务的链接。c++
有了这个链接以后。WindowManagerService服务就可以为应用程序窗体建立画图表面了,以便可以用来渲染窗体的UI。安全
在本文中,咱们就具体分析应用程序窗体的画图表面的建立过程。session
这节主要是把http://blog.csdn.net/luoshengyang/article/details/8303098这篇博客复制过来的app
每一个在C++层实现的应用程序窗体都需要有一个画图表面,而后才干够将本身的UI表现出来。这个画图表面是需要由应用程序进程请求SurfaceFlinger服务来建立的,在SurfaceFlinger服务内部使用一个Layer对象来描写叙述,同一时候,SurfaceFlinger服务会返回一个实现了ISurface接口的Binder本地对象给应用程序进程,因而,应用程序进程就可以得到一个实现了ISurface接口的Binder代理对象。有了这个实现了ISurface接口的Binder代理对象以后。在C++层实现的应用程序窗体就可以请求SurfaceFlinger服务分配图形缓冲区以及渲染已经填充好UI数据的图形缓冲区了。框架
对于在Java层实现的Android应用程序窗体来讲,它也需要请求SurfaceFlinger服务为它建立画图表面,这个画图表面使用一个Surface对象来描写叙述。由于在Java层实现的Android应用程序窗体还要接受WindowManagerService服务管理。所以,它的画图表面的建立流程就会比在C++层实现的应用程序窗体复杂一些。ide
详细来讲,就是在在Java层实现的Android应用程序窗体的画图表面是经过两个Surface对象来描写叙述,一个是在应用程序进程这一側建立的,还有一个是在WindowManagerService服务这一側建立的,它们相应于SurfaceFlinger服务这一側的同一个Layer对象,如图所看到的:函数
在应用程序进程这一側。每一个应用程序窗体,即每一个Activity组件,都有一个关联的Surface对象,这个Surface对象是保在在一个关联的ViewRootImpl对象的成员变量mSurface中的。oop
这里咱们仅仅关注Surface类的实现。在应用程序进程这一側,每一个Java层的Surface对都相应有一个C++层的Surface对象。
在WindowManagerService服务这一側。每一个应用程序窗体,即每一个Activity组件。都有一个相应的WindowState对象,这个WindowState对象的成员变量mSurface相同是指向了一个Surface对象,在WindowManagerService服务这一側,每一个Java层的Surface对都相应有一个C++层的SurfaceControl对象。
一个应用程序窗体分别位于应用程序进程和WindowManagerService服务中的两个Surface对象有什么差异呢?尽管它们都是用来操做位于SurfaceFlinger服务中的同一个Layer对象的,只是,它们的操做方式却不同。详细来讲,就是位于应用程序进程这一側的Surface对象负责绘制应用程序窗体的UI,即往应用程序窗体的图形缓冲区填充UI数据,而位于WindowManagerService服务这一側的Surface对象负责设置应用程序窗体的属性。好比位置、大小等属性。这两种不一样的操做方式各自是经过C++层的Surface对象和SurfaceControl对象来完毕的。所以。位于应用程序进程和WindowManagerService服务中的两个Surface对象的使用方法是有差异的。
之因此会有这种差异。是因为绘制应用程序窗体是独立的,由应用程序进程来完就能够。而设置应用程序窗体的属性却需要全局考虑,即需要由WindowManagerService服务来统筹安排,好比,一个应用程序窗体的Z轴坐标大小要考虑它到的窗体类型以及它与系统中的其余窗体的关系。
讲到这里。另一个问题又来了,由于一个应用程序窗体相应有两个Surface对象,那么它们是怎样建立出来的呢?简单地说,就是依照下面步骤来建立:
1. 应用程序进程请求WindowManagerService服务为一个应用程序窗体建立一个Surface对象;
2. WindowManagerService服务请求SurfaceFlinger服务建立一个Layer对象,并且得到一个ISurface接口。
3. WindowManagerService服务将得到的ISurface接口保存在其内部的一个Surface对象中,并且将该ISurface接口返回给应用程序进程;
4. 应用程序进程获得WindowManagerService服务返回的ISurface接口以后,再将其封装成其内部的另一个Surface对象中。
那么应用程序窗体的画图表面又是何时建立的呢?一般是在不存在的时候就建立。因为应用程序窗体在执行的过程当中,它的画图表面会依据需要来销毁以及又一次建立的,好比。应用程序窗体在第一次显示的时候,就会请求WindowManagerService服务为其建立绘制表面。从前面Android应用程序窗体(Activity)的视图对象(View)的建立过程分析一文可以知道,当一个应用程序窗体被激活并且它的视图对象建立完毕以后,应用程序进程就会调用与其所关联的一个ViewRootImpl对象的成员函数requestLayout来请求对其UI进行布局以及显示。因为这时候应用程序窗体的画图表面还没有建立,所以,ViewRoot类的成员函数requestLayout就会请求WindowManagerService服务来建立画图表面。接下来,咱们就从ViewRoot类的成员函数requestLayout開始。分析应用程序窗体的画图表面的建立过程。如图所看到的:
以前咱们分析过在ViewRootImpl的setView函数中调用了requestLayout函数,现在咱们来分析下这个函数。 ViewRootImpl类的成员函数requestLayout首先调用另一个成员函数checkThread来检查当前线程是否就是建立当前正在处理的ViewRootImpl对象的线程。假设不是的话,那么ViewRoot类的成员函数checkThread就会抛出一个异常出来。ViewRoot类是从Handler类继承下来的,用来处理应用程序窗体的UI布局和渲染等消息。
由于这些消息都是与Ui相关的。所以它们就需要在UI线程中处理,这样咱们就可以判断出当前正在处理的ViewRootImpl对象是要应用程序进程的UI线程中建立的。进一步地,咱们就可以判断出ViewRootImpl类的成员函数checkThread实际上就是用来检查当前线程是不是应用程序进程的UI线程,假设不是的话。它就会抛出一个异常出来。
经过了上述检查以后,ViewRootImpl类的成员函数requestLayout首先将其成员变量mLayoutRequested的值设置为true。表示应用程序进程的UI线程正在被请求运行一个UI布局操做。接着再调用另一个成员函数scheduleTraversals来继续运行UI布局的操做。
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
在scheduleTraversals函数中,ViewRootImpl类的成员变量mTraversalScheduled用来表示应用程序进程的UI线程是否已经在scheduleTraversals。假设已经调度了的话。它的值就会等于true。在这样的状况下, ViewRootImpl类的成员函数scheduleTraversals就什么也不作。不然的话,它就会首先将成员变量mTraversalScheduled的值设置为true,就会发送一个消息来处理mTraversalRunnable这个Runnable。
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }咱们来看下TraversalRunnable 这个Runnable,就是调用了doTraversal函数
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }
而在doTraversal这个函数中调用了performTraversals函数
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
ViewRootImpl类的成员函数performTraversals的实现是至关复杂的。这里咱们分析它的实现框架.
在分析ViewRootImpl类的成员函数performTraversals的实现框架以前,咱们首先了解ViewRoot类的下面五个成员变量:
--mView:它的类型为View,但它实际上指向的是一个DecorView对象。用来描写叙述应用程序窗体的顶级视图,这一点可以參考前面Android应用程序窗体(Activity)的视图对象(View)的建立过程分析一文。
--mLayoutRequested:这是一个布尔变量。用来描写叙述应用程序进程的UI线程是否需要正在被请求运行一个UI布局操做。
--mFirst:这是一个布尔变量,用来描写叙述应用程序进程的UI线程是否第一次处理一个应用程序窗体的UI。
--mFullRedrawNeeded:这是一个布尔变量。用来描写叙述应用程序进程的UI线程是否需要将一个应用程序窗体的全部区域都又一次绘制。
--mSurface:它指向一个Java层的Surface对象。用来描写叙述一个应用程序窗体的画图表面。
注意,成员变量mSurface所指向的Surface对象在建立的时候,尚未在C++层有一个关联的Surface对象,所以。这时候它描写叙述的就是一个无效的画图表面。另外,这个Surface对象在应用程序窗体执行的过程当中,也会可能被销毁,所以,这时候它描写叙述的画图表面也会变得无效。在上述两种状况中,咱们都需要请求WindowManagerService服务来为当前正在处理的应用程序窗体建立有一个有效的画图表面,以便可以在上面渲染UI。这个建立画图表面的过程正是本文所要关心的。
理解了上述五个成员变量以后。咱们就可以分析ViewRootImpl类的成员函数performTraversals的实现框架了,例如如下所看到的:
1. 将成员变量mView和mFullRedrawNeeded的值分别保存在本地变量host和fullRedrawNeeded中。并且将成员变量mTraversalScheduled的值设置为false,表示应用程序进程的UI线程中的消息已经被处理了。
2. 本地变量newSurface用来描写叙述当前正在处理的应用程序窗体在本轮的消息处理中是否新建立了一个画图表面。它的初始值为false。
3. 假设成员变量mLayoutRequested的值等于true,那么就表示应用程序进程的UI线程正在被请求对当前正在处理的应用程序窗体运行一个UI布局操做,所以,这时候就会调用本地变量host所描写叙述的一个顶层视图对象的成员函数measure来測量位于各个层次的UI控件的大小。
4. 假设当前正在处理的应用程序窗体的UI是第一次被处理。即成员变量mFirst的值等于true,或者当前正在处理的应用程序窗体的大小发生了变化。即本地变量windowShouldResize的值等于true,或者当前正在处理的应用程序窗体的边衬发生了变化。即本地变量insetsChanged的值等于true,或者正在处理的应用程序窗体的可见性发生了变化。即本地变量viewVisibilityChanged的值等于true。或者正在处理的应用程序窗体的UI布局參数发生了变化,即本地变量params指向了一个WindowManager.LayoutParams对象,那么应用程序进程的UI线程就会调用另一个成员函数relayoutWindow来请求WindowManagerService服务又一次布局系统中的所有窗体。WindowManagerService服务在又一次布局系统中的所有窗体的过程当中,假设发现当前正在处理的应用程序窗体还没有具备一个有效的画图表面,那么就会为它建立一个有效的画图表面。这一点是咱们在本文中所要关注的。
5. 应用程序进程的UI线程在调用ViewRootImpl类的成员函数relayoutWindow来请求WindowManagerService服务又一次布局系统中的所有窗体以前。会调用成员变量mSurface所指向的一个Surface对象的成员函数isValid来推断它描写叙述的是不是一个有效的画图表面。并且将结果保存在本地变量hadSurface中。
6. 应用程序进程的UI线程在调用ViewRootImpl类的成员函数relayoutWindow来请求WindowManagerService服务又一次布局系统中的所有窗体以后,又会继续调用成员变量mSurface所指向的一个Surface对象的成员函数isValid来推断它描写叙述的是不是一个有效的画图表面。假设这时候成员变量mSurface所指向的一个Surface对象描写叙述的是不是一个有效的画图表面,并且本地变量hadSurface的值等于false,那么就说明WindowManagerService服务为当前正在处理的应用程序窗体新建立了一个有效的画图表面。因而就会将本地变量newSurface和fullRedrawNeeded的值均改动为true。
7. 应用程序进程的UI线程再次推断mLayoutRequested的值是否等于true。假设等于的话,那么就说明需要对当前正在处理的应用程序窗体的UI进行又一次布局,这是经过调用本地变量host所描写叙述的一个顶层视图对象的成员函数layout来实现的。在对当前正在处理的应用程序窗体的UI进行又一次布局以前。应用程序进程的UI线程会将成员变量mLayoutRequested的值设置为false,表示以前所请求的一个UI布局操做已经获得处理了。
8. 应用程序进程的UI线程接下来就要開始对当前正在处理的应用程序窗体的UI进行又一次绘制了,只是在重绘以前,会先询问一下那些注冊到当前正在处理的应用程序窗体中的Tree Observer,即调用它们的成员函数dispatchOnPreDraw,看看它们是否需要取消接下来的重绘操做,这个询问结果保存在本地变量cancelDraw中。
9. 假设本地变量cancelDraw的值等于false,并且本地变量newSurface的值也等于false,那么就说明注冊到当前正在处理的应用程序窗体中的Tree Observer不要求取消当前的此次重绘操做,并且当前正在处理的应用程序窗体也没有得到一个新的画图表面。在这样的状况下。应用程序进程的UI线程就会调用ViewRoot类的成员函数draw来对当前正在处理的应用程序窗体的UI进行重绘。
在重绘以前,还会将ViewRoot类的成员变量mFullRedrawNeeded的值重置为false。
10. 假设本地变量cancelDraw的值等于true,或者本地变量newSurface的值等于true,那么就说明注冊到当前正在处理的应用程序窗体中的Tree Observer要求取消当前的此次重绘操做。或者当前正在处理的应用程序窗体得到了一个新的画图表面。在这两种状况下,应用程序进程的UI线程就不能对当前正在处理的应用程序窗体的UI进行重绘了,而是要等到下一个消息到来的时候。再进行重绘。以便使得当前正在处理的应用程序窗体的各项參数可以获得又一次设置。
下一个消息需要当即被调度。所以。应用程序进程的UI线程就会又一次运行ViewRootImpl类的成员函数scheduleTraversals。
这样,咱们就分析完毕ViewRoot类的成员函数performTraversals的实现框架了,接下来咱们就继续分析ViewRootImpl类的成员函数relayoutWindow的实现,以便可以看到当前正在处理的应用程序窗体的画图表面是怎样建立的。
在performTraversals函数中调用了relayoutWindow函数例如如下:
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
而后在relayoutWindow函数中调用了WindowSession的relayout函数
int relayoutResult = mWindowSession.relayout( mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ?WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingStableInsets, mPendingOutsets, mPendingConfiguration, mSurface);
咱们先看看IWindowSession函数。这个函数是一个aidl需要在out文件夹下看其java文件
public interface IWindowSession extends android.os.IInterface { ...... public static abstract class Stub extends android.os.Binder implements android.view.IWindowSession { ...... private static class Proxy implements android.view.IWindowSession { private android.os.IBinder mRemote; ...... public int relayout(android.view.IWindow window, android.view.WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, boolean insetsPending, android.graphics.Rect outFrame, android.graphics.Rect outContentInsets, android.graphics.Rect outVisibleInsets, android.content.res.Configuration outConfig, android.view.Surface outSurface) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeStrongBinder((((window!=null))?(window.asBinder()):(null))); if ((attrs!=null)) { _data.writeInt(1); attrs.writeToParcel(_data, 0); } else { _data.writeInt(0); } _data.writeInt(requestedWidth); _data.writeInt(requestedHeight); _data.writeInt(viewVisibility); _data.writeInt(((insetsPending)?(1):(0))); mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); if ((0!=_reply.readInt())) { outFrame.readFromParcel(_reply); } if ((0!=_reply.readInt())) { outContentInsets.readFromParcel(_reply); } if ((0!=_reply.readInt())) { outVisibleInsets.readFromParcel(_reply); } if ((0!=_reply.readInt())) { outConfig.readFromParcel(_reply); } if ((0!=_reply.readInt())) { outSurface.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } return _result; } ...... } ...... } ...... }
IWindowSession.Stub.Proxy类的成员函数relayout首先将从前面传进来的各个參数写入到Parcel对象_data中,接着再经过其成员变量mRemote所描写叙述的一个Binder代理对象向执行在WindowManagerService服务内部的一个Session对象发送一个类型为TRANSACTION_relayout的进程间通讯请求,当中。这个Session对象是用来描写叙述从当前应用程序进程到WindowManagerService服务的一个链接的。
咱们来看下ViewRootImpl的Surface的readFromParcel函数,获取数据以后调用了setNativeObjectLocked函数
public void readFromParcel(Parcel source) { if (source == null) { throw new IllegalArgumentException("source must not be null"); } synchronized (mLock) { // nativeReadFromParcel() will either return mNativeObject, or // create a new native Surface and return it after reducing // the reference count on mNativeObject. Either way, it is // not necessary to call nativeRelease() here. mName = source.readString(); setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source)); } }
setNativeObjectLocked函数保存了从WMS传过来的指针。
private void setNativeObjectLocked(long ptr) { if (mNativeObject != ptr) { if (mNativeObject == 0 && ptr != 0) { mCloseGuard.open("release"); } else if (mNativeObject != 0 && ptr == 0) { mCloseGuard.close(); } mNativeObject = ptr; mGenerationId += 1; if (mHwuiContext != null) { mHwuiContext.updateSurface(); } } }
当执行在WindowManagerService服务内部的Session对象处理完毕当前应用程序进程发送过来的类型为TRANSACTION_relayout的进程间通讯请求以后,就会将处理结果写入到Parcel对象_reply中。并且将这个Parcel对象_reply返回给当前应用程序进程处理。返回结果包括了一系列与參数window所描写叙述的应用程序窗体相关的參数,例如如下所看到的:
1. 窗体的大小:终于保存在输出參数outFrame中。
2. 窗体的内容区域边衬大小:终于保存在输出參数outContentInsets中。
3. 窗体的可见区域边衬大小:终于保存在输出參数outVisibleInsets中。
4. 窗体的配置信息:终于保存在输出參数outConfig中。
5. 窗体的画图表面:终于保存在输出參数outSurface中。
这里咱们仅仅关注从WindowManagerService服务返回来的窗体画图表面是怎样保存到输出參数outSurface中的,即关注Surface类的成员函数readFromParcel的实现。从前面的调用过程可以知道,输出參数outSurface描写叙述的即是当前正在处理的应用程序窗体的画图表面。将WindowManagerService服务返回来的窗体画图表面保存在它里面。就至关因而为当前正在处理的应用程序窗体建立了一个画图表面。
在分析Surface类的成员函数readFromParcel的实现以前,咱们先分析Session类的成员函数relayout的实现,因为它是用来处理类型为TRANSACTION_relayout的进程间通讯请求的。
session类的relayout的话最后调用了WMS的relayoutWindow函数
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Configuration outConfig, Surface outSurface) { if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); int res = mService.relayoutWindow(this, window, seq, attrs, requestedWidth, requestedHeight, viewFlags, flags, outFrame, outOverscanInsets, outContentInsets, outVisibleInsets, outStableInsets, outsets, outConfig, outSurface); if (false) Slog.d(WindowManagerService.TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); return res; }
在WMS中后面有例如如下代码,是把对象传到outSurface中去了。
SurfaceControl surfaceControl = winAnimator.createSurfaceLocked(); if (surfaceControl != null) { outSurface.copyFrom(surfaceControl);
WindowManagerService类的成员函数relayoutWindow的实现是至关复杂的。这里咱们仅仅关注与建立应用程序窗体的画图表面相关的代码。
简单来讲。WindowManagerService类的成员函数relayoutWindow依据应用程序进程传递过来的一系列数据来又一次设置由參数client所描写叙述的一个应用程序窗体的大小和可见性等信息。而当一个应用程序窗体的大小或者可见性发生变化以后,系统中当前得到焦点的窗体,以及输入法窗体和壁纸窗体等均可能会发生变化,而且也会对其余窗体产生影响,所以,这时候WindowManagerService类的成员函数relayoutWindow就会对系统中的窗体的布局进行又一次调整。对系统中的窗体的布局进行又一次调整的过程是整个WindowManagerService服务最为复杂和核心的内容。咱们相同是在后面的文章中再具体分析。
现在,咱们就主要分析參数client所描写叙述的一个应用程序窗体的画图表面的建立过程。
WindowManagerService类的成员函数relayoutWindow首先得到与參数client所相应的一个WindowState对象win,这是经过调用WindowManagerService类的成员函数windowForClientLocked来实现的。假设这个相应的WindowState对象win不存在,那么就说明应用程序进程所请求处理的应用程序窗体不存在,这时候WindowManagerService类的成员函数relayoutWindow就直接返回一个0值给应用程序进程。
WindowManagerService类的成员函数relayoutWindow接下来推断參数client所描写叙述的一个应用程序窗体是不是可见的。一个窗体仅仅有在可见的状况下。WindowManagerService服务才会为它建立一个画图表面。 一个窗体是否可见由下面两个条件决定:
1. 參数viewVisibility的值等于View.VISIBLE,表示应用程序进程请求将它设置为可见的。
2. WindowState对象win的成员变量mAppToken不等于null,并且它所描写叙述的一个AppWindowToken对象的成员变量clientHidden的值等于false。这意味着參数client所描写叙述的窗体是一个应用程序窗体,即一个Activity组件窗体,并且这个Activity组件当前是处于可见状态的。当一个Activity组件当前是处于不可见状态时,它的窗体就也必须是处于不可见状态。
注意。当WindowState对象win的成员变量mAppToken等于null时。仅仅要知足条件1就可以了。因为这时候參数client所描写叙述的窗体不是一个Activity组件窗体。它的可见性不像Activity组件窗体同样受到Activity组件的可见性影响。
咱们假设參数client所描写叙述的是一个应用程序窗体,并且这个应用程序窗体是可见的,那么WindowManagerService类的成员函数relayoutWindow接下来就会调用WindowState对象win的winAnimator的函数createSurfaceLocked来为它建立一个画图表面。假设这个画图表面能建立成功,那么WindowManagerService类的成员函数relayoutWindow就会将它的内容复制到输出參数outSource所描写叙述的一个Surface对象去,以便可以将它返回给应用程序进程处理。还有一方面,假设这个画图表面不能建立成功。那么WindowManagerService类的成员函数relayoutWindow就会将输出參数outSource所描写叙述的一个Surface对象的内容释放掉,以便应用程序进程知道该Surface对象所描写叙述的画图表面已经失效了。
接下来,咱们就继续分析WindowState类的winAnimator的成员函数createSurfaceLocked的实现,以便可以了解一个应用程序窗体的画图表面的建立过程。
SurfaceControl createSurfaceLocked() { final WindowState w = mWin; if (mSurfaceControl == null) { if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG, "createSurface " + this + ": mDrawState=DRAW_PENDING"); mDrawState = DRAW_PENDING; if (w.mAppToken != null) { if (w.mAppToken.mAppAnimator.animation == null) { w.mAppToken.allDrawn = false; w.mAppToken.deferClearAllDrawn = false; } else { // Currently animating, persist current state of allDrawn until animation // is complete. w.mAppToken.deferClearAllDrawn = true; } } mService.makeWindowFreezingScreenIfNeededLocked(w); int flags = SurfaceControl.HIDDEN; final WindowManager.LayoutParams attrs = w.mAttrs; if (mService.isSecureLocked(w)) { flags |= SurfaceControl.SECURE; } int width; int height; if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) { // for a scaled surface, we always want the requested // size. width = w.mRequestedWidth; height = w.mRequestedHeight; } else { width = w.mCompatFrame.width(); height = w.mCompatFrame.height(); } // Something is wrong and SurfaceFlinger will not like this, // try to revert to sane values if (width <= 0) { width = 1; } if (height <= 0) { height = 1; } float left = w.mFrame.left + w.mXOffset; float top = w.mFrame.top + w.mYOffset; // Adjust for surface insets. width += attrs.surfaceInsets.left + attrs.surfaceInsets.right; height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom; left -= attrs.surfaceInsets.left; top -= attrs.surfaceInsets.top; if (DEBUG_VISIBILITY) { Slog.v(TAG, "Creating surface in session " + mSession.mSurfaceSession + " window " + this + " w=" + width + " h=" + height + " x=" + left + " y=" + top + " format=" + attrs.format + " flags=" + flags); } // We may abort, so initialize to defaults. mSurfaceShown = false; mSurfaceLayer = 0; mSurfaceAlpha = 0; mSurfaceX = 0; mSurfaceY = 0; w.mLastSystemDecorRect.set(0, 0, 0, 0); mHasClipRect = false; mClipRect.set(0, 0, 0, 0); mLastClipRect.set(0, 0, 0, 0); // Set up surface control with initial size. try { mSurfaceW = width; mSurfaceH = height; final boolean isHwAccelerated = (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format; if (!PixelFormat.formatHasAlpha(attrs.format) && attrs.surfaceInsets.left == 0 && attrs.surfaceInsets.top == 0 && attrs.surfaceInsets.right == 0 && attrs.surfaceInsets.bottom == 0) { flags |= SurfaceControl.OPAQUE; } mSurfaceFormat = format; if (DEBUG_SURFACE_TRACE) { mSurfaceControl = new SurfaceTrace(//新建一个SurfaceTrace对象 mSession.mSurfaceSession, attrs.getTitle().toString(), width, height, format, flags); } else { mSurfaceControl = new SurfaceControl( mSession.mSurfaceSession, attrs.getTitle().toString(), width, height, format, flags); }
在建立一个应用程序窗体的画图表面以前。咱们需要知道下面数据:
1. 应用程序窗体它所执行的应用程序进程的PID。
2. 与应用程序窗体它所执行的应用程序进程所关联的一个SurfaceSession对象。
3. 应用程序窗体的标题。
4. 应用程序窗体的像素格式。
5. 应用程序窗体的宽度。
6. 应用程序窗体的高度。
7. 应用程序窗体的图形缓冲区属性标志。
第1个和第2个数据可以经过当前正在处理的WindowState对象的成员变量mSession所描写叙述的一个Session对象的成员变量mPid和mSurfaceSession来得到。第3个和第4个数据可以经过当前正在处理的WindowState对象的成员变量mAttr所描写叙述的一个WindowManager.LayoutParams对象的成员函数getTitle以及成员变量format来得到;接下来咱们就分析后面3个数据是怎样得到的。
WindowState类的成员变量mFrame的类型为Rect。它用来描写叙述应用程序窗体的位置和大小,它们是由WindowManagerService服务依据屏幕大小以及其余属性计算出来的。所以,经过调用过它的成员函数width和height就可以获得要建立画图表面的应用程序窗体的宽度和高度。并且保存在变量w和h中。
但是,当当前正在处理的WindowState对象的成员变量mAttr所描写叙述的一个WindowManager.LayoutParams对象的成员变量flags的LayoutParams.FLAG_SCALED位不等于0时,就说明应用程序进程指定了该应用程序窗体的大小,这时候指定的应用程序窗体的宽度和高度就保存在WindowState类的成员变量mRequestedWidth和mRequestedHeight中,所以,咱们就需要将当前正在处理的WindowState对象的成员变量mRequestedWidth和mRequestedHeight的值分别保存在变量w和h中。
通过上述两步计算以后,假设获得的变量w和h等于0,那么就说明当前正在处理的WindowState对象所描写叙述的应用程序窗体的大小尚未通过计算。或者尚未被指定过,这时候就需要将它们的值设置为1。避免接下来建立一个大小为0的画图表面。
假设当前正在处理的WindowState对象的成员变量mAttr所描写叙述的一个WindowManager.LayoutParams对象的成员变量memoryType的值等于MEMORY_TYPE_PUSH_BUFFERS,那么就说明正在处理的应用程序窗体不拥有专属的图形缓冲区,这时候就需要将用来描写叙述正在处理的应用程序窗体的图形缓冲区属性标志的变量flags的Surface.PUSH_BUFFERS位设置为1。
此外。假设当前正在处理的WindowState对象的成员变量mAttr所描写叙述的一个WindowManager.LayoutParams对象的成员变量flags的WindowManager.LayoutParams.FLAG_SECURE位不等于0,那么就说明正在处理的应用程序窗体的界面是安全的。便是受保护的。这时候就需要将用来描写叙述正在处理的应用程序窗体的图形缓冲区属性标志的变量flags的Surface.SECURE位设置为1。当一个应用程序窗体的界面是受保护时,SurfaceFlinger服务在运行截屏功能时,就不能把它的界面截取下来。
上述数据准备就绪以后,就可以建立当前正在处理的WindowState对象所描写叙述的一个应用程序窗体的画图表面了,只是在建立以前,还会初始化该WindowState对象的下面成员变量:
--mReportDestroySurface的值被设置为false。当一个应用程序窗体的画图表面被销毁时,WindowManagerService服务就会将对应的WindowState对象的成员变量mReportDestroySurface的值设置为true,表示要向该应用程序窗体所执行在应用程序进程发送一个画图表面销毁通知。
--mSurfacePendingDestroy的值被设置为false。当一个应用程序窗体的画图表面正在等待销毁时,WindowManagerService服务就会将对应的WindowState对象的成员变量mReportDestroySurface的值设置为true。
--mDrawPending的值被设置为true。当一个应用程序窗体的画图表面处于建立以后并且绘制以前时,WindowManagerService服务就会将对应的WindowState对象的成员变量mDrawPending的值设置为true,以表示该应用程序窗体的画图表面正在等待绘制。
--mCommitDrawPending的值被设置为false。
当一个应用程序窗体的画图表面绘制完毕以后并且可以显示出来以前时,WindowManagerService服务就会将对应的WindowState对象的成员变量mCommitDrawPending的值设置为true。以表示该应用程序窗体正在等待显示。
--mReadyToShow的值被设置为false。有时候当一个应用程序窗体的画图表面绘制完毕并且可以显示出来以后,由于与该应用程序窗体所关联的一个Activity组件的其余窗体还未准备好显示出来。这时候WindowManagerService服务就会将对应的WindowState对象的成员变量mReadyToShow的值设置为true,以表示该应用程序窗体需要延迟显示出来。即需要等到与该应用程序窗体所关联的一个Activity组件的其余窗体也可以显示出来以后再显示。
--假设成员变量mAppToken的值不等于null。那么就需要将它所描写叙述的一个AppWindowToken对象的成员变量allDrawn的值设置为false。
从前面Android应用程序窗体(Activity)与WindowManagerService服务的链接过程分析一文可以知道,一个AppWindowToken对象用来描写叙述一个Activity组件的,当该AppWindowToken对象的成员变量allDrawn的值等于true时,就表示属于该Activity组件的所有窗体都已经绘制完毕了。
--mSurfaceShown的值被设置为false,表示应用程序窗体尚未显示出来,它是用来显示调试信息的。
--mSurfaceLayer的值被设置为0。表示应用程序窗体的Z轴位置。它是用来显示调试信息的。
--mSurfaceAlpha的值被设置为1,表示应用程序窗体的透明值。它是用来显示调试信息的。
--mSurfaceX的值被设置为0,表示应用程序程序窗体的X轴位置,它是用来显示调试信息的。
--mSurfaceY的值被设置为0。表示应用程序程序窗体的Y轴位置,它是用来显示调试信息的。
--mSurfaceW的值被设置为w,表示应用程序程序窗体的宽度,它是用来显示调试信息的。
--mSurfaceH的值被设置为h。表示应用程序程序窗体的高度。它是用来显示调试信息的。
一个应用程序窗体的画图表面在建立完毕以后,函数就会将获得的一个Surface对象保存在当前正在处理的WindowState对象的成员变量mSurface中。注意。假设建立画图表面失败,并且从Surface类的构造函数抛出来的异常的类型为Surface.OutOfResourcesException,那么就说明系统当前的内存不足了。这时候函数就会调用WindowManagerService类的成员函数reclaimSomeSurfaceMemoryLocked来回收内存。
假设一切正常。那么函数接下来还会设置当前正在处理的WindowState对象所描写叙述应用程序窗体的下面属性:
1. X轴和Y轴位置。前面提到。WindowState类的成员变量mFrame是用来描写叙述应用程序窗体的位置和大小的,当中。位置就是经过它所描写叙述的一个Rect对象的成员变量left和top来表示的,它们分别应用窗体在X轴和Y轴上的位置。此外。当一个WindowState对象所描写叙述的应用程序窗体是一个壁纸窗体时。该WindowState对象的成员变量mXOffset和mYOffset用来描写叙述壁纸窗体相对当前要显示的窗体在X轴和Y轴上的偏移量。所以,将WindowState类的成员变量mXOffset的值加上另一个成员变量mFrame所描写叙述的一个Rect对象的成员变量left的值,就可以获得一个应用程序窗体在X轴上的位置,相同。将WindowState类的成员变量mYOffset的值加上另一个成员变量mFrame所描写叙述的一个Rect对象的成员变量top的值。就可以获得一个应用程序窗体在Y轴上的位置。终于获得的位置值就分别保存在WindowState类的成员变量mSurfaceX和mSurfaceY,并且会调用WindowState类的成员变量mSurface所描写叙述的一个Surface对象的成员函数setPosition来将它们设置到SurfaceFlinger服务中去。
2. Z轴位置。
在前面Android应用程序窗体(Activity)与WindowManagerService服务的链接过程分析一文中提到,WindowState类的成员变量mAnimLayer用来描写叙述一个应用程序窗体的Z轴位置,所以。这里就会先将它保存在WindowState类的另一个成员变量mSurfaceLayer中。而后再调用WindowState类的成员变量mSurface所描写叙述的一个Surface对象的成员函数setLayer来将它设置到SurfaceFlinger服务中去。
3. 抖动标志。
假设WindowState类的成员变量mAttr所描写叙述的一个WindowManager.LayoutParams对象的成员变量flags的WindowManager.LayoutParams.FLAG_DITHER位不等于0,那么就说明一个应用程序窗体的图形缓冲区在渲染时。需要进行抖动处理,这时候就会调用WindowState类的成员变量mSurface所描写叙述的一个Surface对象的成员函数setLayer来将相应的应用程序窗体的图形缓冲区的属性标志的Surface.SURFACE_DITHER位设置为1。
4. 显示状态。
由于当前正在处理的WindowState对象所描写叙述的一个应用程序窗体的画图表面刚刚建立出来,所以。咱们就需要通知SurfaceFlinger服务将它隐藏起来,这是经过调用当前正在处理的WindowState对象的成员变量mSurface所描写叙述的一个Surface对象的成员变量hide来实现的。
这时候还会将当前正在处理的WindowState对象的成员变量mSurfaceShown和mLastHidden的值分别设置为false和true。以表示相应的应用程序窗体是处于隐藏状态的。
注意,为了不SurfaceFlinger服务每设置一个应用程序窗体属性就又一次渲染一次系统的UI,上述4个属性设置需要在一个事务中进行,这样就可以避免出现界面闪烁。咱们经过调用Surface类的静态成员函数openTransaction和closeTransaction就可以分别在SurfaceFlinger服务中打开和关闭一个事务。
还有另一个地方需要注意的是。在设置应用程序窗体属性的过程当中,假设抛出了一个RuntimeException异常。那么就说明系统当前的内存不足了。这时候函数也会调用WindowManagerService类的成员函数reclaimSomeSurfaceMemoryLocked来回收内存。
接下来,咱们就继续分析Surface类的构造函数的实现,以便可以了解一个应用程序窗体的画图表面的具体建立过程。
如下咱们再来看看SurfaceControl的构造函数。主要是调用了nativeCreate函数
public SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags) throws OutOfResourcesException { if (session == null) { throw new IllegalArgumentException("session must not be null"); } if (name == null) { throw new IllegalArgumentException("name must not be null"); } if ((flags & SurfaceControl.HIDDEN) == 0) { Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set " + "to ensure that they are not made visible prematurely before " + "all of the surface's properties have been configured. " + "Set the other properties and make the surface visible within " + "a transaction. New surface name: " + name, new Throwable()); } mName = name; mNativeObject = nativeCreate(session, name, w, h, format, flags); if (mNativeObject == 0) { throw new OutOfResourcesException( "Couldn't allocate SurfaceControl native object"); } mCloseGuard.open("release"); }
咱们来看下这个nativeCreate就是经过上篇博客说的SurfaceComposerClient来获取一个c++层的SurfaceControl
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj, jstring nameStr, jint w, jint h, jint format, jint flags) { ScopedUtfChars name(env, nameStr); sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj)); sp<SurfaceControl> surface = client->createSurface( String8(name.c_str()), w, h, format, flags); if (surface == NULL) { jniThrowException(env, OutOfResourcesException, NULL); return 0; } surface->incStrong((void *)nativeCreate); return reinterpret_cast<jlong>(surface.get()); }
而nativeCreate返回的值保存在mNativeObject 中,后面所有一些操做函数都是经过mNativeObject 传到JNI进行操做的。
public void setLayer(int zorder) { checkNotReleased(); nativeSetLayer(mNativeObject, zorder); } public void setPosition(float x, float y) { checkNotReleased(); nativeSetPosition(mNativeObject, x, y); } public void setSize(int w, int h) { checkNotReleased(); nativeSetSize(mNativeObject, w, h);
在WMS中的Surface是保存在每一个Activity相应的WindowState的winAnimator的mSurfaceControl成员变量中。而Activity的Surface是保存在ViewRootImpl的Surface是经过WMS的outSurface保存在ViewRoot的mSurface中。
因此最后无论在WMS的SurfaceControl仍是ViewRootImpl的Surface最后在c++层用的是同一个对象。
至此,咱们就分析完毕Android应用程序窗体的画图表面的建立过程了。经过这个过程咱们就可以知道:
1. 每一个应用程序窗体都相应有两个Java层的Surface对象,当中一个是在WindowManagerService服务这一側建立的。而另一个是在应用程序进程这一側建立的。
2. 在WindowManagerService服务这一側建立的Java层的Surface对象在C++层关联有一个SurfaceControl对象,用来设置应用窗体的属性,好比,大小和位置等。
3. 在应用程序进程这一側建立的ava层的Surface对象在C++层关联有一个Surface对象。用来绘制应用程序窗品的UI。
理解上述三个结论对理解Android应用程序窗体的实现框架以及WindowManagerService服务的实现都很重要。
一个应用程序窗体的画图表面在建立完毕以后,接下来应用程序进程就可以在上面绘制它的UI了。