val layoutParams = WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE shl WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL shl WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
windowManager.addView(imageView,layoutParams)
复制代码
Window是一个抽象概念,它是以View的形式存在,每一个Window都对应着View和ViewRootImpl,Window和View之间经过ViewRootImpl创建联系android
Window的整个添加过程可分为两部分执行:bash
View的添加是从调用windowManager.addView()开始的,其实点开windowManager只是一个继承ViewManager的接口,在活动中真正执行任务的是它的实现类WindowMangerImpl,所以方法会执行到WindowMangerImpl.addView(),但WindowMangerImpl 是个聪明的类,在addView()中除了验证设置LayoutParams的合法性以外,它又将全部的工做都桥接给WindowManagerGlobal执行:网络
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);//验证params的合法性
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); // 直接交给WindowManagerGlobal处理
}
复制代码
在具体执行方法前先介绍下WindowManagerGlobal中的各个集合的做用(见下面注释),在Window工做的整个过程他们时刻保存着Window和View的运行状态app
private final ArrayList<View> mViews = new ArrayList<View>(); // 保存全部Window对应的View
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); // 保存全部Window对应的ViewRootImpl
private final ArrayList<WindowManager.LayoutParams> mParams = // 保存全部Window对应的WindowManager.LayoutParams
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>(); // 保存正在被删除的View
复制代码
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView); // View的绘制
复制代码
上面是addView()中的部分代码,它执行了如下几个操做:异步
在保存了相关数据后,View真正的执行就是setView()这一句开始,下面看看ViewRootImpl中是如何实现View的测量绘制的ide
ViewRootImpl是View中的最高层级,属于全部View的根(但ViewRootImpl不是View,只是实现了ViewParent接口),实现了View和WindowManager之间的通讯协议oop
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
......
requestLayout();//对View进行第一次测量和绘制
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); //调用WindowSession的addTodiaplay()添加窗口
}
}
复制代码
requestLayout()内调用scheduleTraversals(),scheduleTraversals()中 会获取主线程的Handler而后发送消息执行TraversalRunnable实例,TraversalRunnable是Runnable的实现类,在run()方法中执行oTraversal() ,而后方法会执行到performTraversals()布局
//调用performMeasure完成Window内视图的测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (measureAgain) {
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
......
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, mWidth, mHeight); //完成View的布局Layout
}
......
performDraw();//对View的绘制
复制代码
performTraversals方法中,依次调用了performMeasure、performLayout、performDraw三个方法,这三个方法中又分别调用View或ViewGroupde的measure、layout和draw方法,完成了View的测量、布局和绘制;ui
@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);
}
复制代码
删除过程和添加同样最后执行任务的都是WindowManagerGlobal,先看下WindowManagerGlobal的removeView()方法:this
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
复制代码
removeView()中主要执行三个步骤:
ViewRootImpl root = mRoots.get(index);
View view = root.getView(); // 获取ViewRootImpl保存的View
boolean deferred = root.die(immediate); // 调用die()执行删除View
mDyingViews.add(view); // 将要删除的View添加到mDyingViews
复制代码
boolean die(boolean immediate) {
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
复制代码
在die()方法中根据传入的immediate执行同步或异步删除:
mView.dispatchDetachedFromWindow();
mWindowSession.remove(mWindow);
mView.onDetachedFromWindow();
WindowManagerGlobal.getInstance().doRemoveView(this);
复制代码
doDie是真正发起删除的地方,执行操做以下:
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams); // 设置新的LyaoutParams
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index); //更新root,mParams集合中的数据
mParams.remove(index);
mParams.add(index, wparams);//替换mParams中保存的wparams
root.setLayoutParams(wparams, false); // 更新View
}
}
复制代码
执行过程见上面注释,在 root.setLayoutParams中会触发ViewRootImpl的scheduleTraversals实现View的测量、布局、绘制;
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
复制代码
//Activity中
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID); //调用PhoneWindow的setContentView()
initWindowDecorActionBar();
}
复制代码
此处的getWindow()获得的就是前面建立的PhoneWindow ,因此setContentView()最终是在PhoneWindow中执行的
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
}
复制代码
setContentView()方法中,首先判断contentParent是否空,若是为空则执行installDecor(),installDecor()中有两处代码比较明显,分别是初始化DecorView和mContentParent,下面分别看看这两个方法
protected DecorView generateDecor(int featureId) {
return new DecorView(context, featureId, this, getAttributes()); //初始化DecorView,此时只是一个FrameLayout
}
复制代码
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//根据加载后的布局查找content
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); // 加载DecorView布局中的content容器
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content; //content的id
//mDecor.onResourcesLoaded()
final View root = inflater.inflate(layoutResource, null); //加载原始布局文件:包含标题栏和content
addView(root,new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT)); //将加载的布局添加到DecorView中
复制代码
在generateLayout中完成了布局layout文件的加载,具体细节以下:
@Override
public void setContentView(int layoutResID) {
mLayoutInflater.inflate(layoutResID, mContentParent); //加载布局到mContentParent中
}
复制代码
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
复制代码
到此DecorView和contentParent初始化已经完成,DecorView中加载了一个具备TitleView和ContentView的布局,而且加载的layoutResID也已加载到ContentView中,因此关于DecorView内部的工做已经完成,但DecorView未被添加到Window中,因此此时界面还是不可见
ActivityThread的handleResumeActivity()中调用Activity的makeVisible()方法,makeVisible中调用WindowManager.addView()将DecorView添加到PhoneWindow中,到此布局资源展现在屏幕上
//handleResumeActivity
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes()); //将DecorView添加到PhoneWindow中
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE); //设置DecorView可见
}
复制代码
val dialog = Dialog(this,R.style.Base_ThemeOverlay_AppCompat_Dialog)
dialog.setContentView(R.layout.dialog)
dialog.show()
dialog.cancel()
复制代码
从上面使用能够看出,dialog设置布局时和Activity都是使用setContentView,因此其执行初始化的过程和Activity一致,只是在将DecorView添加到Window时有所不一样
public void show() { //在Dialog显示时添加到Window中
mWindowManager.addView(mDecor, l); // 添加DecorView
}
复制代码
mWindowManager.removeViewImmediate(mDecor);
复制代码
Toast.makeText(this,"Toast",Toast.LENGTH_SHORT).show()
复制代码
Toast result = new Toast(context, looper); //建立Toast实例,并传入队列Loop
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null); //加载Toast布局并设置View
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text); //设置Toast的信息
result.mNextView = v; // 复制给mNextView
result.mDuration = duration; //设置Toast的弹出时长
return result;
复制代码
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
service.enqueueToast(pkg, tn, mDuration);
复制代码
针对上面方法中作几点说明:
Toast最终回调TN中的show方法,show()中发送Message到Handle,而后调用handleShow()
public void handleShow(IBinder windowToken) {
handleHide();
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = mDuration ==
Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
mParams.token = windowToken;
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
mWM.addView(mView, mParams);
}
复制代码
handleShow()中执行如下操做:
WindowManagerService是如何执行Window的添加和操做的?