在上一篇中我们看了Context的创建过程,我们知道,每一个Activity都有一个Context,同时它还有一个Window对象,用来描述一个具体的应用程序窗口。
现在我们就来分析分析它的创建

还记得在Activity初始化的时候,调用attach方法的时候,会创建一个PhoneWindow对象,我们就从这里开始分析。

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
     ... ... 
        mWindow.setWindowManager(
                (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);
    }

PhoneWindow继承自Window:

public class PhoneWindow extends Window implements MenuBuilder.Callback

Window是一个抽象类,public abstract class Window,可以看看它的介绍:

Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.

它是一个顶层窗口look和behavior的抽象类,它的实例应该用于那些被添加了windowManager的顶层视图。它提供了标准UI策略像background,title area ,默认**处理等。

根据这个介绍,我们这个知道的是,它跟View有关。

然后我们看看它的构造函数:


 // This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;
	private LayoutInflater mLayoutInflater;
	
	 // This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    ViewGroup mContentParent;


 public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
    }
    /**
     * Constructor for main window of an activity.
     */
    public PhoneWindow(Context context, Window preservedWindow,
            ActivityConfigCallback activityConfigCallback) {
        this(context);
        // Only main activity windows use decor context, all the other windows depend on whatever
        // context that was given to them.
        mUseDecorContext = true;
        if (preservedWindow != null) {//隐藏Widow。preservedWindow一般来说是null
            mDecor = (DecorView) preservedWindow.getDecorView();
            mElevation = preservedWindow.getElevation();
            mLoadElevation = false;
            mForceDecorInstall = true;
            // If we're preserving window, carry over the app token from the preserved
            // window, as we'll be skipping the addView in handleResumeActivity(), and
            // the token will not be updated as for a new window.
            getAttributes().token = preservedWindow.getAttributes().token;
        }
        // Even though the device doesn't support picture-in-picture mode,
        // an user can force using it through developer options.
        boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),
                DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
        mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_PICTURE_IN_PICTURE);
        mActivityConfigCallback = activityConfigCallback;
    }

其实可以看到,在PhoneWindow的构造中,先是调用了父类的构造方法:

public Window(Context context) {
        mContext = context;
        mFeatures = mLocalFeatures = getDefaultFeatures(context);
    }

然后调用LayoutInflater.from方法创建一个LayoutInflate对象。这样PhoneWindow就可以通过mLayoutInflate来创建应用程序窗口的视图,这个视图用类型用DecorVIew的成员变量mDecorView来描述。
PhoneWindow类中还有一个类型为ViewGroup的成员变量mContentParent,用来描述一个视图容器,这个容器存放的就是mDecor所描述的视图内容,不过这个容器也可能指向mDecor本身。

在attach方法中,我们知道,在PhoneWindow创建完后,就会调用mWindow.setCallback(this);


//Winodw.java
 /**
     * Set the Callback interface for this window, used to intercept key
     * events and other dynamic operations in the window.
     *
     * @param callback The desired Callback interface.
     */
    public void setCallback(Callback callback) {
        mCallback = callback;
    }

正在启动的Activity组件会他创所实现的Callback接口设置到与它所关联的PhoneWindow对象中,这样当这个PhoneWindow对象接收到系统给它分发的IO输入事件,例如,键盘和触摸事件,就可以转发给它所关联的Activity中去处理。

继续看,在attach方法中,一般情况下,mWinodw会设置:mWindow.setSoftInputMode(info.softInputMode);

//Window.java
  /**
     * Specify an explicit soft input mode to use for the window, as per
     * {@link WindowManager.LayoutParams#softInputMode
     * WindowManager.LayoutParams.softInputMode}.  Providing anything besides
     * "unspecified" here will override the input mode the window would
     * normally retrieve from its theme.
     */
    public void setSoftInputMode(int mode) {
        final WindowManager.LayoutParams attrs = getAttributes();
        if (mode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            attrs.softInputMode = mode;
            mHasSoftInputMode = true;
        } else {
            mHasSoftInputMode = false;
        }
        dispatchWindowAttributesChanged(attrs);
    }

protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) {
        if (mCallback != null) {
            mCallback.onWindowAttributesChanged(attrs);
        }
    }

这个mode有下面六个取值:

  • SOFT_INPUT_STATE_UNSPECIFIED:没有指定软键盘输入区域的显示状态。
  • SOFT_INPUT_STATE_UNCHANGED:不要改变软键盘输入区域的显示状态
  • SOFT_INPUT_STATE_HIDDEN:在合适的时候隐藏软键盘输入区域;例如:当用户导航到窗口时
  • SOFT_INPUT_STATE_ALWAYS_HIDDEN:当窗口获得焦点时,总是隐藏软键盘输入区域。
  • SOFT_INPUT_STATE_VISIBLE:在合适的时候显示软键盘输入区域,例如,当用户导航到当前窗口时。
  • SOFT_INPUT_STATE_ALWAYS_VISIBLE:当窗口获得焦点时,总是显示软键盘输入区域。

当参数mode的值不等于SOFT_INPUT_STATE_UNSPECIFIED时,就表示当前窗口被指定软键盘输入区域的显示模式,这时候Window类的成员函数setSoftInputMode就会将成员变量mHasSoftInputMode的值设置为true,并且将这个显示模式保存在用来描述窗口布局属性的一个WindowManager.LayoutParams对象的成员变量softInputMode中,否则的话,就会将成员变量mHasSoftInputMode的值设置为false。

设置完成窗口的软键盘输入区域的显示模式之后,如果Window的成员变量mCallback指向了一个窗口回调窗口,那么就会调用mCallback.onWindowAttributesChanged(attrs);来通知与窗口所关联的Activity组件,它的窗口属性发生了变化。

继续看attach方法,后面会调用mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);来设置应用程序窗口的本地窗口管理器:

//Window.java
/**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is <em>not</em> used for displaying the
     * Window itself -- that must be done by the client.
     *
     * @param wm The window manager for adding new windows.
     */
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

参数appToken用来描述正在处理的窗口与哪一个Activity组件关联,它是一个Binder代理对象,引用在ActivityManagerService这里所创建的一个类型为ActivityRecord的Binder本地对象。在Activity启动的过程中,每一个Activity组件在ActivityManagerSerivce这里,都有一个对应的ActivityRecord对象,用来描述Activity组件的运行状态。这个Binder代理对象会保存在mAPPToken中,这样当前正在处理的窗口就可以知道与它关联的Activity是什么。
参数appName用来描述当前正在处理的窗口所关联的Activity的名称。
参数wm用来描述一个窗口管理器,如果它等于null,就通过wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);得到一个默认的窗口管理器,然后再通过它窗口一个本地窗口管理器,用来维护当前正在处理的应用程序窗口。

//Context.java
@SuppressWarnings("unchecked")
    public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) {
        // Because subclasses may override getSystemService(String) we cannot
        // perform a lookup by class alone.  We must first map the class to its
        // service name then invoke the string-based method.
        String serviceName = getSystemServiceName(serviceClass);
        return serviceName != null ? (T)getSystemService(serviceName) : null;
    }
   
   
//ContextWrapper.java

@Override
    public String getSystemServiceName(Class<?> serviceClass) {
        return mBase.getSystemServiceName(serviceClass);
    }
//ContextImpl.java
 @Override
    public String getSystemServiceName(Class<?> serviceClass) {
        return SystemServiceRegistry.getSystemServiceName(serviceClass);
    }

//SystemServiceRegistry.java
 private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
            new HashMap<Class<?>, String>();
public static String getSystemServiceName(Class<?> serviceClass) {
        return SYSTEM_SERVICE_NAMES.get(serviceClass);
    }

//ContextWrapper.java
 @Override
    public Object getSystemService(String name) {
        return mBase.getSystemService(name);
    }

//ContextImpl.java
 @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

//SystemServiceRegistry.java
 /**
     * Gets a system service from a given context.
     */
    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }


其实我们可以看到,得到ServiceManager都是通过mBase来完成的,这个mBase是在创建Activity之前,创建的ContextImpl对象,然后在ContextImpl中通过SystemServiceRegistry去得到ServiceManager。

回到setWindowManger方法中,在创建本地WindowManager之前,会将得到的wm强制类型转换为WindowManagerImplWindowManagerImpl实现了WindowManager接口,因此它可以用来管理应用程序窗口

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;//其实就是所关联的Activity中的ContextImpl
    private final Window mParentWindow;//其实就是所关联的Activity中的PhoneWindow
    private IBinder mDefaultToken;
    public WindowManagerImpl(Context context) {
        this(context, null);
    }
    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }
... ... 
}

接下来,调用mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);创建本地应用程序窗口:

 public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

在WindowMangerImpl中有一个方法:

 @Override
    public Display getDefaultDisplay() {
        return mContext.getDisplay();
    }

之前我们有提到mContext其实就是所关联的Activity的ContextImpl:

 @Override
    public Display getDisplay() {
        if (mDisplay == null) {
            return mResourcesManager.getAdjustedDisplay(Display.DEFAULT_DISPLAY,
                    mResources);
        }
        return mDisplay;
    }

Display就是用来描述系统屏幕属性的,可以通过这个独享,对正在处理的窗口配置一些可定义的屏幕属性。

这样,Window的创建就分析完了,我们可以总结下面几点

  • 一个AActivity所关联的应用程序窗口对象的类型是PhoneWinodw。
  • 这个类型为PhoneWindow的应用程序窗口是通过一个类型为WindowManagerImpl的本地窗口管理器来维护的。
  • 这个类型为WindowManagerImpl的本地窗口管理器是通过Context.getSystemManager()创建的WindowManagerImpl来维护的。
  • 这个类型为PhoneWindow的应用程序窗口内部有一个类型为DecorView的视图对象,这个视图对象才是真正用来描述一个Activity组件的UI的。

PhoneWindow和Window的关系

Android Java层UI渲染实现 二 Window的创建

Activity和Window的关系

Android Java层UI渲染实现 二 Window的创建

参考:
https://www.kancloud.cn/alex_wsc/androids/473771

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-11-01
  • 2021-10-05
  • 2022-02-13
  • 2022-12-23
  • 2022-12-23
  • 2021-07-14
猜你喜欢
  • 2021-10-14
  • 2021-11-14
  • 2021-10-08
  • 2021-12-12
  • 2021-11-16
  • 2021-05-03
  • 2021-05-17
相关资源
相似解决方案