【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就显示到用户的界面上了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容