在安卓中,我们都是用Activity来显示一个界面,在Activity中我们设置了一个布局layout,然后 整个手机的窗口就会按照我们所设置的这个布局来展示整个界面,那么整个界面是怎么展示出来的?这个中间有哪些操作?在其中Window,Activity,View之间是一个怎样的关系存在?以下展开简要的分析:
关于Activity的创建这里仅做一个简单的说明,更详细的可见Activity的启动,Activity的启动最终会进入到ActivityThread的handleLaunchActivity中,以下是该方法的源码(SDK22)
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
.
.
.
// Initialize before creating the activity
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
.
.
.
}
在上述的方法中我们需要关注的有三个地方:
1.WindowManagerGlobal.initialize(),这一段代码一看上去就是初始化了一个WindowManagerGlobal,至于WindowManagerGlobal是什么我们这里不用去管,只需要知道对这个东西进行了初始化。
2.Activity a=performLaunchActivity(r,customIntent),这一段代码很明显是通过调用一个方法返回了一个Activity,这里可以大胆参测一下应该是通过什么方式创建了一个要启动的Activity对象然后返回,进入performLaunchActivity方法如下:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
.
.
.
// 1.通过反射创建Activity
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
//2.创建Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
//3.创建Context
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
//4.回调Activity.attach方法
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);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
// 4.回调Activity的onCreat方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
//5.回调Activity的onStart方法
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
performLaunchActivity的源码中有5个中文注释的地方是比较重要的地方,这5个地方我们本篇文章所关注的为Activity.attach方法回调,然后依次onCreate方法,onStart方法,这都是Activity在启动创建过程中会执行的一些生命周期方法。
3.handleResumeActivity(...)方法调用,这是在performLaunchActivity方法执行完之后调用的,这个方法也是需要关注的地方,后续会详细的介绍。
总结:所以关于Activity的创建启动,我们需要知道的是a.WindowManagerGlobal初始化,b.attach方法和onCreate方法的依次回调,c.handleResumeActivity的方法执行。
在Activity的启动过程中会回调Activity的attach方法,通过查看attach方法我们可以看到mWindow,mWindowManager的创建,并且mWindow和mWindowManager都是Activity的成员变量。
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) {
attachBaseContext(context);
mFragments.attachActivity(this, mContainer, null);
//创建window
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
.
.
.
//创建windowmanager
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;
}
以上是Activity的attach方法的部分源码,
通过PolicyManager来创建了Window,PolicyManager是一个策略类,它的实现是在Policy的makeNewWindow方法中:
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
可以看到通过new一个PhoneWindow来创建Activity的mWindow。
通过context.getSystemService来获取WindowManager,通过查看ContextImpl中的返回值我们可以发这个WindowManager是一个WindowManagerImpl。
registerService(WINDOW_SERVICE, new ServiceFetcher() {
Display mDefaultDisplay;
public Object getService(ContextImpl ctx) {
Display display = ctx.mDisplay;
if (display == null) {
if (mDefaultDisplay == null) {
DisplayManager dm = (DisplayManager)ctx.getOuterContext().
getSystemService(Context.DISPLAY_SERVICE);
mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
}
display = mDefaultDisplay;
}
return new WindowManagerImpl(display);
}});
到此Activity中的mWindow和mWindowManager就都已经在attach方法中创建好,其中mWindow是一个PhoneWindow对象,mWindowManager是由WindowManagerImpl实现。
Activity的启动过程中,在attach方法之后就要执行onCreate方法了,在上述所讲的attach方法中,我们已经把mWindow和mWindowManager和Activity关联在一起了。我们在应用开发过程中,常常会在onCreate方法中调用一个setContentView的方法,传入的参数是我们定义好的布局的id,下面跟随这个方法去了解Activity中View的关联:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
上述中可以看到,setContentView方法实际上是调用了getWindow的setContentView方法,getWindow返回的为mWindow变量也就是PhoneWindow,所以会执行PhoneWindow中的setContentView方法:
@Override
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();
} 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);
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
分析PhoneWindow中的setContentView方法,需要关注的地方有三处:
1.installDecor()方法的调用,在PhoneWindow中有几个变量,mDecor,mContentRoot,mContentParent,
mDecor:是一个DecorView,作为Window中布局的顶层View。
mContentRoot:一个ViewGroup,mDecor中添加布局的根布局容器。
mCntentParent:一个ViewGroup,mDecor中添加的布局中的一个容器,用来添加setContentView设置的布局。
以上的三个PhoneWindow中的成员变量会在installDecor方法中初始化好。
2.mLayoutInflater.inflate(layoutResID,mContentParent),这一段代码是把我们手动设置的布局添加到mContentParent中,也就是添加到Window中。
3.cb.onContentChanged(),这是一个回调方法,cb是Activity,在设置了布局之后,会回调Activity中的onContentChanged方法。
所以在onCreate中的setContentView方法的调用,会把我们所设置的布局添加到mContentParent容器中,也就是添加到mDecor中,而mDecor作为作为PhoneWindow的成员变量,此时意味着Activty,Window,WindowManager,View都已经伴随着Activity启动的某些生命周期方法准备好了,接下来就是View显示的问题。
在Activity的attach,onCreate方法中,我们已经把Window,WindowManager,View和Activity关联好了,接下来的问题就是View的显示了。在Activity的handleLaunchActivity我们还要关注的一个方法是handleResumeActivity方法,它是在Activity的attach,onCreate,onStart方法之后被调用,源码如下:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(
TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow
+ ", finished: " + a.mFinished);
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r);
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
+ r.activityInfo.name + " with newConfig " + r.newConfig);
performConfigurationChanged(r.activity, r.newConfig);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.newConfig));
r.newConfig = null;
}
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
+ isForward);
WindowManager.LayoutParams l = r.window.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
!= forwardBit) {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);
}
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
if (!r.onlyLocalRequest) {
r.nextIdle = mNewActivities;
mNewActivities = r;
if (localLOGV) Slog.v(
TAG, "Scheduling idle handler for " + r);
Looper.myQueue().addIdleHandler(new Idler());
}
r.onlyLocalRequest = false;
// Tell the activity manager we have resumed.
if (reallyResume) {
try {
ActivityManagerNative.getDefault().activityResumed(token);
} catch (RemoteException ex) {
}
}
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
}
这个方法中,我们主要关注的是三句代码,分别是wm.addView(decor,l),wm.updateViewLayout(decor,l),r.activity.makeVisible():
1.wm.addView():wm是指Activity的WindowManager,前面我们有提到过,Activity中的mWindowManager实际上是由WindowManagerImpl实现的,那么我们跟进到WindowManagerImpl中可以看到,它的真正实现是在WindowManagerGlobal中,查看WindowManagerGlobal中addView方法如下:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
.
.
.
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
前面的很多判断直接省略,可以看到它创建了一个ViewRootImpl,并且在WindowManagerGlobal中有一个几个集合分别是mViews,mRoots,mParams(因为这个类是单单例,它需要管理很多的Window),然后通过root.setView方法,把View设置到ViewRootImpl中。在ViewRootImpl中的setView方法可以看到它和WindowSession的交互等,这里我们不探讨。而我们通过对ViewRootImpl的了解可以知道,它里面有控制布局绘制显示以及事件传递等很多方法,它就相当于View相关的一个管理者,而到此我们已经把该Activity的View设置到了VIewRootImpl中,并且从WindowManagerGlobal中的几个数组我们可以知道,每个Activity,mWindow都有单独对应的ViewRootImpl。接下来就是View绘制显示的问题了。
2.第二句代码,wm.updateViewLayout(decor,l):在WindowManagerGlobal中我们可以看到,它调用了ViewRootImpl的setLayoutParams方法,在该方法中会触发ViewRootImpl中TraversalRunnable的run方法,从而触发doTraversal方法,这个方法就会触发一系列的View的测量布局绘制等方法。所以这个时候View已经绘制出来即将显示了。
3.r.activity.makeVisible:该方法就是设置mDecor为VISIBLE,前面已经绘制好了,这个时候布局就真正的显示出来了。
所以在ActivityThread的handleResumeActivity的方法中,我们把ViewRootImpl和Activity关联起来了,并且完成了View的真正的显示,ViewRootImpl还会在后续完成事件传递等View的其它一些管理。
对整个思路做一个总结:
1.Activity的启动会执行ActivityThread中的handleLaunchActivity方法;
2.handleLaunchActivity方法会调用Activity的的attach方法,在attach方法中会创建对应的PhoneWindow和关联WindowManager。
3.handleLaunchActivity会回调Activity的onCreate方法,我们在onCreate方法中设置布局,该布局会关联到PhoneWindow中。
4.handleLaunchActivity方法中调用handleResumeActivity方法,该方法会把PhoneWindow中的布局添加到wm中,wm的真正实现WindowManagerGlobal会把布局添加到ViewRootImpl中,并完成布局的测量绘制等,实现Activity中布局的显示。
5.ViewRootImpl中通过WindowSession把视图添加到WMS中。