一文搞懂Activity、Window和View的关系

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的话,你会发现他其实也很精,脏活累活他都是外包出去的,自己也就干了一点,本次的内容就先到这了,欲知后事如何,且听下回分解。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值