【源码学习】window 添加 view

此类文章主要是用来记录学习源码的过程,更多的参考别人的分析过程自己去追踪源码,然后做下的记录。看 Android 源码是一个痛苦的过程,之前几次尝试都以失败而告终,这里把这个过程记录下来,算是对自己的一种激励。

下面的所有源码都是基于Android_7.1.1而来。

View 的添加过程

可以知道的是 Android 中的所有视图都是通过 Window 来呈现的,Window 是 View 的管理者。

如果做过悬浮窗的话一定对下面这个代码不陌生

windowManager.addView(layout,params);

可以看到向 Window 添加 View 是通过 WindowManager 来实现的。那么就从 WindowManager 作为入口来进行研究。

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 vaiew);
}

ViewManager只有三个方法,添加、更新、删除 View。

WindowManager 也是一个接口,它的实现类是 WindowManagerImpl(这里可以通过activity.getWindowManager()追踪到)。查看 WindowManagerImpl:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

    @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 也没有真正的实现视图的添加、更新、删除,而是交给了 WindowManagerGlobal 来处理,继续追踪。找到 WindowManagerGlobal.addView()

WindowManagerGlobal.addView()

下面是 WindowManagerGlobal.addView() 的部分代码:

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");
        }

        // 检查父级 View 是否存在
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            // adjustLayoutParamsForSubWindow 从字面意思是调整窗口布局参数
            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);
            }

            // 检测是否已经存在这个 view
            int index = findViewLocked(view, false);
            if (index >= 0) {
                // 如果已经存在但是是快要移除的 view 就 移除它
                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
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            // 这里的 mViews 是存储所有 Window 所对应的 view
            mViews.add(view);
            // mRoots 是存储所有 window 对应的 ViewRootImpl
            mRoots.add(root);
            // mParams 存储所有 window 对应的布局参数
            mParams.add(wparams);
        }

        // 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.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

ViewRootImpl.setView()

经过上面的分析可以知道,最终是通过 ViewRootImpl.setView() 方法来添加

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...
    // 在添加之前先进行了刷新
    requestLayout();
    ...
    collectViewAttributes();
    // 通过mWindowSession.addToDisplay 来进行显示
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
            getHostVisibility(), mDisplay.getDisplayId(),
            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
            mAttachInfo.mOutsets, mInputChannel);
    ...
}

在 ViewRootImpl 中可以找到 mWindowSession 初始化过程。

IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();

IWindowSession 是一个 Binder 对象,所以在添加过程中进行了 IPC 调用,那么 mWindowSession 的具体实现是什么,继续追踪代码,WindowManagerGlobal.getWindowSession() 方法如下:

public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            ...
            // getWindowManagerService 就是获取 WindowManagerService
            IWindowManager windowManager = getWindowManagerService();
            sWindowSession = windowManager.openSession()
            ...     
            }
            return sWindowSession;
        }
}

//WindowManagerService.openSession
 @Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
        IInputContext inputContext) {
    if (client == null) throw new IllegalArgumentException("null client");
    if (inputContext == null) throw new IllegalArgumentException("null inputContext");
    Session session = new Session(this, callback, client, inputContext);
    return session;
}

可以看到上面的 mWindowSession 对象真正实现是一个 Session。那就可以继续追踪到 Session.addToDisplay()


    final WindowManagerService mService;

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

通过上面的代码可以看到,最终是通过 WindowManagerService.addWindow 来进行添加。至于 WindowManagerService 怎么处理的暂时不做深入了。

总结

经过上面的一通分析,向 window 添加 view 的过程已经基本清晰:

通过写文章算是对 window 的添加过程再进行一边梳理。水平所限可能不够详细,仅供参考。


参考:

  • 《Android 开发艺术探索》——任玉刚
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值