Window

WindowManagerGlobal 内部有如下几个列表比较重要:
// mViews 存储的是所有Window 所对应的View
private final ArrayList<View> mViews = new ArrayList<View>();

    //mRoots 存储的是所有Window 所对应的ViewRootImpl
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();

    // mParams 存储的是所有Window 所对应的布局参数
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();

    // mDyingViews 存储量那些已经调用removeView() 但是删除操作还未完成的Window 对象
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

在 WindowManagerGlobal 的addView() 通过如下方式将Window 的一系列对象添加到列表。

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

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

 

Window 的更新

WindowManagerGlobal#updateViewLayout()

在该方法更新了View 的LayoutParams 

接着更新了ViewRootImpl 的LayoutParams 调用的setLayoutParams() ,这会调用scheduleTraversals() 来对View 重新布局,包括测量,布局,重绘这三个过程。

Window 的创建过程

View 不能单独存在,它必须附着在Window 这个抽象的概念上,因此有视图的地方就有Window 。

Activity Dialog Toast 等视图都对应着一个Window 。

Activity 的Window 创建过程

调用Activity 的attach() 为其关联运行过程中所依赖的一系列上下文环境变量。

activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);

在Attach() 里创建Window ,这里为创建了一个PhoneWindow ,并为其设置回调。由于Activity 实现了Window 的接口,因此当Window 接收到外界的状态改变时就会回调相应的方法。

onAttachedToWindow()      onDetachedFromWindow()      dispatchTouchEvent()     。。CallBack 还有好多

onContentChanged() 

mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }

PhoneWindow 的setContentView()

1. 如果没有DecorView ,那么就创建它

2.将View 添加到DecorView 的mContentParent 里

3.回调Activity 的 onContentChanged() 通知Activity 视图已经发生改变

DecorView 创建完了,但完成了真正添加和显示是在Activity#makeVisible()

void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager(); // 这里可以看出wm 是WindowManager ,它继承了ViewManager 
            wm.addView(mDecor, getWindow().getAttributes()); // 这里添加的mDecor
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE); // 设置成可见
    }

handleResumeActivity() 先调用performResumeActivity() 调用onResume() ,接着会调用makeVisible() ,这样Activity 才能被用户看到。

Dialog 的Window 创建过程

1.创建Window 在Dialog 的构造函数里也是创建了PhoneWindow ,并设置了回调。

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

2.初始化DecorView 并将Dialog 的视图添加到DecorView

在PhoneWindow 的setContentView() 加载这个layoutResID ,并将View 添加到DecorView

public void setContentView(@LayoutRes int layoutResID) {
        mWindow.setContentView(layoutResID);
    }

3.将DecorView 添加到Window 并显示

在Dialog 的show() ,会通过WindowManager 将DecorView 添加到Window

public void show() {
        ...
        mWindowManager.addView(mDecor, l);
        mShowing = true;

        sendShowMessage();
    }

当Dialog 被关闭时,会通过WindowManager 将DecorView 移除

void dismissDialog() {
       mWindowManager.removeViewImmediate(mDecor);
       ...
    }

Toast 的Window 创建过程

Toast 属于系统Window

显示与隐藏内部都对应着一个IPC 过程。

// 展示
public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;

        try {
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }


// 取消
getService().cancelToast(mPackageName, TN.this);


// 可以看到,显示和隐藏Toast 都需要通过NMS 来实现

Toast 的显示调用了NMS 的enqueueToast 方法,enqueueToast 方法首先将Toast 请求封装为ToastRecord ,并(入队)添加到mToastQueue 队列(其实就是一个ArrayList),对于非系统应用来说,mToastQueue 里最多有50 个这样做是为了防止DOS (试想如果用循环弹出Toast ,这种恶意行为倒置其他应用没有机会弹出Toast ,所以当超过50 则拒绝服务,这就是拒绝服务攻击的含义,这种常用于网络攻击的的)。

接着调用showNextToastLocked() 显示,并调用scheduleTimeoutLocked(record) 发送一盒个延时消息,用于到达该Toast 显示时长的隐藏操作。

到达延时时,NMS 会调用cancelToastLocked() 来取消隐藏此Toast 并将其从mToastQueue 队列移除。接着NMS会显示其他Toast (如果mToastQueue 队列里还有的话)。

那么Toast 的Window 在哪添加的呢?

原来是在IPC 回调到客户端时,在TN 的handleShow()  将Toast 的视图添加到Window。说白了这才是真正完成显示的地方。

public void handleShow(IBinder windowToken) {
    ...
    mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
    ...
    mWM.addView(mView, mParams);
}

在handleHide() 将Toast 视图从Window 移除。

public void handleHide() {
    if (mView.getParent() != null) {
        if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
        mWM.removeViewImmediate(mView);
    }
}

 

除了Activity   Dialog  Toast 以外,PopupWindow   菜单以及状态栏等都是通过Window 来实现的。View 都是附属在一个Window 上面的。

 

调用setContentView 方法,先获取一个window ,这个window 是在attach 方法里,创建的PhoneWindow ,将DecorView 设置给Window 

installDecor() 里进行 DecorView 的创建,获取mContentParent

mDecor == null 就去创建一个DecorView ,然后获取主题样式,根据主题样式(例如有没有标题栏),加载对应的视图到这个DecorView ,然后从中获取mContentParent 。就是前面布局中@android:id/content所对应的FrameLayout。将布局添加给他。

什么时候可见呢

就是先将DecorView 添加到Window 才行,在handleResumeActivity 进行了添加(调用Activity#makeVisible() 可见的),是通过WindowManager 添加的,之后还是调用的WindowManagerGlobal 的addView 方法,添加的,而其源码是创建了一个ViewRootImpl 实例,,将DecorView 交给了ViewRootImpl ,

所以是先创建DecorView ,然后添加到window ,window 交给ViewRoot,ViewRoot 调用performTraversals() 执行测量,布局与绘制,界面才显示出来。

通过以上了解可以知道,Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图。DecorView就是个顶层视图,是所有View的最外层布局。ViewRoot像个连接器,负责沟通,通过硬件的感知来通知视图,进行用户之间的交互。

硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值