Android进阶(四)Window和WindowManager

一、Window、WindowManager、WMS

1、三者的定义和关系

  • Window可以理解为窗体,是一种抽象概念,其中具体表现形式就是View。
  • WindowManager是用来添加、删除、更新Window(具体是View)的。
  • WindowManagerService是在SystemServer进程中的,WindowManger中的所有操作都是由WMS来最终完成的。

三者关系:

2、Window

一个抽象类,其具体实现为PhoneWindow

3、WindowManager

WindowManager继承了ViewManager,ViewManager中定义了添、删、改的方法。

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
复制代码

4、Activity中PhoneWindow的创建

在Activity启动过程中,执行到了Activity的attach方法中

final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback) {
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);
    //实现了PhoneWindow的创建
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    //设置回调
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    ......
    //(1)设置WindowManager
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;

    mWindow.setColorMode(info.colorMode);
}
复制代码

(1)context.getSystemService(Context.WINDOW_SERVICE)获取的是什么?
Context具体实现在ContextImpl中

#ContextImpl
@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

#SystemServiceRegistry
public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}
复制代码

SYSTEM_SERVICE_FETCHERS是一个map,key为WINDOW_SERVICE时,对应的value是什么?

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

注册服务时,传入Context.WINDOW_SERVICE时,最终返回的是WindowManagerImpl。

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    ......
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
复制代码

通过createLocalWindowManager,最终创建了WindowManagerImpl对象。

二、Activity中Window添加

1、ActivityThread的handleResumeActivity

当界面要与用户进行交互时,需要调用handleResumeActivity方法

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ......
    (1) //最终调用Activity的onResume
    r = performResumeActivity(token, clearHide, reason);
        ......
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    (2)
                    wm.addView(decor, l);
            }
            ......
      
    }
}
复制代码

(2)最终调用了WindowManagerImpl的addView方法

2、WindowManager的addView

#WindowManagerImpl
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
复制代码

内部调用WindowManagerGlobal的addView方法

3、WindowManagerGlobal

(1)部分参数:

//Window所对应的view
private final ArrayList<View> mViews = new ArrayList<View>();
//Window所对应的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//Window所对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
//已经调用了removeView,但是还没完成的Window对象
private final ArraySet<View> mDyingViews = new ArraySet<View>();
复制代码

(2)addView方法

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
        ......
        //创建ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        
        view.setLayoutParams(wparams);
        //将参数添加到list中
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        try {
            //调用了ViewRootImpl的setView方法
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}
复制代码

调用了ViewRootImpl的setView方法

#ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            ......
            (1)
            requestLayout();
            ......
            try {
                mOrigWindowType = mWindowAttributes.type;
                mAttachInfo.mRecomputeGlobalAttributes = true;
                collectViewAttributes();
                (2)
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } 
            ......
        }
    }
}
复制代码

(1)通过调用requestLayout,最终通过performTraversals方法完成View的测量、布局、绘制操作。
(2)调用了IWindowSession的addToDisplay方法,IWindowSession实现是什么?
IWindowSession是Binder对象,最终调用的是SystemServer进程的Session的addToDisplay方法

#Session
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
        Rect outOutsets, InputChannel outInputChannel) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
            outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
复制代码

调用了WMS的addWindow方法,其中使用list将Session进行保存,并为窗口分配画布Surface,用来显示页面,并确定窗口的显示次序。

时序图:

三、WindowManager删除View

1、WindowManagerGlobal的removeView

WindowManager的removeView方法,最终同样调用了WindowManagerGlobal的removeView方法

#WindowManagerGlobal
public void removeView(View view, boolean immediate) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }

    synchronized (mLock) {
        //找到view对应的索引位置
        int index = findViewLocked(view, true);
        View curView = mRoots.get(index).getView();
        //下一步操作
        removeViewLocked(index, immediate);
        if (curView == view) {
            return;
        }

        throw new IllegalStateException("Calling with view " + view
                + " but the ViewAncestor is attached to " + curView);
    }
}

private void removeViewLocked(int index, boolean immediate) {
    //获取到对应的ViewRootImpl
    ViewRootImpl root = mRoots.get(index);
    View view = root.getView();

    if (view != null) {
        InputMethodManager imm = InputMethodManager.getInstance();
        if (imm != null) {
            imm.windowDismissed(mViews.get(index).getWindowToken());
        }
    }
    //进行下一步操作
    boolean deferred = root.die(immediate);
    if (view != null) {
        view.assignParent(null);
        if (deferred) {
            mDyingViews.add(view);
        }
    }
}
复制代码

2、die

boolean die(boolean immediate) {
    if (immediate && !mIsInTraversal) {
        //(1)如果immediate为true,立即删除
        doDie();
        return false;
    }

    if (!mIsDrawing) {
        destroyHardwareRenderer();
    } else {
        Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
                "  window=" + this + ", title=" + mWindowAttributes.getTitle());
    }
    (2)
    mHandler.sendEmptyMessage(MSG_DIE);
    return true;
}
复制代码

(1)同步删除直接调用doDie方法
(2)异步删除通过Handler发送了MSG_DIE的消息,没有真正删除。只是将view添加到一个待删除的列表mDyingViews中


void doDie() {
    ......
    if (mAdded) {
        //(1)下一步删除操作
        dispatchDetachedFromWindow();//5
    }
    ......
    //(2)
    WindowManagerGlobal.getInstance().doRemoveView(this);
}
复制代码

(1)调用了Session的remove方法,最终交给WMS进行处理。

void dispatchDetachedFromWindow() {
  ...
    try {
        mWindowSession.remove(mWindow);
    } catch (RemoteException e) {
    }
    ...
}
复制代码

(2)将该view对应索引在list中删除

void doRemoveView(ViewRootImpl root) {
    synchronized (mLock) {
        final int index = mRoots.indexOf(root);
        if (index >= 0) {
            //将该view对应索引在list中删除
            mRoots.remove(index);
            mParams.remove(index);
            final View view = mViews.remove(index);
            mDyingViews.remove(view);
        }
    }
    if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
        doTrimForeground();
    }
}
复制代码

四、WindowManager更新View

1、WindowManagerGlobal的updateViewLayout

#WindowManagerGlobal
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    ......
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
    //更新view的LayoutParams
    view.setLayoutParams(wparams);

    synchronized (mLock) {
        //找到view索引
        int index = findViewLocked(view, true);
        ViewRootImpl root = mRoots.get(index);
        //移除之前的LayoutParams
        mParams.remove(index);
        //添加新的LayoutParams
        mParams.add(index, wparams);
        //更新ViewRootImpl,调用scheduleTraversals方法
        root.setLayoutParams(wparams, false);
    }
}
复制代码

2、ViewRootImpl的setLayoutParams

#ViewRootImpl

void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
    synchronized (this) {
        ......
        scheduleTraversals();
    }
}


void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //注册了TraversalRunnable
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}


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

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

private void performTraversals() {
    ......
    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
    ......
}

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {
    ......
    int relayoutResult = mWindowSession.relayout(
            mWindow, mSeq, params,
            (int) (mView.getMeasuredWidth() * appScale + 0.5f),
            (int) (mView.getMeasuredHeight() * appScale + 0.5f),
            viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
            mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
            mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
            mPendingMergedConfiguration, mSurface);
    ......
    return relayoutResult;
}
复制代码

最终通过Session的relayout方法实现view的更新

参考资料:

  • 《Android开发艺术探索》
  • 《Android进阶解密》

猜你喜欢

转载自juejin.im/post/5c4ef140518825261e1f2c4c