Android应用程序窗口设计框架三

performTraversals函数相当复杂,其主要实现以下几个重要步骤:

1.执行窗口测量;

2.执行窗口注册;

3.执行窗口布局;

4.执行窗口绘图;

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
private void performTraversals() {
     // cache mView since it is used so much below...
     final View host = mView;
     if (host == null || !mAdded)
         return ;
     mWillDrawSoon = true ;
     boolean windowSizeMayChange = false ;
     boolean newSurface = false ;
     boolean surfaceChanged = false ;
     WindowManager.LayoutParams lp = mWindowAttributes;
     int desiredWindowWidth;
     int desiredWindowHeight;
     final View.AttachInfo attachInfo = mAttachInfo;
     final int viewVisibility = getHostVisibility();
     boolean viewVisibilityChanged = mViewVisibility != viewVisibility
             || mNewSurfaceNeeded;
     WindowManager.LayoutParams params = null ;
     if (mWindowAttributesChanged) {
         mWindowAttributesChanged = false ;
         surfaceChanged = true ;
         params = lp;
     }
     ...
     /****************执行窗口测量******************/
     boolean layoutRequested = mLayoutRequested && !mStopped;
     if (layoutRequested) {
         ...
         // Ask host how big it wants to be
         windowSizeMayChange |= measureHierarchy(host, lp, res,
                 desiredWindowWidth, desiredWindowHeight);
     }
     ...
     /****************向WMS服务添加窗口******************/
     if (mFirst || windowShouldResize || insetsChanged ||
             viewVisibilityChanged || params != null ) {
         ...
         try {
             final int surfaceGenerationId = mSurface.getGenerationId();
             relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
             ...
         } catch (RemoteException e) {
         }
         ...
         if (!mStopped) {
             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                     (relayoutResult&WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE) != 0 );
             if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                     || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
                 ...
                  // Ask host how big it wants to be
                 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                 ...
             }
         }
     }
     /****************执行窗口布局******************/
     final boolean didLayout = layoutRequested && !mStopped;
     boolean triggerGlobalLayoutListener = didLayout
             || attachInfo.mRecomputeGlobalAttributes;
     if (didLayout) {
         performLayout();
         ...
     }
     ...
     /****************查找窗口焦点******************/
     boolean skipDraw = false ;
     if (mFirst) {
         // handle first focus request
         if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
                 + mView.hasFocus());
         if (mView != null ) {
             if (!mView.hasFocus()) {
                 mView.requestFocus(View.FOCUS_FORWARD);
                 mFocusedView = mRealFocusedView = mView.findFocus();
                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
                         + mFocusedView);
             } else {
                 mRealFocusedView = mView.findFocus();
                 if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
                         + mRealFocusedView);
             }
         }
         if ((relayoutResult&WindowManagerImpl.RELAYOUT_RES_ANIMATING) != 0 ) {
             // The first time we relayout the window, if the system is
             // doing window animations, we want to hold of on any future
             // draws until the animation is done.
             mWindowsAnimating = true ;
         }
     } else if (mWindowsAnimating) {
         skipDraw = true ;
     }
     /****************执行窗口绘制******************/
     mFirst = false ;
     mWillDrawSoon = false ;
     mNewSurfaceNeeded = false ;
     mViewVisibility = viewVisibility;
     ...
     boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
             viewVisibility != View.VISIBLE;
     if (!cancelDraw && !newSurface) {
         if (!skipDraw || mReportNextDraw) {
             if (mPendingTransitions != null && mPendingTransitions.size() > 0 ) {
                 for ( int i = 0 ; i < mPendingTransitions.size(); ++i) {
                     mPendingTransitions.get(i).startChangingAnimations();
                 }
                 mPendingTransitions.clear();
             }
             performDraw();
         }
     } else {
         if (viewVisibility == View.VISIBLE) {
             // Try again
             scheduleTraversals();
         } else if (mPendingTransitions != null && mPendingTransitions.size() > 0 ) {
             for ( int i = 0 ; i < mPendingTransitions.size(); ++i) {
                 mPendingTransitions.get(i).endChangingAnimations();
             }
             mPendingTransitions.clear();
         }
     }
}
performMeasure

frameworks\base\core\java\android\view\ViewRootImpl.java

?
1
2
3
4
5
6
7
8
private void performMeasure( int childWidthMeasureSpec, int childHeightMeasureSpec) {
  Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure" );
  try {
   mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  } finally {
   Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  }
}
relayoutWindow

frameworks\base\core\java\android\view\ViewRootImpl.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
         boolean insetsPending) throws RemoteException {
     ...
     int relayoutResult = sWindowSession.relayout(
             mWindow, mSeq, params,
             ( int ) (mView.getMeasuredWidth() * appScale + 0 .5f),
             ( int ) (mView.getMeasuredHeight() * appScale + 0 .5f),
             viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0 ,
             mWinFrame, mPendingContentInsets, mPendingVisibleInsets,
             mPendingConfiguration, mSurface);
     ...
     return relayoutResult;
}

这里通过前面获取的IWindowSession代理对象请求WMS服务执行窗口布局,mSurface是ViewRootImpl的成员变量

?
1
private final Surface mSurface = new Surface();

frameworks\base\core\java\android\view\ Surface.java

?
1
2
3
4
5
6
7
public Surface() {
     checkHeadless();
     if (DEBUG_RELEASE) {
         mCreationStack = new Exception();
     }
     mCanvas = new CompatibleCanvas();
}

该Surface构造函数仅仅创建了一个CompatibleCanvas对象,并没有对该Surface进程native层的初始化,到此我们知道应用程序进程为每个窗口对象都创建了一个Surface对象。并且将该Surface通过跨进程方式传输给WMS服务进程,我们知道,在Android系统中,如果一个对象需要在不同进程间传输,必须实现Parcelable接口,Surface类正好实现了Parcelable接口。ViewRootImpl通过IWindowSession接口请求WMS的完整过程如下:

frameworks\base\core\java\android\view\IWindowSession.java$ Proxy

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public int relayout(android.view.IWindow window, int seq,
         android.view.WindowManager.LayoutParams attrs, int requestedWidth,
         int requestedHeight, int viewVisibility, int flags,
         android.graphics.Rect outFrame,
         android.graphics.Rect outOverscanInsets,
         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 )));
         _data.writeInt(seq);
         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(flags);
         mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0 );
         _reply.readException();
         _result = _reply.readInt();
         if (( 0 != _reply.readInt())) {
             outFrame.readFromParcel(_reply);
         }
         if (( 0 != _reply.readInt())) {
             outOverscanInsets.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;
}

从该函数的实现可以看出,应用程序进程中创建的Surface对象并没有传递到WMS服务进程,只是读取WMS服务进程返回来的Surface。那么WMS服务进程是如何响应应用程序进程布局请求的呢?
frameworks\base\core\java\android\view\IWindowSession.java$ Stub

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public boolean onTransact( int code, android.os.Parcel data,
         android.os.Parcel reply, int flags) throws android.os.RemoteException {
     switch (code) {
     case TRANSACTION_relayout: {
         data.enforceInterface(DESCRIPTOR);
         android.view.IWindow _arg0;
         _arg0 = android.view.IWindow.Stub.asInterface(data.readStrongBinder());
         int _arg1;
         _arg1 = data.readInt();
         android.view.WindowManager.LayoutParams _arg2;
         if (( 0 != data.readInt())) {
             _arg2 = android.view.WindowManager.LayoutParams.CREATOR
                     .createFromParcel(data);
         } else {
             _arg2 = null ;
         }
         int _arg3;
         _arg3 = data.readInt();
         int _arg4;
         _arg4 = data.readInt();
         int _arg5;
         _arg5 = data.readInt();
         int _arg6;
         _arg6 = data.readInt();
         android.graphics.Rect _arg7;
         _arg7 = new android.graphics.Rect();
         android.graphics.Rect _arg8;
         _arg8 = new android.graphics.Rect();
         android.graphics.Rect _arg9;
         _arg9 = new android.graphics.Rect();
         android.graphics.Rect _arg10;
         _arg10 = new android.graphics.Rect();
         android.content.res.Configuration _arg11;
         _arg11 = new android.content.res.Configuration();
         android.view.Surface _arg12;
         _arg12 = new android.view.Surface();
         int _result = this .relayout(_arg0, _arg1, _arg2, _arg3, _arg4,
                 _arg5, _arg6, _arg7, _arg8, _arg9, _arg10, _arg11, _arg12);
         reply.writeNoException();
         reply.writeInt(_result);
         if ((_arg7 != null )) {
             reply.writeInt( 1 );
             _arg7.writeToParcel(reply,
                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
         } else {
             reply.writeInt( 0 );
         }
         if ((_arg8 != null )) {
             reply.writeInt( 1 );
             _arg8.writeToParcel(reply,
                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
         } else {
             reply.writeInt( 0 );
         }
         if ((_arg9 != null )) {
             reply.writeInt( 1 );
             _arg9.writeToParcel(reply,
                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
         } else {
             reply.writeInt( 0 );
         }
         if ((_arg10 != null )) {
             reply.writeInt( 1 );
             _arg10.writeToParcel(reply,
                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
         } else {
             reply.writeInt( 0 );
         }
         if ((_arg11 != null )) {
             reply.writeInt( 1 );
             _arg11.writeToParcel(reply,
                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
         } else {
             reply.writeInt( 0 );
         }
         if ((_arg12 != null )) {
             reply.writeInt( 1 );
             _arg12.writeToParcel(reply,
                     android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
         } else {
             reply.writeInt( 0 );
         }
         return true ;
     }
     }
}

该函数可以看出,WMS服务在响应应用程序进程请求添加窗口时,首先在当前进程空间创建一个Surface对象,然后调用Session的relayout()函数进一步完成窗口添加过程,最后将WMS服务中创建的Surface返回给应用程序进程。
\
到目前为止,在应用程序进程和WMS服务进程分别创建了一个Surface对象,但是他们调用的都是Surface的无参构造函数,在该构造函数中并未真正初始化native层的Surface,那native层的Surface是在那里创建的呢?
frameworks\base\services\java\com\android\server\wm\ Session.java

?
1
2
3
4
5
6
7
8
9
10
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
         int requestedWidth, int requestedHeight, int viewFlags,
         int flags, Rect outFrame, Rect outContentInsets,
         Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
     int res = mService.relayoutWindow( this , window, seq, attrs,
             requestedWidth, requestedHeight, viewFlags, flags,
             outFrame, outContentInsets, outVisibleInsets,
             outConfig, outSurface);
     return res;
}

frameworks\base\services\java\com\android\server\wm\ WindowManagerService.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public int relayoutWindow(Session session, IWindow client, int seq,
         WindowManager.LayoutParams attrs, int requestedWidth,
         int requestedHeight, int viewVisibility, int flags,
         Rect outFrame, Rect outContentInsets,
         Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
     ...
     synchronized (mWindowMap) {
         // TODO(cmautner): synchronize on mAnimator or win.mWinAnimator.
         WindowState win = windowForClientLocked(session, client, false );
         if (win == null ) {
             return 0 ;
         }
         ...
         if (viewVisibility == View.VISIBLE &&
                 (win.mAppToken == null || !win.mAppToken.clientHidden)) {
             ...
             try {
                 if (!win.mHasSurface) {
                     surfaceChanged = true ;
                 }
                 //创建Surface
                 Surface surface = winAnimator.createSurfaceLocked();
                 if (surface != null ) {
                     outSurface.copyFrom(surface);
                 } else {
                     outSurface.release();
                 }
             } catch (Exception e) {
                 ...
             }
             ...
         }
         ...
     }
     ...
}

frameworks\base\services\java\com\android\server\wm\WindowStateAnimator.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Surface createSurfaceLocked() {
     if (mSurface == null ) {
         ...
         try {
             ...
             if (DEBUG_SURFACE_TRACE) {
                 mSurface = new SurfaceTrace(
                         mSession.mSurfaceSession, mSession.mPid,
                         attrs.getTitle().toString(),
                         0 , w, h, format, flags);
             } else {
                 mSurface = new Surface(
                     mSession.mSurfaceSession, mSession.mPid,
                     attrs.getTitle().toString(),
                     0 , w, h, format, flags);
             }
             mWin.mHasSurface = true ;
         } catch (Surface.OutOfResourcesException e) {
             ...
         }
         Surface.openTransaction();
         ...
     }
     return mSurface;
}

Surface创建过程
frameworks\base\core\java\android\view\Surface.java

?
1
2
3
4
5
6
7
8
9
10
public Surface(SurfaceSession s, int pid, String name, int display, int w, int h, int format, int flags)
     throws OutOfResourcesException {
     checkHeadless();
     if (DEBUG_RELEASE) {
         mCreationStack = new Exception();
     }
     mCanvas = new CompatibleCanvas();
     init(s,pid,name,display,w,h,format,flags);
     mName = name;
}

frameworks\base\core\jni\ android_view_Surface.cpp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static void Surface_init(
         JNIEnv* env, jobject clazz,
         jobject session,
         jint, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
{
     if (session == NULL) {
         doThrowNPE(env);
         return ;
     }
     SurfaceComposerClient* client =
             (SurfaceComposerClient*)env->GetIntField(session, sso.client);
     sp<surfacecontrol> surface;
     if (jname == NULL) {
         surface = client->createSurface(dpy, w, h, format, flags);
     } else {
         const jchar* str = env->GetStringCritical(jname, 0 );
         const String8 name(str, env->GetStringLength(jname));
         env->ReleaseStringCritical(jname, str);
         surface = client->createSurface(name, dpy, w, h, format, flags);
     }
     if (surface == 0 ) {
         jniThrowException(env, OutOfResourcesException, NULL);
         return ;
     }
     setSurfaceControl(env, clazz, surface);
}</surfacecontrol>

到此才算真正创建了一个可用于绘图的Surface,从上面的分析我们可以看出,在WMS服务进程端,其实创建了两个Java层的Surface对象,第一个Surface使用了无参构造函数,仅仅构造一个Surface对象而已,而第二个Surface却使用了有参构造函数,参数指定了图象宽高等信息,这个Java层Surface对象还会在native层请求SurfaceFlinger创建一个真正能用于绘制图象的native层Surface。最后通过浅拷贝的方式将第二个Surface复制到第一个Surface中,最后通过writeToParcel方式写回到应用程序进程。
\

到目前为止,应用程序和WMS一共创建了3个Java层Surface对象,如上图所示,而真正能用于绘图的Surface只有3号,那么3号Surface与2号Surface之间是什么关系呢?outSurface.copyFrom(surface)
frameworks\base\core\jni\ android_view_Surface.cpp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void Surface_copyFrom(JNIEnv* env, jobject clazz, jobject other)
{
     if (clazz == other)
         return ;
     if (other == NULL) {
         doThrowNPE(env);
         return ;
     }
     //得到当前Surface所引用的SurfaceControl对象
     const sp<surfacecontrol>& surface = getSurfaceControl(env, clazz);
     //得到源Surface所引用的SurfaceControl对象
     const sp<surfacecontrol>& rhs = getSurfaceControl(env, other);
     //如果它们引用的不是同一个SurfaceControl对象
     if (!SurfaceControl::isSameSurface(surface, rhs)) {
         setSurfaceControl(env, clazz, rhs);
     }
}
</surfacecontrol></surfacecontrol>

2号Surface引用到了3号Surface的SurfaceControl对象后,通过writeToParcel()函数写会到应用程序进程。
frameworks\base\core\jni\ android_view_Surface.cpp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static void Surface_writeToParcel(
         JNIEnv* env, jobject clazz, jobject argParcel, jint flags)
{
     Parcel* parcel = (Parcel*)env->GetIntField(
             argParcel, no.native_parcel);
     if (parcel == NULL) {
         doThrowNPE(env);
         return ;
     }
     const sp<surfacecontrol>& control(getSurfaceControl(env, clazz));
     if (control != NULL) {
         SurfaceControl::writeSurfaceToParcel(control, parcel);
     } else {
         sp<surface> surface(Surface_getSurface(env, clazz));
         if (surface != NULL) {
             Surface::writeToParcel(surface, parcel);
         } else {
             SurfaceControl::writeSurfaceToParcel(NULL, parcel);
         }
     }
     if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
         setSurfaceControl(env, clazz, NULL);
         setSurface(env, clazz, NULL);
     }
}
</surface></surfacecontrol>

由于2号Surface引用的SurfaceControl对象不为空,因此这里就将SurfaceControl对象写会给应用程序进程
frameworks\native\libs\gui\ Surface.cpp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
status_t SurfaceControl::writeSurfaceToParcel(
         const sp<surfacecontrol>& control, Parcel* parcel)
{
     sp<isurface> sur;
     uint32_t identity = 0 ;
     if (SurfaceControl::isValid(control)) {
         sur = control->mSurface;
         identity = control->mIdentity;
     }
     parcel->writeStrongBinder(sur!= 0 ? sur->asBinder() : NULL);
     parcel->writeStrongBinder(NULL);  // NULL ISurfaceTexture in this case.
     parcel->writeInt32(identity);
     return NO_ERROR;
}
</isurface></surfacecontrol>

写入Parcel包裹的对象顺序如下:

\

应用程序进程中的1号Surface通过readFromParcel()函数读取从WMS服务进程写回的Binder对象。
frameworks\base\core\jni\ android_view_Surface.cpp

?
1
2
3
4
5
6
7
8
9
10
11
12
static void Surface_readFromParcel(
         JNIEnv* env, jobject clazz, jobject argParcel)
{
     Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
     if (parcel == NULL) {
         doThrowNPE(env);
         return ;
     }
     sp<surface> sur(Surface::readFromParcel(*parcel));
     setSurface(env, clazz, sur);
}
</surface>

frameworks\native\libs\gui\ Surface.cpp

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sp<surface> Surface::readFromParcel( const Parcel& data) {
     Mutex::Autolock _l(sCachedSurfacesLock);
     sp<ibinder> binder(data.readStrongBinder());
     sp<surface> surface = sCachedSurfaces.valueFor(binder).promote();
     if (surface == 0 ) {
        surface = new Surface(data, binder);
        sCachedSurfaces.add(binder, surface);
     } else {
         // The Surface was found in the cache, but we still should clear any
         // remaining data from the parcel.
         data.readStrongBinder();  // ISurfaceTexture
         data.readInt32();         // identity
     }
     if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
         surface = 0 ;
     }
     cleanCachedSurfacesLocked();
     return surface;
}
</surface></ibinder></surface>

应用程序进程中的1号Surface按相反顺序读取WMS服务端返回过来的Binder对象等数据,并构造一个native层的Surface对象。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Surface::Surface( const Parcel& parcel, const sp<ibinder>& ref)
     : SurfaceTextureClient()
{
     mSurface = interface_cast<isurface>(ref);
     sp<ibinder> st_binder(parcel.readStrongBinder());
     sp<isurfacetexture> st;
     if (st_binder != NULL) {
         st = interface_cast<isurfacetexture>(st_binder);
     } else if (mSurface != NULL) {
         st = mSurface->getSurfaceTexture();
     }
     mIdentity   = parcel.readInt32();
     init(st);
}
</isurfacetexture></isurfacetexture></ibinder></isurface></ibinder>

每个Activity可以有一个或多个Surface,默认情况下一个Activity只有一个Surface,当Activity中使用SurfaceView时,就存在多个Surface。Activity默认surface是在relayoutWindow过程中由WMS服务创建的,然后回传给应用程序进程,我们知道一个Surface其实就是应用程序端的本地窗口,关于Surface的初始化过程这里就不在介绍。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值