android 深入理解WindowManager

27 篇文章 1 订阅
23 篇文章 3 订阅

所有需要显示到屏幕上的内容(包括Activity等)都是通过WindowManager来操作的,看来WindowManager是一个非常重要的子系统,这就是我们常说的WMS(WindowManager Service)。我们只关心WindowManager和WindowManagerService(后续简称WMS)、Surface、SurfaceFlinger等建立关联以及交互的一个基本过程。

WindowManager是接口, 真正的实现是类WindowManagerImpl。

与WindowManager联系上的第一步就是通过Context的getSystemService方法,在上一章的单例模式中,各种系统服务会注册到SystemServiceRegistry 的一个map容器中,然后通过该服务的字串键来获取。

#ContextImpl
 // The system service cache for the system services that are cached per-ContextImpl.
  final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();




#SystemServiceRegistry 
 registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
               //构造WindowManagerImp对象
                return new WindowManagerImpl(ctx);
            }});

/**
 * Creates an array which is used to cache per-Context service instances.
 */
#SystemServiceRegistry 
public static Object[] createServiceCache() {
    return new Object[sServiceCacheSize];
}

 

上述代码中可以发现,我们看到了WindowManager在Java层的具体实现,也就是WIndowManagerImpl。那么Dialog是如何获取到WindowManager对象的呢?我们从上述代码中可以知道,Windowmanager是注册到SystemServiceRegistry的,而ContextImp中存在一个ServiceCache(是一个Object类型的数组)。而getSystemService也是Context定义的接口,因此,需要先从Dialog的构造函数进行分析,因为Context对象就是从Dialog的构造函数传入的。

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
  //设置包装Context相关的代码
    if (createContextThemeWrapper) {
        if (themeResId == ResourceId.ID_NULL) {
            final TypedValue outValue = new TypedValue();
            context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
            themeResId = outValue.resourceId;
        }
        mContext = new ContextThemeWrapper(context, themeResId);
    } else {
        mContext = context;
    }

   //1.获取WindowManager
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    //2.设置Window 的回调。
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setOnWindowSwipeDismissedCallback(() -> {
        if (mCancelable) {
            cancel();
        }
    });
   //3.设置Window的WindowManager
    w.setWindowManager(mWindowManager, null, null);
    w.setGravity(Gravity.CENTER);

    mListenersHandler = new ListenersHandler(this);
}

 

Window是抽象类

通过上述代码可以发现,最终还是通过Window对象的setWindowManager函数将Window对象与WindowManager建立了联系,该函数在Window类中,我们看看这个函数的实现。

/**
 * Set the window manager for use by this Window to, for example,
 * display panels.  This is <em>not</em> used for displaying the
 * Window itself -- that must be done by the client.
 *
 * @param wm The window manager for adding new windows.
 */
#window 
public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
    setWindowManager(wm, appToken, appName, false);
}

/**
 * Set the window manager for use by this Window to, for example,
 * display panels.  This is <em>not</em> used for displaying the
 * Window itself -- that must be done by the client.
 *
 * @param wm The window manager for adding new windows.
 */
#window 
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken;
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated
            || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
   //参数赋值等代码
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
//注意这里,调用了createLocalWindowManager函数
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

 

//这里的一句代码很重要,调用的是WindowManagerImpl中的createLocalWindowManager 方法

#WinodwManagerImpl

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {

//重新构建的WindowManagerImpl与具体的WIndow关联。

return new WindowManagerImpl(mContext, parentWindow);

}

 

这个方法很简单,只是单纯地构建了一个WindowManagerImpl对象。与ContextImpl(SystemServiceRegistry)注册的WindowManagerImpl不同的是,这里多了一个parentWindow参数,也就是说,此时构造的WindowMangerImpl对象与具体的Window相关联,而在ContextImpl(SystemServiceRegistry)中注册的并没有此参数(只有一个ContextImpl)。此时,java层上Window对象就已经和WindowManger建立了第一步联系。

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

    private IBinder mDefaultToken;

    public WindowManagerImpl(Context context) {
        this(context, null);
    }

    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }
   ... ...

@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);
    }
... ....
}

显然,WindowManagerImp也不是具体“干活”的对象,添加View、移除View、更新View的布局等具体的工作都交给了WindowManagerGlobal这个类,在这一切就绪之后,会调用WindowManager的addView方法请求系统将该View显示到屏幕,经过上述代码可知,实际上调用的是WindowManagerGlobal中的addView方法。

#WindowManagerGlobal

//将View添加的到WindowManager中,也就是在手机窗口中显示该View。
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }
//检查参数有效性
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

     //构建ViewRootImpl
     //是作为native层与java层View系统通信的桥梁
            root = new ViewRootImpl(view.getContext(), display);
    // 给View设置布局参数
            view.setLayoutParams(wparams);
    //将View添加到View列表中
            mViews.add(view);
           //将ViewRootImpl对象root添加到mRoots对象中
            mRoots.add(root);
            mParams.add(wparams);

            /// M: Add log for tracking mViews.
            Log.d("WindowClient", "Add to mViews: " + view + ", this = " + this);

            // do this last because it fires off messages to start doing things
            try {
                //调用ViewRootImpl的setView方法将View显示到手机窗口中
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

上面的程序主要分为以下4个步骤:

1.构建ViewRootImpl;

2.将布局参数设置给View;

3.存储这些ViewRootImpl、View、LayoutParams到列表中。

4.通过ViewRootImpl的setView将View显示到窗口上。

 

@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})

public final class ViewRootImpl implements ViewParent,

View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks

 

final class ViewRootHandler extends Handler

 

ViewRootImpl并不是一个View,(它继承自Handler类(现不是)),是作为native层与java层View系统通信的桥梁,比如我们熟知的private void performTraversals()函数就是收到系统绘制View的消息之后,通过调用视图的各个节点的measure、layout、draw方法来绘制整棵视图树。

既然ViewRootImpl是Framework层与Native层的通信桥梁,ViewRootImpl很有可能是与Native层建立联系的关键点,不过我们这里只讨论与WMS的关联。

从WindowManagerGlobal的addView函数中可以看到,第一个重要步骤就是构建了ViewRootImpl对象root,我们看看它的构造函数。

 

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {

... ...
 root = new ViewRootImpl(view.getContext(), display);
... ...
}

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
... ....
}

public ViewRootImpl(Context context, Display display) {
    mContext = context;
    //1.获取Window Session,也就是和WindowManagerService建立连接
    mWindowSession = WindowManagerGlobal.getWindowSession();
    mDisplay = display;
    mBasePackageName = context.getBasePackageName();
    //保存当前线程,更新UI的线程只能是创建ViewRoorImpl时的线程
    //我们在应用的开发中,如果在子线程中更新UI会抛出异常,但并不是因为只有UI线程才能更新UI
    //而是因为ViewRootImpl是在UI线程中创建的
    mThread = Thread.currentThread();
    ... ...
}

 

我们看到ViewRootImpl的代码1处,有一个WindowManagerGlobal.getWindowSession()方法,通过函数名以及WindowManagerGlobal的作用来看,这很有可能是与Native层建立通信的地方。

#WindowManagerGlobal
public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
//1.获取WindowManagerService
                    IWindowManager windowManager = getWindowManagerService();
                  2.与WindowManagerService建立一个Session
                    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;
        }
    }

#WindowManagerGlobal
  public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

 

在getWindowSession函数中,Framework层首先通过getWindowManagerService()函数获取到IWindowManager对象,该函数中通过ServiceManager.getService函数获取WMS,并且将WMS转换为IWindowManger类型,我们先看看ServiceManager.getService函数代码。

 

/**
 * Returns a reference to a service with the given name.
 * 
 * @param name the name of the service to get
 * @return a reference to the service, or <code>null</code> if the service doesn't exist
 */
public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            return Binder.allowBlocking(getIServiceManager().getService(name));
        }
    } catch (RemoteException e) {
        Log.e(TAG, "error in getService", e);
    }
    return null;
}

从程序中可以看到ServiceManager.getService返回的IBinder对象,也就是说Android Framework与WMS之间也是通过Binder机制进行通信,到了这一步我们已经与WMS建立了初步联系。

 

获取WMS之后,又调用了IWindowManager.Stub类的asInterface函数,将获取到的WMS的IBinder对象转换成WindowManager对象,最后通过openSession函数与WMS建立一个通信会话,相当于Framework层与Native层建立了一个长期的“办事处”,双方有什么需求都是通过这个Session来交换信息。

 

但是,此时Dialog或者Activity的View并不能显示在手机屏幕上,WMS只是负责管理手机屏幕上View的z-order,也就是说WMS管理当前状态下哪个View应该显示在最上层显示。仔细想一想,你发现其实WMS管理的并不是Window,而是View,只不过它管理的是属于某一个Window下的View。

 

与WMS建立Session之后就到了调用ViewRootImpl的setView方法了,该方法会向WMS发起显示Dialog或者Activity中DecorView的请求。

 

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        //1.请求布局
        requestLayout();
        try {
            mOrigWindowType = mWindowAttributes.type;
            mAttachInfo.mRecomputeGlobalAttributes = true;
            collectViewAttributes();
            //2.向WMS发起请求
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(),
                    mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                    mAttachInfo.mOutsets, mInputChannel);
        } catch (RemoteException e) {
            ... ...
        }
    }
}

setView很复杂,但是我们主要关注两步:

1.requestLayout

2.向WMS发起显示当前Window的请求

 

我们先看看requestLayout函数。

#ViewRootImpl
   @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            if (ViewDebugManager.DEBUG_REQUESTLAYOUT) {
                Log.d(mTag, "requestLayout: mView = " + mView + ", this = " + this,
                        new Throwable("requestLayout"));
            }
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();//发送 DO_TRAVERSAL
        }
    }

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            if (ViewDebugManager.DEBUG_SCHEDULETRAVERSALS) {
                Log.v(mTag, "scheduleTraversals: mTraversalBarrier = " + mTraversalBarrier
                        + ",this = " + this, new Throwable("scheduleTraversals"));
            }
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

就是往handler发送一个DO_TRAVERSAL消息,这个消息会触发整个视图树的绘制操作,也就是最终会执行performTraversals函数,这是一个极为复杂的但又非常重要的函数。

private void performTraversals() {

... ...

//1.获取Surface对象,用于图形绘制

//2.丈量整个视图树的各个View的大小,performMeasure函数

//3.布局整个视图树,performLayout函数

//4.绘制整棵视图树,performDraw函数

}

 

在第四步中,Framework会获取到图形绘制表面Surface对象,然后获取它的可绘制区域,也就是我们的Canvas对象,然后Framework再这个Canvas对象上绘制。

 private void performDraw() {
        if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
            return;
        } else if (mView == null) {
            return;
        }

        final boolean fullRedrawNeeded = mFullRedrawNeeded;
        mFullRedrawNeeded = false;

        mIsDrawing = true;
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
        try {
           //调用绘制函数
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        // For whatever reason we didn't create a HardwareRenderer, end any
        // hardware animations that are now dangling
        if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
            final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
            for (int i = 0; i < count; i++) {
                mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
            }
            mAttachInfo.mPendingAnimatingRenderNodes.clear();
        }

        if (mReportNextDraw) {
            mReportNextDraw = false;

            // if we're using multi-thread renderer, wait for the window frame draws
            if (mWindowDrawCountDown != null) {
                try {
                    mWindowDrawCountDown.await();
                } catch (InterruptedException e) {
                    Log.e(mTag, "Window redraw count down interruped!");
                }
                mWindowDrawCountDown = null;
            }

            if (mAttachInfo.mThreadedRenderer != null) {
                mAttachInfo.mThreadedRenderer.fence();
                mAttachInfo.mThreadedRenderer.setStopped(mStopped);
            }

            if (DEBUG_DRAW) {
                Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle()
                        + ", this = " + this);
            }

            if (mSurfaceHolder != null && mSurface.isValid()) {
                SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
                SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();

                sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
            } else {
                pendingDrawFinished();
            }
        }
    }

#ViewRootImpl
 private void draw(boolean fullRedrawNeeded) {
//1.获取绘制表面
Surface surface = mSurface;
        if (!surface.isValid()) {
            return;
        }
... ...
//代码省略
//2.绘图表面需要更新
 if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
       //使用GPU绘制,也就是硬件加速
if (mAttachInfo.mThreadedRenderer != null && 	mAttachInfo.mThreadedRenderer.isEnabled()) {
... ...
//代码省略
//使用硬件渲染器绘制
 mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
... ...
//代码省略
//使用CPU绘制图形
 if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
               return;
      }
}
}

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

 

 

在draw函数中会获取到需要绘制的区域,以及判断是否使用GPU进行绘制。通常情况下使用的是CPU绘制,也就是调用的是drawSoftware函数来绘制。

 /**
     * @return true if drawing was successful, false if an error occurred
     */
    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;

    //1.获取指定区域的Canvas对象,用于framework层绘图
    canvas = mSurface.lockCanvas(dirty);
    // ... ...
    // The dirty rectangle can be modified by Surface.lockCanvas()
    //noinspection ConstantConditions
    if (left != dirty.left || top != dirty.top || right != dirty.right
            || bottom != dirty.bottom) {
        attachInfo.mIgnoreDirtyState = true;
    }

    // TODO: Do this in native
    canvas.setDensity(mDensity);
}catch(){
}

try {
    ... ...
    }
    try {
        ... ...
        //从DecorView开始绘制,也就是整个Window的根视图,这会引起整棵树的重绘。
        mView.draw(canvas);
        ... ...
    } finally {
        if (!attachInfo.mSetIgnoreDirtyState) {
            // Only clear the flag if it was not set during the mView.draw() call
            attachInfo.mIgnoreDirtyState = false;
        }
    }
    
} finally {
    try {
        //释放Canvas锁,然后通知SurfaceFlinger更新这块区域
        surface.unlockCanvasAndPost(canvas);
    } catch (IllegalArgumentException e) {
       
    }
}
mPerfFrameInfoManager.setSoftwareDraw();
return true;
}

 

上述的视图树绘制代码主要分为以下几个步骤:

1.判断使用CPU绘制还是使用GPU绘制

2.获取绘制表面Surface对象

3.通过Surface对象获取并锁住Canvas绘图对象

5.Surface对象解锁Canvas,并且通知SurfaceFlinger更新视图

 

内容绘制完毕请求WMS显示该窗口上的内容。

 

 

 

 参考《Android源码设计模式》

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值