每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一个具体的应用程序窗口。与Activity组件所关联的窗口对象的实际类型为PhoneWindow。
那么创建这个window的过程就如图所示:
上面的图可以分为9个步骤,接下来我们开始分析每一个步骤
如果想研究Activity中的Window创建过程就必须知道Activity的启动过程,这里不做具体讲解,我们只要知道最终会由ActivityThread中的performLaunchActivity()来完成整个启动过程的,这个过程中会调用Activity的attach方法为activity关联运行过程中所依赖的一系列上下文环境变量。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......省略部分
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
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);
......省略部分
return activity;
}
步骤一:Activity.attach
在Activity的attach方法里,系统会创建Activity所属的Window对象并为它设置回调接口,首先是调用PolicyManager的makeNewWindow来创建一个类型为PhoneWindow的应用程序窗口,并且保存在Activity类的成员变量mWindow中,因为设置了Callback接口,所以当外界的状态改变的时候Activity就能感知到并做出回应处理。
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
private Window mWindow;
......
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,
Object lastNonConfigurationInstance,
HashMap lastNonConfigurationChildInstances,
Configuration config) {
......
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
......
mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
......
}
......
}
步骤二:PolicyManager.makeNewWindow
Activity的Window是通过PolicyManager的一个方法创建的, PolicyManager是一个窗口管理策略类,它在第一次被使用的时候,就会创建一个Policy类实例,并且保存在静态成员变量sPolicy中,而PolicyManager的makeNewWindow方法其实就是调用的sPolicy.makeNewWindow(context);来实现的。
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static {
// Pull in the actual implementation of the policy at run-time
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
} catch (InstantiationException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
}
}
......
// The static methods to spawn new policy-specific objects
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
......
}
步骤三:Policy.makeNewWindow
这个方法就跟简单了就是创建了一个PhoneWindow对象,然后返回给了调用者了.
public class Policy implements IPolicy {
......
public PhoneWindow makeNewWindow(Context context) {
return new PhoneWindow(context);
}
......
}
步骤四:new PhoneWindow
下面代码可以发现,使用LayoutInflater.from创建了一个LayoutInflater,PhoneWindow使用它来创建窗口的视图,并放在类型为ViewGroup的成员变量mContentParent中,这个稍后会讲。
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
这个Context通过一层层的调用我们知道正是当前启动的这个Activity,这样一来那,PhoneWindow就可以访问与Activity组件相关的资源了。接下来我们接着回到第一个步骤,在Activity的attach中创建了Window,来给当前的Activity通过setCallback来设置窗口回调接口。接下来就分析一下这个方法
步骤五:Window.setCallback
当我们给Activity设置上回调的时候Callback接口中的方法很多,这样当这个PhoneWindow对象接收到系统给它分发的IO输入事件,例如,键盘和触摸屏事件,转发给与它所关联的Activity组件处理,,我们比较熟悉的方法有:dispatchTouchEvent,onAttachedToWindow,等等吧。
public abstract class Window {
......
private Callback mCallback;
......
/**
* 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;
}
......
}
步骤六:Window.setSoftInputMode
接着第一步骤继续往下走,设置应用程序窗口的软键盘输入区域的显示模式,此方法中首先判断当参数mode的值是否等于SOFT_INPUT_STATE_UNSPECIFIED,如果不等于,就表示当前窗口被指定软键盘输入区域的显示模式,然后调用函数onWindowAttributesChanged来通知与窗口所关联的Activity组件,它的窗口布局属性发生了变化。
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);
}
步骤七:Window.setWindowManager
我们继续来看Activity的attach方法,接下来就到了调用Window的setWindowManager来实现窗口管理。
appToken保存了当前处理的窗口是与哪一个Activity组件关联的。
appName用来描述当前正在处理的窗口所关联的Activity组件的名称。
wm是用来描述一个窗口管理者,一开始传进来的就是一个null所以会创建一个窗口管理器,然后再使用它创建一个本地窗口管理器即可用来维护当前正在处理的应用程序窗口。
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);
}
步骤八:mContext.getSystemService(Context.WINDOW_SERVICE);
因为当前的mContext是传入的Activity,所以调用的是Activity的getSystemService,判断传入的name等于WINDOW_SERVICE,返回了一个WindowManager。
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}
步骤九: ((WindowManagerImpl)wm).createLocalWindowManager(this);
创建一个WindowManagerImpl,类继承WindowManager,所以就是创建了一个窗口管理。这样就可以使用窗口管理器来管理应用程序窗口了
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
至此,我们就分析完成一个Activity组件所关联的应用程序窗口对象的创建过程了。如果有错误的地方希望大家多多指出来,此时视图还没有添加上来,接下来的一篇我们将分析应用窗口视图对象的添加过程!