1. Window类以及其实例
Window是什么可以看到Window.java文件,window类是一个抽象类,所以Window类不能实例化对象,只能通过子类实例化对象了。 但是里面会有一些参数这里就不详细解释了,有很多博客都讲的很清晰,我今天要讲的是这个大体的概念。
唯一继承自Window类的就是phoneWindow类了,所以phoneWindow类的实例就是唯一的Window类的实例了,那我们就可以通过phoneWindow来了解什么是window了。
2. Window和View的关系
这里需要讲一个概念,就是Window和View的关系。我们可以把Activity类比成一个画板,我们想去画画,那是不是得在画板上放一张纸?对了,那window就可以类比成一张纸,在纸上我们就可以画画了,那画是不是就可以比作我们看到的手机界面了。 但是这时候就有一个概念,View是什么?View可以理解成我们画笔在纸上留下的痕迹。 所以说window得依附于Activity,然后widow又是view的载体,想象下没有画板是不是就不好画画,没有纸画笔都没地方涂抹,那哪来的画?
3. Window的实例化
理解了这些概念就该看代码了。闲话少说直接上代码,这里直接跳转到Activity.java文件中的attach方法(今天就说window所以暂时不说为什么从这里开始),attach方法就是实现activity和window关联的地方。
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);// (1)
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
……………………………………//此处省略我不想看的若干行代码
mWindow.setWindowManager( //(2)
(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);
mWindow.setPreferMinimalPostProcessing(
(info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);
getAutofillClientController().onActivityAttached(application);
setContentCaptureOptions(application.getContentCaptureOptions());
从上述代码中可以看到,phoneWindow对象是在(1)处创建的,然后后面一堆就是在初始化mWindow以及Activity的一些属性。 此处可见mWindow是Activity的一个属性,且这个实例是一个phoneWindow类型的Window。 然后就是看到(2)处,setWindowManager,WindowManager是什么后面会再出一节专门解读,这里先接着往下看:setWindowManager方法是用来干嘛的,从函数实现来看,就是去SystemServer中去获取一个service,也就是WindowManagerService。 然后再通过WindowManagerService的代理和当前的Window,创建一个本地的WindowManager,这里WindowManager是一个接口,所以WindowManager的实例需要通过其子类WindowManagerImpl实现。 具体关系先按下不表+1;然后就获得一个通过当前Window实例化的WindowManagerImpl。
在(3)这里有小伙伴就要问了,啊,(3)这里的wm就是WindowManagerImpl类型的实例啦,为啥还要去通过createLocalWindowManager再去初始化一个WindowManagerImpl类型的实例呢?我只能说wm没有灵魂,(3)这里的this赋予了windowManagerImpl灵魂,啊其实就是没关联上,自己看一下就知道,这里wm的mParentWindow属性是null,只有经过步骤(3),才能将当前Window和windowManagerImpl关联上。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); //(3)
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
}
private WindowManagerImpl(Context context, Window parentWindow,
@Nullable IBinder windowContextToken) {
mContext = context;
mParentWindow = parentWindow;
mWindowContextToken = windowContextToken;
}
4. View的创建以及关联
好了现在就看到了Activity创建的过程中会再创建一个Window,也就是phoneWindow来作为View的载体。啊看到这那又有同学要问啦,view和Window的关系呢? 接下来就说下View和Window怎么关联上的。 Android第一行代码HelloWorld,大家应该都记得,我们函数中的setContentView方法吧,对的就是这个方法会去加载我们的整个布局。就是将view和Window关联上。 下面在细细道来:首先是在Activity.java中的setContentView方法:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
没啥好说的,getWindow()返回的是啥应该都知道了,就是上面说的phoneWindow了,直接去PhoneWindow.java中去看:
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();//(4)
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);//(6)
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
首先是步骤(4)处,这里也可以看到是一个if语句,这里mContentParent就是DecorView下面的一个子View,初始化DecorView的时候都会初始化他,我个人理解这个if语句其实就是保证要移除mContentParent下面的子view,因为这个地方就是我们直接操作的activity界面,不能有其他布局存在。 然后installDecorView方法里一大堆东西,我们也不细究,只看几个关键地方:
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);//(5)
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);//(6)
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeFrameworkOptionalFitsSystemWindows();
首先是(5),这里只是返回了一个DecorView的空壳子,然后再(6)处加载布局。好的这里就有一个DecorView了,那DecorView我就不详细讲他的结构了,总之他继承了FrameLayout类直接再往上就是上次说到的继承View,实现ViewParent了,所以他可以是一个ParentView,他下面就可以衍生出一个view树了。
看到这里PhoneWindow类会持有一个DecorView的实例,即我们Activity中的mWindow里面会有一个DecorView的实例,这个实例就可以作为View树的根(当然他其实也是有一个parent就是ViewRootImpl),我们接下来想在Activity中添加一些view的话,就可以直接把View“挂在”他的子View上就行了。
这个“挂在”的操作就是setContentView方法实现的功能了。接着往下看:generateLayout方法中干了什么呢?简单点说就是根据不同场景选择了一个DecorView的布局进行解析了。然后通过id找到contentParent这个view,再返回这个view。所以contentParent其实就是DecorView的一个子view。 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
然后我们回到步骤(6)处,这里的layoutResID不就是我们直接写的HelloWorld那个程序里,onCreate方法里,调用的setContentView方法的参数嘛,所以懂了为什么叫setContentView了吧?意思就是你能操作的就是这个contentView的区域。 所以这下就可以有个关系图总结下Activity、Window和View的关系了
而我们的setContentView方法,就是去编辑黄色这块区域的view了。其他子View这个区域就包括了几个地方,1、标题栏,2.底部导航键;(包括但不限于这两块,这里我没细究),当然还记得上次说的知识点吗? ViewRootImpl到底是什么 DecorView的ParentView是ViewRootImpl。 这个和这里的包含关系不相干,我就没说了。
5. 后话
了解了这些知识,那下一个问题又来了,Activity怎么控制他的一些View呢?那这里就涉及到了前面按下不表的内容,WindowManager这个知识点了。 WindowManager是接口,所以需要实现后才能实例化,而唯一实现这个接口的就是WindowManagerImpl类,不过WindowManagerImpl类其实也是把工作外包出去了,最后的活都是WindowManagerImpl的实例来干,但是仔细研究WindowManagerGlobal的话,你会发现他其实也很精,脏活累活他都是外包出去的,自己也就干了一点,本次的内容就先到这了,欲知后事如何,且听下回分解。