android6.0 Activity(四) Surface创建
原文:http://blog.csdn.net/luoshengyang/article/details/8303098,原文代码比较老了,但是核心不变。在原文基础上修改了一些代码,以及加入自己少量的理解。
在上一篇博客中,我们分析了应用程序窗口连接到WindowManagerService服务的过程。在这个过程中,WindowManagerService服务会为应用程序窗口创建过一个到SurfaceFlinger服务的连接。有了这个连接之后,WindowManagerService服务就可以为应用程序窗口创建绘图表面了,以便可以用来渲染窗口的UI。在本文中,我们就详细分析应用程序窗口的绘图表面的创建过程。
一、Surface创建过程介绍
这节主要是把http://blog.csdn.net/luoshengyang/article/details/8303098这篇博客复制过来的
每一个在C++层实现的应用程序窗口都需要有一个绘图表面,然后才可以将自己的UI表现出来。这个绘图表面是需要由应用程序进程请求SurfaceFlinger服务来创建的,在SurfaceFlinger服务内部使用一个Layer对象来描述,同时,SurfaceFlinger服务会返回一个实现了ISurface接口的Binder本地对象给应用程序进程,于是,应用程序进程就可以获得一个实现了ISurface接口的Binder代理对象。有了这个实现了ISurface接口的Binder代理对象之后,在C++层实现的应用程序窗口就可以请求SurfaceFlinger服务分配图形缓冲区以及渲染已经填充好UI数据的图形缓冲区了。
对于在Java层实现的Android应用程序窗口来说,它也需要请求SurfaceFlinger服务为它创建绘图表面,这个绘图表面使用一个Surface对象来描述。由于在Java层实现的Android应用程序窗口还要接受WindowManagerService服务管理,因此,它的绘图表面的创建流程就会比在C++层实现的应用程序窗口复杂一些。具体来说,就是在在Java层实现的Android应用程序窗口的绘图表面是通过两个Surface对象来描述,一个是在应用程序进程这一侧创建的,另一个是在WindowManagerService服务这一侧创建的,它们对应于SurfaceFlinger服务这一侧的同一个Layer对象,如图所示:
在应用程序进程这一侧,每一个应用程序窗口,即每一个Activity组件,都有一个关联的Surface对象,这个Surface对象是保在在一个关联的ViewRootImpl对象的成员变量mSurface中的。这里我们只关注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开始,分析应用程序窗口的绘图表面的创建过程,如图所示:
二、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的进程间通信请求的。
2.1 WMS中创建SurfaceControl
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类的构造函数的实现,以便可以了解一个应用程序窗口的绘图表面的详细创建过程。
2.2 SurfaceControl在JNI层创建了SurfaceControl C++层对象那个
下面我们再来看看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了。
android6.0 Activity(四) Surface创建相关推荐
- android6.0 Activity(一) Activity创建 初始化
之前在http://blog.csdn.net/kc58236582/article/details/50069785这篇博客中,讲述了一个Activity如何通过AMS启动的.还有http:/ ...
- 项目疑难杂症记录(四):Activity被重新创建的原因分析
在项目中遇到一个奇怪的Bug,插上带有升级包固件的U盘,选择升级框中的放弃按钮,Activity被onDestroy,随后又重新onCreate,相应的图片和日志如下: [一] 现象和日志 1.升级框 ...
- Android6.0动态权限申请及RxPermissions权限库使用
一.AndroidManifest.xml 所有权限列表: 访问登记属性 android.permission.ACCESS_CHECKIN_PROPERTIES ,读取或写入登记check-in数据 ...
- Android6.0 Log的工作机制
Android6.0log新机制 Android6.0后Android 日志系统做了很大的改变,但是对于应用层改变是透明的,原因是由于日志系统只是针对底层做了相应改变.之前的系统通过读写设备文件的方式 ...
- android6.0源码分析之Camera API2.0下的初始化流程分析
1.Camera2初始化的应用层流程分析 Camera2的初始化流程与Camera1.0有所区别,本文将就Camera2的内置应用来分析Camera2.0的初始化过程.Camera2.0首先启动的是C ...
- Android 系统(98)---Android app 在线更新那点事儿(适配Android6.0、7.0、8.0)
Android app 在线更新那点事儿(适配Android6.0.7.0.8.0) 一.前言 app在线更新是一个比较常见需求,新版本发布时,用户进入我们的app,就会弹出更新提示框,第一时间更新新 ...
- Android6.0 Sensor架构和问题分析
本文在借鉴网友的资料后再重新梳理了一遍,都是站在前人的基础.巨人的肩膀上再次总结分析出来的,仅供大家参考! 本文主要描述了在Android 6.0系统.MTK6755平台上sensor相关软硬件的体系 ...
- 探讨Android6.0及以上系统APP常驻内存(保活)实现-争宠篇
探讨Android6.0及以上系统APP常驻内存(保活)实现-争宠篇 (转载请声明出处:http://blog.csdn.net/andrexpert/article/details/75045678 ...
- android6.0中app crash流程分析
要根据这个流程分析一下如何在应用中截获系统的app crash弹框,然后做到更人性化 基于Android 6.0的源码剖析, 分析Android应用Crash是如何处理的. /frameworks/b ...
- Android6.0执行时权限解析,RxPermissions的使用,自己封装一套权限框架
Android6.0执行时权限解析,RxPermissions的使用.自己封装一套权限框架 在Android6.0中,新添加了一个执行时的权限,我相信非常多人都已经知道了.预计也知道怎么用了,这篇博客 ...
最新文章
- 统计学和机器学习到底有什么区别?
- DICOM医学图像处理:Dcmtk与fo-dicom保存文件的不同设计模式之“同步VS异步”+“单线程VS多线程”...
- if you can not make a solid foundation
- emwin之在中断服务程序中创建窗口的结果
- Activity 与ListActivity的区别
- 启动LINUX下的TFTP服务器
- python123添加列表元素_Python之列表
- vbs连接oracle11,vbs连oracle数据库
- PHP微信模版消息有时收不到_PHP微信开发之模板消息回复
- jsp java ldquo_添加jsp · yayaangel/java201521123103 - Gitee.com
- [北航软工]技术规格说明书
- 中文版通用工程师软件DPS 别克雪佛兰编程改装
- 未安装任何音频输出设备
- redis通过hscan导入大hash key
- 多路IO转接服务器——epoll模型
- 第二章:HLK-7621开发板介绍
- 图像处理之matlab中fspecial函数用法详解
- 酒桌上的学问(搜集整理帖)
- PNAS:植物香豆素塑造拟南芥合成根系微生物组的组成
- 彩色图像直方图均衡化