Android 7.1 GUI系统-窗口管理WMS-Surface管理(四)

摘要:
静态最终NO_表面=0;//曲面已创建,但尚未绘制窗口。此时,曲面将隐藏。staticfinalintDRAW_待定=1;//窗口是第一次绘制,但曲面尚未显示。曲面将显示在下一个布局中。UI数据的绘制需要一个绘图板,这是BufferQueue的支持。无论是哪种窗口,它都需要向surfacefinger申请相应的层,以进一步获得缓冲区的使用权。当在视图根中执行视图树遍历,并从WMS请求曲面时,所有这些都将开始。具体来说,从relayWindow开始并重新布局窗口。应用程序要求WMS通过mWindowSession从surfacelinger申请画板。重新布局,然后通过最后一个参数mSurface返回结果。

Surface的管理

Surface是窗口能真正显示到物理屏幕上的基础,由surfaceflinger管理,可以通过WindowStateAnimator.java中的变量mDrawState来查看每个窗口相关的surface的状态。

surface5中状态:

WindowStateAnimator.java

//Surface还没有创建。

staticfinal int NO_SURFACE = 0;

//Surface已经创建,但是窗口还没绘制,这时surface是隐藏的。

staticfinal int DRAW_PENDING = 1;

//窗口已经完成了第一次绘制,但是surface还没有显示,Surface将会在下次layout时显示出来。

staticfinal int COMMIT_DRAW_PENDING = 2;

//窗口的绘制请求已经提交,surface还没真正的显示,这通常用于延迟显示surface直到这个token中的所有窗口都已准备显示。

staticfinal int READY_TO_SHOW = 3;

//窗口已经第一次在屏幕上显示出来。

staticfinal int HAS_DRAWN = 4;

  1. String drawStateToString() @WindowStateAnimator.java{
  2. switch (mDrawState) {
  3. case NO_SURFACE: return "NO_SURFACE";
  4. case DRAW_PENDING: return "DRAW_PENDING";
  5. case COMMIT_DRAW_PENDING: return "COMMIT_DRAW_PENDING";
  6. case READY_TO_SHOW: return "READY_TO_SHOW";
  7. case HAS_DRAWN: return "HAS_DRAWN";
  8. default: return Integer.toString(mDrawState);
  9. }
  10. }
  11. int mDrawState;

1),Surface的申请

WMS只负责窗口的层级和属性,surfaceflinger负责真正的将数据合成并显示到屏幕上。UI数据的绘制需要有画板,就是BufferQueue的支持,不管是那类窗口都要向surfaceflinger申请相应的layer,进一步得到缓冲区的使用权。这一切的启动是在viewroot执行viewtree遍历时,会想WMS申请一个surface

 

具体是从relayoutWindow开始重新布局窗口,应用进程通过mWindowSession.relayoutWMSsurfaceflinger申请画板,然后通过最后一个参数mSurface将结果返回

  1. final Surface mSurface = new Surface();
  2. private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
  3. boolean insetsPending) throws RemoteException@ViewRootImpl.java {
  4. int relayoutResult = mWindowSession.relayout(
  5. mWindow, mSeq, params,
  6. (int) (mView.getMeasuredWidth() * appScale + 0.5f),
  7. (int) (mView.getMeasuredHeight() * appScale + 0.5f),
  8. viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
  9. mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
  10. mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
  11. mSurface);
  12. }

接着看surface是怎么申请的,先看IwindowSession中的处理

  1. 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.graphics.Rect outStableInsets, android.graphics.Rect outOutsets, android.graphics.Rect outBackdropFrame, android.content.res.Configuration outConfig, android.view.Surface outSurface) throws android.os.RemoteException
  2. @IwindowSession.java$IWindowSession$Stub$Proxy{
  3. //通过Binder向服务端发送请求TRANSACTION_relayout,
  4. mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);
  5. //收到恢复后,从reply中取出结果,这是客户端的实现,具体取出的是什么,就要看服务端写入的什么了。
  6. outSurface.readFromParcel(_reply);
  7. }

//看服务端的处理

  1. public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
  2. @IwindowSession.java$IWindowSession$Stub{
  3. switch (code){
  4. //跟客户端相同的业务码TRANSACTION_relayout
  5. case TRANSACTION_relayout:
  6. {
  7. //服务器端,首先创建了一个surface对象,然后调用WMS的relayoutWindow函数为这个对象赋值,最后通过_arg15.writeToParcel把结果写入回复的parcel中。如果一个类想在aidl机制中,客户度与服务端之间传递,就要继承Parcelable接口,并实现writeToParcel方法。
  8. android.view.Surface _arg15;
  9. _arg15 = new android.view.Surface();
  10. //这里会调用服务端Session.java中的实现,进一步调用WMS中的relayoutWindow。
  11. int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10, _arg11,
  12. _arg12, _arg13, _arg14, _arg15);
  13. _arg15.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
  14. }
  15. }
  16. }
  1. public int relayoutWindow(Session session, IWindow client, int seq,
  2. WindowManager.LayoutParams attrs, int requestedWidth,
  3. int requestedHeight, int viewVisibility, int flags,
  4. Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
  5. Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
  6. Configuration outConfig, Surface outSurface) @WindowManagerService.java{
  7. //每个windowState都有一个mWinAnimator来处理窗口的退出和进入动画。
  8. WindowStateAnimator winAnimator = win.mWinAnimator;
  9. //设置用户请求的宽、高,这值也会影响到surface的属性。
  10. win.setRequestedSize(requestedWidth, requestedHeight);
  11. //生成surface对象,通过outSurface.copyFrom返回客户端。
  12. result = createSurfaceControl(outSurface, result, win, winAnimator);
  13.  
  14. updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
  15. false /*updateInputWindows*/)
  16.  
  17. mWindowPlacerLocked.performSurfacePlacement();
  18. }

WMS并没有直接生成surface的操作,继续跟踪createSurfaceControl

  1. private int createSurfaceControl(Surface outSurface, int result, WindowState win,
  2. WindowStateAnimator winAnimator) @WindowManagerService.java{
  3. // createSurfaceLocked将会生成一个真正的surface,并有SurfaceControl来管理。
  4. WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked();
  5. if (surfaceController != null) {
  6. surfaceController.getSurface(outSurface);
  7. }
  8. }

//管理SurfacemSurfaceControl变量是在WindowSurfaceController的构造函数中生成的。

  1. WindowSurfaceController createSurfaceLocked() @WindowStateAnimator.java{
  2. mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
  3. attrs.getTitle().toString(),
  4. width, height, format, flags, this);
  5. }

接着看SurfaceControl的构造函数。

  1. long mNativeObject;
  2. public SurfaceControl(SurfaceSession session,
  3. String name, int w, int h, int format, int flags){
  4. //调用的nativeCreate函数,将会建立跟SurfaceFlinger的联系。
  5. mNativeObject = nativeCreate(session, name, w, h, format, flags);
  6. }
  1. static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
  2. jstring nameStr, jint w, jint h, jint format, jint flags) @android_view_SurfaceControl.cpp{
  3. // SurfaceComposerClient是surfaceflinger的代表,opengl es和surface都是在这个类的协助下申请和访问buffer缓冲区。这里是通过 android_view_SurfaceSession_getClient来获取client对象,那么是什么地方创建的呢?
  4. sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
  5. sp<SurfaceControl> surface = client->createSurface( String8(name.c_str()), w, h, format, flags);
  6. surface->incStrong((void *)nativeCreate);
  7. return reinterpret_cast<jlong>(surface.get());
  8. }

继续看哪里创建了clientSurfaceComposerClient)对象。从这个函数的实现,可以知道,这是从jni层获取java层的属性,所以创建相关类实例的地方应该在native层创建了对象,然后通过jni保存在了java层。

  1. sp<SurfaceComposerClient> android_view_SurfaceSession_getClient(
  2. JNIEnv* env, jobject surfaceSessionObj) @android_view_SurfaceSession.cpp{
  3. return reinterpret_cast<SurfaceComposerClient*>(
  4. env->GetLongField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
  5. }

具体过程,还要从addWindow看起。

  1. public int addWindow(…...)@WindowManagerService.java{
  2. //创建WindowState时,将session变量保存在WindowState 的变量mSession中。
  3. WindowState win = new WindowState(this, session, client, token…);
  4. //attach()的实现中,调用mSession的windowAddedLocked()方法。进一步生成了SurfaceSession实例mSurfaceSession(属于Session.java的变量)。
  5. win.attach();
  6. }

//SurfaceSession的构造函数中,通过native方法创建一个到SurfaceFlinger的连接,就是SurfaceComposerClient实例,这是nativeSurfaceComposerClient.cpp对象,这个对象保存在了SurfaceSession.java中的变量mNativeClient中。

  1. private long mNativeClient; // SurfaceComposerClient*
  2. public SurfaceSession() @SurfaceSession.java{
  3. mNativeClient = nativeCreate();
  4. }

在创建WindowState对象时,其构造函数中,同时创建了WindowStateAnimator对象,而且WindowStateAnimator.java的构造函数中也保存了Session对象到其变量mSession中。

  1. WindowStateAnimator(final WindowState win) @WindowStateAnimator.java{
  2. mSession = win.mSession;
  3. }

在调用WindowStateAnimatorcreateSurfaceLocked函数,创建WindowSurfaceController对象时,把mSession.mSurfaceSession传给了WindowSurfaceController的构造函数,在WindowSurfaceController的构造函数中,这个SurfaceSession对象传给了SurfaceControl

现在我们直到SurfaceControl.java调用native函数nativeCreate时,获取的SurfaceComposerClient实例client是怎么来的了。

 

前面说了Surface是有SurfaceControl来管理的,SurfaceControl.java构造函数的nativeCreate就是建立跟surfaceflinger的连接,最终的surface的创建还要有SurfaceFlinger来完成。

  1. sp<SurfaceControl> SurfaceComposerClient::createSurface(
  2. const String8& name,
  3. uint32_t w,
  4. uint32_t h,
  5. PixelFormat format,
  6. uint32_t flags)@SurfaceComposerClient.cpp{
  7. sp<IGraphicBufferProducer> gbp;
  8. //调用Client.cpp的实现,创建IGraphicBufferProducer实例。
  9. status_t err = mClient->createSurface(name, w, h, format, flags,
  10. &handle, &gbp);
  11. //生成native层的 SurfaceControl对象,同时传入IGraphicBufferProducer对象。
  12. sur = new SurfaceControl(this, handle, gbp);
  13. }



回头看前面createSurfaceControl函数中获取surface的方式是surfaceControllerWindowSurfaceController.java)的getSurface(outSurface);这个java层方法最终会调用SurfaceControl.cpp中的getSurface方法。

  1. sp<Surface> SurfaceControl::getSurface() const @SurfaceControl.cpp{
  2. mSurfaceData = new Surface(mGraphicBufferProducer, false);
  3. return mSurfaceData;
  4. }

 

可以看到这里创建了真正的Surface对象,当然这里的对象还是在server端,然后会通过writeToParcel写入reply。这样ViewRootImpl.java这边,也即是客户端就可以利用readFromParcel把它还原出来,实现了对ViewRootImpl.javamSurface变量的填充。

  1. 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.graphics.Rect outStableInsets, android.graphics.Rect outOutsets, android.graphics.Rect outBackdropFrame, android.content.res.Configuration outConfig, android.view.Surface outSurface) throws android.os.RemoteException@IWindowSession.java{
  2. //这里就是读取server端的返回值。Surface.java中的 readFromParcel调用了native层的实现nativeReadFromParcel。
  3. outSurface.readFromParcel(_reply);
  4. }

//可以看到客户端新创建了native层的Surface对象,所以和服务端的surface(native)对象并不是同一个,但是跟surface关联的IGraphicBufferProducer是同一个,实际上Surface的主要职责就是管理用于存储UI数据的内存块。

  1. static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz,
  2. jlong nativeObject, jobject parcelObj) @android_view_Surface.cpp{
  3. Parcel* parcel = parcelForJavaObject(env, parcelObj);
  4. android::view::Surface surfaceShim;
  5. surfaceShim.readFromParcel(parcel, /*nameAlreadyRead*/true);
  6. sp<Surface> sur;
  7. sur = new Surface(surfaceShim.graphicBufferProducer, true);
  8. sur->incStrong(&sRefBaseOwner);
  9. return jlong(sur.get());
  10. }



2),relayoutWindow中还有一个比较重要的调用performSurfacePlacement,功能包括两部分,一是performLayout,计算窗口大小,二是performSurface,对Surface属性的设置,如:setSizesetLayersetPostion,然后这些变更会告知Surfaceflinger

  1. public int relayoutWindow(Session session, IWindow client, int seq,
  2. WindowManager.LayoutParams attrs, int requestedWidth,
  3. int requestedHeight, int viewVisibility, int flags,
  4. Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
  5. Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
  6. Configuration outConfig, Surface outSurface)@WindowManagerService.java {
  7. mWindowPlacerLocked.performSurfacePlacement();
  8. }

 

performSurfacePlacement间接调用了performSurfacePlacementInner

//有些属性发生了变化,要在这里对变更做计算。

  1. private void performSurfacePlacementInner(boolean recoveringMemory) @WindowSurfacePlacer.java{
  2. //在处理surface属性前,先调用 openTransaction,然后对surface属性做处理,但是处理不是立即生效,而是要等到 closeTransaction业务关闭后,统一告知surfaceflinger才会生效。
  3. SurfaceControl.openTransaction();
  4. try {
  5. applySurfaceChangesTransaction(recoveringMemory, numDisplays, defaultDw, defaultDh);
  6. }finally {
  7. SurfaceControl.closeTransaction();
  8. }
  9. //获取默认显示屏的窗口列表。
  10. final WindowList defaultWindows = defaultDisplay.getWindowList();
  11.  
  12. //处理应用切换mService.mAppTransition相关的操作。
  13. defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);
  14. defaultDisplay.pendingLayoutChanges |=mService.handleAnimatingStoppedAndTransitionLocked();
  15. //如果窗口不可见了,销毁他的surface。
  16. boolean wallpaperDestroyed = false;
  17. i = mService.mDestroySurface.size();
  18. if (i > 0) {
  19. //这里有while循环,处理mDestroySurface列表中的每个 WindowState。
  20. WindowState win = mService.mDestroySurface.get(i);
  21. win.destroyOrSaveSurface();
  22. mService.mDestroySurface.clear();
  23. }
  24.  
  25. //移除不在具有可见窗口的token。
  26. for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
  27. final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
  28. ArrayList<WindowToken> exitingTokens = displayContent.mExitingTokens;
  29. for (i = exitingTokens.size() - 1; i >= 0; i--) {
  30. WindowToken token = exitingTokens.get(i);
  31. exitingTokens.remove(i);
  32. mWallpaperControllerLocked.removeWallpaperToken(token);
  33. }
  34. }
  35.  
  36. //从task中移除退出的应用程序。
  37. for (int stackNdx = mService.mStackIdToStack.size() - 1; stackNdx >= 0; --stackNdx) {
  38. final AppTokenList exitingAppTokens =
  39. mService.mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
  40. AppWindowToken token = exitingAppTokens.get(i);
  41. token.mAppAnimator.clearAnimation();
  42. token.removeAppFromTaskLocked();
  43. }
  44.  
  45. //更新窗口信息到Inputdispatch,因为现在窗口更改已经稳定了。
  46. mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
  47.  
  48. //安排动画的执行。
  49. mService.scheduleAnimationLocked();
  50. }

 

    1. private void applySurfaceChangesTransaction(boolean recoveringMemory, int numDisplays,
    2. int defaultDw, int defaultDh) @WindowSurfacePlacer.java{
    3. //循环处理每个显示设备,我们只关注主显示屏,
    4. final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
    5. WindowList windows = displayContent.getWindowList();
    6. DisplayInfo displayInfo = displayContent.getDisplayInfo();
    7. int repeats = 0;
    8. do {
    9. //while循环,6次后跳出,为什么是6次不知道?
    10. repeats++;
    11. if (repeats > 6) {
    12. displayContent.layoutNeeded = false;
    13. break;
    14. }
    15. //需要的化,计算窗口大小,这个 LAYOUT_REPEAT_THRESHOLD是4,定义在WindowManagerService.java中。
    16. if (repeats < LAYOUT_REPEAT_THRESHOLD) {
    17. performLayoutLockedInner(displayContent, repeats == 1,false /* updateInputWindows */);
    18. }
    19.  
    20. if (isDefaultDisplay) {
    21. //对每一个窗口,调用窗口策略policy(PhoneWindowManager.java)中的方法,应用policy。
    22. mService.mPolicy.beginPostLayoutPolicyLw(dw, dh);
    23. for (int i = windows.size() - 1; i >= 0; i--) {
    24. WindowState w = windows.get(i);
    25. if (w.mHasSurface) {
    26. mService.mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs,
    27. w.mAttachedWindow);
    28. }
    29. }
    30. displayContent.pendingLayoutChanges |=mService.mPolicy.finishPostLayoutPolicyLw();
    31. }
    32. }while (displayContent.pendingLayoutChanges != 0);
    33.  
    34. //针对每个窗口,根据需要应用特效,动画相关等。
    35. for (int i = windows.size() - 1; i >= 0; i--) {
    36. WindowState w = windows.get(i);
    37. w.applyDimLayerIfNeeded();
    38. }
    39. }

免责声明:文章转载自《Android 7.1 GUI系统-窗口管理WMS-Surface管理(四)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇docker-java Docker的java APIGo 字符串拼接6种,最快的方式 strings.builder下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ALV常用参数的详细描述

调用功能模块: CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' EXPORTING i_interface_check = '' 接口一致性检查 i_callback_program = sy-r...

Win10系统的SurfacePro4如何重装系统-2 重装WIN10系统

把SurfacePro充好电,然后关机,开机按住音量+,然后再按电源键,可以开机并进入BIOS(此前应确保优盘已经装了PE并插入Surface)   然后选择U盘启动为第一个(按住之后把他拖放到第一位置即可),如果你能看到多个Windows Boot Manager,也可以点击后面的删除,这些都是残留的启动选项,反正也启动不了,最后弄完之后又会把可用...

多线程之旅七——GUI线程模型,消息的投递(post)与处理

基于消息的GUI构架 在过去的日子中,大部分编程语言平台的GUI构架几乎没有发生变化。虽然在细节上存在一些差异,比如在功能和编程风格上,但大部分都是采用了相同的构架来响应用户输入以及重新绘制屏幕。这种构架可以被总结为“单线程且基于消息”。   Message msg; While(GetMessage(msg)) { TranslateMess...

Java全家桶的这些知识,不用学了

众所周知,Java 的知识体系繁冗复杂,但是有很多知识在实际工作中几乎没有人用。 很多人在学习过程中,却经常把有限的时间和精力花在了这些“没有用”的知识上,事倍功半。 下面我捋一捋 Java 中那些不建议学习的知识点,让大家能避过雷区,尽量提升些学习的精准度。 Java 的桌面 GUI 相关技术 GUI,即 Graphical User Interface...

命令行程序增加 GUI 外壳

Conmajia © 2012Updated on Feb. 21, 2018 命令行大家都用过: 图 1 命令行程序工作界面 现在想办法为它做一个 GUI 外壳,实际效果参考图 2. 图 2 带 GUI 外壳的命令行程序 程序思路是这样的: 通过运行 cmd.exe 来操作命令行,现在要给它一个 GUI. Windows 的命令行 cmd.exe(...

Simulink仿真入门到精通(八) M语言对Simulink模型的自动化操作及配置

8.1 M语言控制模型的仿真 M语言与Simulink结合的方式: 在Simulink模型或模块中使用回调函数 在M语言中调用与模型相关的命令,控制模型的建立,设置模块的属性,增删信号线,以及运行模型仿真等 为了调用和操作Simulink模型,M语言中最常用的函数有sim、set_param、get_param。 8.1.1 sim控制模型仿真及参数配...