【Android源码】WindowManagerService 浅析

所有需要显示在屏幕上的内容,都是通过WindowManager来操作的,这就是我们通常所说的WMS(WindowManagerService),本篇我们一起来分析一下WindowManagerService、Surface、SurfaceFlinger、WindowManager之间是如何建立关系并交互的。我们通过Dialog来分析它们之间如何建立联系并交互。

首先当我们想要获取WindowManager的时候,发现它是一个抽象类,所以我们需要找到它的实现。
我们可以通过Context的getSystemService方法获取到对应的服务实例,而WindowManager就是在ContextImpl中注册的服务之一。

registerService(Context.WINDOW_SERVICE, WindowManager.class,
      new CachedServiceFetcher<WindowManager>() {
  @Override
  public WindowManager createService(ContextImpl ctx) {
      return new WindowManagerImpl(ctx);
  }});

原来WIndowManager的实例是WindowManagerImpl。
那么Dialog是如何获取到WindowManager的?

首先我们先分析下Dialog的构造函数:

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
   mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

   final Window w = new PhoneWindow(mContext);
   mWindow = w;
   w.setCallback(this);
   w.setOnWindowDismissedCallback(this);
   w.setWindowManager(mWindowManager, null, null);
   w.setGravity(Gravity.CENTER);

   mListenersHandler = new ListenersHandler(this);
}

可以发现,在构造函数中获取到WindowManager对象,并通过Window的setWindowManager方法将Window和WindowManager联系起来。

public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
     mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
   return new WindowManagerImpl(mContext, parentWindow);
}

最终通过new WindowManagerImpl(mContext, parentWindow)将WindowManager和Window联系起来,此时可以发现这个构建的WindowManager方法中多出一个parentWindow,表示和具体的WIndow相关联。

现在我们跳转到WindowManagerImpl中:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;
    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

    public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
        return new WindowManagerImpl(displayContext, mParentWindow);
    }

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }
}

可以发现WindowManagerImpl并没有做什么操作,添加View、移除View、更新View的操作最终都交给了WindowManagerGlobal类,并调用addView方法。

public void addView(View view, ViewGroup.LayoutParams params,
       Display display, Window parentWindow) {
   ViewRootImpl root;
   View panelParentView = null;

   synchronized (mLock) {
       // 构建ViewRootImpl
       root = new ViewRootImpl(view.getContext(), display);
         // 设置布局
       view.setLayoutParams(wparams);
         // 将View添加到View列表中
       mViews.add(view);
       // 将root添加到root列表中
       mRoots.add(root);
       mParams.add(wparams);
   }

   // do this last because it fires off messages to start doing things
   try {
        // 调用ViewRootImpl的addView方法将View显示到界面上
       root.setView(view, wparams, panelParentView);
   } catch (RuntimeException e) {
     throw e;
   }
}

通过分析WindowManagerGlobal,可以发现主要操作是:

  1. 构建ViewRootImpl
  2. 将布局设置给View
  3. 将View、ViewRootImpl存储起来
  4. 通过setView将view显示到界面上

到此时,我们分析到了framework层,而WMS是在native层,那么framework层是如何与native层建立关系和交互的呢?

这个时候我们就需要看ViewRootImpl类了,这个类继承自Handler,是native与java层View系统通讯的桥梁,比如performTraversals函数就是收到绘制View的消息之后,通过调用measure、layout、draw来绘制整个视图。

首先我们先看构造函数:

public ViewRootImpl(Context context, Display display) {
   mContext = context;
   // 获取Window Session,与WindowManagerService之间建立联系
   mWindowSession = WindowManagerGlobal.getWindowSession();
   // 保存当前线程,更新UI只能在创建了ViewRootImpl的线程
   // 所以通常情况下,我们只能在UI线程更新UI,但并不是不能在子线程中更新UI
   mThread = Thread.currentThread();
}

我们继续点进WindowManagerGlobal.getWindowSession()方法:

public static IWindowSession getWindowSession() {
   synchronized (WindowManagerGlobal.class) {
       if (sWindowSession == null) {
           try {
               InputMethodManager imm = InputMethodManager.getInstance();
               // 获取WindowManagerService
               IWindowManager windowManager = getWindowManagerService();
               // 与WindowManagerService建立联系
               sWindowSession = windowManager.openSession(
                       new IWindowSessionCallback.Stub() {
                           @Override
                           public void onAnimatorScaleChanged(float scale) {
                               ValueAnimator.setDurationScale(scale);
                           }
                       },
                       imm.getClient(), imm.getInputContext());
           } catch (RemoteException e) {
               throw e.rethrowFromSystemServer();
           }
       }
       return sWindowSession;
   }
}

public static IWindowManager getWindowManagerService() {
   synchronized (WindowManagerGlobal.class) {
       if (sWindowManagerService == null) {
           sWindowManagerService = IWindowManager.Stub.asInterface(
                   ServiceManager.getService("window"));
       }
       return sWindowManagerService;
   }
}

最终通过ServiceManager.getService(“window”)获取WMS,并将WMS转换为IWindowManager。

public static IBinder getService(String name) {
   try {
       IBinder service = sCache.get(name);
       if (service != null) {
           return service;
       } else {
           return getIServiceManager().getService(name);
       }
   } catch (RemoteException e) {
       Log.e(TAG, "error in getService", e);
   }
   return null;
}

通过getService方法可以发现,返回的是一个IBinder对象,这个也就能从侧面说明Framework和WMS之间是通过Binder机制进行通信的,获取到WMS之后再通过IWindowManager.Stub.asInterface方法将WMS的IBinder转换成WindowManager对象。最终通过openSession与WMS建立通信对话,之后就可以通过这个session,java层和native层之间就可以通信了。

这个时候View并不会显示在界面上,WMS只负责管理View的z-order,也就是管理哪个view应该显示在最上层。这个时候WindowManagerGlobal之后就调用了root.setView(view, wparams, panelParentView)方法,这个方法就是发起显示请求:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
   synchronized (this) {
       if (mView == null) {
            // 请求布局
           requestLayout();
           // 向WMS发起请求
           res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                       getHostVisibility(), mDisplay.getDisplayId(),
                       mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                       mAttachInfo.mOutsets, mInputChannel);
       }
   }
}

@Override
public void requestLayout() {
   if (!mHandlingLayoutInLayoutRequest) {
       checkThread();
       mLayoutRequested = true;
       scheduleTraversals();
   }
}

void scheduleTraversals() {
   if (!mTraversalScheduled) {
       mChoreographer.postCallback(
               Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
   }
}

final class TraversalRunnable implements Runnable {
   @Override
   public void run() {
       doTraversal();
   }
}

void doTraversal() {
   if (mTraversalScheduled) {
       performTraversals();
   }
}

private void performTraversals() {
    // 获取Surface,用于图形绘制
    // performMeasure
    // performLayout
    // performDraw
}

调用performTraversals方法,这个方法就是我们绘制界面的方法,里面会调用我们所熟悉的onMeasure、onLayout、onDraw方法。

private void performDraw() {
   try {
      // 调用绘制函数
       draw(fullRedrawNeeded);
   } finally {
       mIsDrawing = false;
       Trace.traceEnd(Trace.TRACE_TAG_VIEW);
   }
}

private void draw(boolean fullRedrawNeeded) {
    // 获取surface
   Surface surface = mSurface;
   if (!surface.isValid()) {
       return;
   }
    // 绘制需要更新
   if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
        // 使用硬件加速
       if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                // 使用硬件渲染绘制
           mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
       } else {
            // 使用cpu绘制
           if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
               return;
           }
       }
   }

   if (animating) {
       mFullRedrawNeeded = true;
       scheduleTraversals();
   }
}

在draw函数中获取需要绘制的区域,再判断是否使用硬件加速。
通常情况下都是使用cpu绘制,也就是直接调用drawSoftware:

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
       boolean scalingRequired, Rect dirty) {

   // Draw with software renderer.
   final Canvas canvas;
   try {
       final int left = dirty.left;
       final int top = dirty.top;
       final int right = dirty.right;
       final int bottom = dirty.bottom;

            // 获取canvas对象,用于framework层绘制
       canvas = mSurface.lockCanvas(dirty);
   } catch (Surface.OutOfResourcesException e) {
        return false;
   }

   try {
        // 开始绘制,从decorView开始绘制
         mView.draw(canvas);
   } finally {
        // 释放canvas锁,并通知SurfaceFlinger更新
        // private static native void nHwuiDraw(long renderer); 调用native方法
           surface.unlockCanvasAndPost(canvas);
   }
   return true;
}

从上面的函数可以看出绘制的过程:

  1. 判断使用gpu还是cpu渲染
  2. 获取绘制的surface对象
  3. 通过surface获取并锁住canvas对象
  4. 从DecorView开始发起整个view树的绘制
  5. 解锁canvas对象,并通知surfaceFlinger对象更新视图,调用native方法。

到此为止,Activity、Dialog等组件的View就显示到用户的界面上了。

    原文作者:指间沙似流年
    原文地址: https://www.jianshu.com/p/6b31e650b347
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞