请尊重原创,转载请注明出处【tianyl_Melodie】的博客,http://blog.csdn.net/tianyl_melodie/article/details/53469700
1、说明
在前一篇【Android源码学习笔记:Context、ActivityThread和Activity的生命周期】中,已经基本学习了Android中,对于一个Activity创建所处理的逻辑,当然对于细节的地方,当时我是一笔略过了。不过对于我们这种钻研的猿类来说,这显然是有些不合适的,所以在这一篇博客中,就来对于创建Activity和将Activity显示到界面上,做一些仔细的学习。
2、创建Activity的实现细节
我们已经说了,Activity的创建,是在ActivityThread中的handleLaunchActivity完成的。在handleLaunchActivity方法中,有一个performLaunchActivity,完成了对Activity的创建,和onCreate的调用,然后在handleLaunchActivity方法中,完成了接下来的onResume和OnPause调用。只是当时我们并没有真正的看到调用Activity的这两个方法,都是根据方法名,例如“callActivityOnPause”进行的猜测。那么较真的同学肯定会有疑惑,单凭一个方法名,怎么就能肯定它是真的调用了这些生命周期的方法了。就算是真的调用了生命周期的方法,那么Activity创建的细节是怎样的呢?所以,我们今天要从performLaunchActivity这个方法看起。
3、performLaunchActivity
之前我们已经知道了Activity是通过这个performLaunchActivity进行创建的,首先看这个方法中的代码片段:
//首先拿到packageInfo中的类加载器
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
//调用mInstrumentation中的newActivity创建activity
activity = mInstrumentation.newActivity(cl,component.getClassName(), r.intent);
//创建Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//调用activity的onCreate方法
mInstrumentation.callActivityOnCreate(activity, r.state);
上面代码就是通过这个performLaunchActivity创建activity的过程,还记得之前我们学习的,在handleLaunchActivity方法中调用Activity的onResume和OnPause也是通过mInstrumentation完成的。例如:
mInstrumentation.callActivityOnPause(r.activity);
当时我们只是说了它调用了Activity的onPause方法。看到这里,可能就会有同学开始好奇了,既然Activity的创建和生命周期方法的调用,有通过mInstrumentation这个对象来进行,那么它到底是一个什么呢?通过看ActivityThread的源码可以发现,它其实是一个Instrumentation类的实例,ActivityThread将它定义成了一个成员变量。
Instrumentation mInstrumentation;
4、Instrumentation
现在,我们就开始学习Activity创建时具体的细节。首先是Activity的创建:newActivity方法。通过查看Instrumentation的源码,可以看到,newActivity方法,实际上也只是通过调用Activity自己的newInstance方法而已:
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
在Instrumentation中,只是通过ClassLoader拿到Activity的对象,然后通过调用Activity自己的newInstance,就创建出了我们需要的Activity。
完成了创建之后,就是对Activity生命周期的调用了,可能有的同学已经猜到了,既然创建不过是这样通过拿到Activity的对象,调用一个它自己的newInstance而已,那么对于Activity生命周期的管理,是不是也没有我们想象中那么神秘呢。接下来,我们就看一下生命周期管理的细节:
mInstrumentation.callActivityOnPostCreate(activity, r.state);
首先,我们已经知道了,它是通过调用mInstrumentation的callActivityOnPostCreate的方法,完成对Activity中onCreate的调用的。接下来,我们就看一下在callActivityOnPostCreate中做了哪些事情。
public void callActivityOnCreate(Activity activity, Bundle icicle) {
if (mWaitingActivities != null) {
synchronized (mSync) {
final int N = mWaitingActivities.size();
for (int i=0; i<N; i++) {
final ActivityWaiter aw = mWaitingActivities.get(i);
final Intent intent = aw.intent;
if (intent.filterEquals(activity.getIntent())) {
aw.activity = activity;
mMessageQueue.addIdleHandler(new ActivityGoing(aw));
}
}
}
}
//调用activity的performCreate方法
activity.performCreate(icicle);
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
am.match(activity, activity, activity.getIntent());
}
}
}
}
我们可以看到,在Instrumentation的代码中,它是做了一件事,就是调用Activity自己的方法performCreate,也就是说Instrumentation更像是一个指挥官,在创建Activity的时候,指挥Activity需要调用什么方法。接下来我们就需要看Activity中的performCreate方法了:
final void performCreate(Bundle icicle) {
onCreate(icicle);
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
}
这是Activity中的performCreate方法,我们可以看到在第一行就调用了onCreate方法,到这里,我们基本就已经完成了对Activity创建和生命周期源码的追踪。既然Activity已经创建了,那么它又是如何显示在界面上的呢?接下来,我们就来学习activity是如何显示的。
5、Activity是如何显示的
如果我们要让一个Activity显示一个布局,那么我们需要调用setContentView这个方法,那么这个方法是如何让我们的Activity把这个布局显示在屏幕上的呢。首先,我们需要看Activity中的setContentView方法:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initActionBar();
}
当然,Activity中其实还有几个重载的setContentView方法,不过实现方式基本一样,都是通过getWindow().setContentView进行实现的。那么getWindow()拿到的是哪个对象呢?
public Window getWindow() {
return mWindow;
}
我们查看Activity中的getWindow方法,发现它返回的是一个成员变量,也就是说在我们调用setContentView这个方法之前,它已经被创建出来了,那么是在哪里创建的这个mWindow对象的呢?
这时我们需要回到performLaunchActivity这个方法,之前我们学习的是其中对Activity创建和生命周期的调用,我们仔细看这个方法,在创建完Activity,还没有调用onCreate方法之前,它还调用了一个Activity的attach方法,我们都是在在onCreate方法中调用的setContentView方法,那么想必这个写在调用onCreate方法前的一个方法,应该就是创建mWindow对象的逻辑了。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
Activity activity = null;
try {
//创建Activity
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
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 {
//创建Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
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);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config);
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;
mInstrumentation.callActivityOnCreate(activity, r.state);
......
}
r.paused = true;
mActivities.put(r.token, r);
} catch
......
return activity;
}
我们跳转到Activity的源码中,查看attach方法:
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) {
attachBaseContext(context);
mFragments.attachActivity(this, mContainer, null);
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
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;
}
可以看到attach方法中基本就是对一些成员变量的初始化,其中就有mWindow对象的创建。我们接着看PolicyManager.makeNewWindow(this)创建的实现逻辑:
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
其中,sPolicy是PolicyManager的一个成员对象,它是IPolicy接口实现类,在Android中,IPolicy主要提供的就是处理Window的一些方法。它只有一个唯一的实现类Policy。我们看实现类中的makeNewWindow方法:
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
它创建了一个PhoneWindow对象。看到这里,我们算是基本理清了把Activity显示在界面上的逻辑,就是通过拿到PhoneWindow 对象,然后调用它的setContentView完成的。
6、PhoneWindow
接下来,我们再学习PhoneWindow中的setContentView是如何把Activity显示到界面上的。首先,我们看setContentView的代码:
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
首先,它会判断一个mContentParent是否为空。mContentParent 其实就是一个ViewGroup,它就是当前Activity的根布局。如果它为空,我们就会初始化一个根布局。
我们接着看根布局初始化逻辑,因为初始化根布局的逻辑非常长,所以这里只列举出其中主要的一些代码:
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
//对一些属性的设置
......
}
}
首先,系统会判断mDecor这个对象是否为null,如果为null,就创建一个mDecor对象。mDecor它是DecorView这个类的实例对象,它是继承自FrameLayout的,也是Android显示中,最外层的布局。系统在创建完这个最外层的DecorView之后,还做一个判断mContentParent是否为null,如果为null,就创建一个mContentParent对象。我们接下来看mContentParent = generateLayout(mDecor)这行代码做了那些事情。
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
......
mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
Drawable drawable = mBackgroundDrawable;
if (mBackgroundResource != 0) {
drawable = getContext().getResources().getDrawable(mBackgroundResource);
}
mDecor.setWindowBackground(drawable);
drawable = null;
if (mFrameResource != 0) {
drawable = getContext().getResources().getDrawable(mFrameResource);
}
mDecor.setWindowFrame(drawable);
// System.out.println("Text=" + Integer.toHexString(mTextColor) +
// " Sel=" + Integer.toHexString(mTextSelectedColor) +
// " Title=" + Integer.toHexString(mTitleColor));
if (mTitleColor == 0) {
mTitleColor = mTextColor;
}
if (mTitle != null) {
setTitle(mTitle);
}
setTitleColor(mTitleColor);
}
mDecor.finishChanging();
return contentParent;
}
因为创建mContentParent对象的代码较多,所以这里也只列出创建的主要代码。
我们可以看到,系统会inflate出一个View,然后将它添加到DecorView中,然后查找view中id为"ID_ANDROID_CONTENT"的view,当然它是一个字符变量。
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content
最后将它return出去。看到这里,我们mDecor和mContentParent的创建也已经完成了,想必机智的同学已经猜到了。mContentParent这个变量的名字和setContentView方法这么像,根据Google工程师的命名,它们之间肯定有这某种联系。
最后我们再回到我们最开始的setContentView方法:
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
没错,看到熟悉的mContentParent对象。正如同我们猜测的那样,这个方法所做的,就是把我们设置的view添加到mContentParent对象中而已。
7、总结
好了,Activity的创建和显示的学习一算是告一段落了。
①Activity的创建是在performLaunchActivity这个方法中完成的。不过它并非直接new一个Activity对象,而已通过Instrumentation这个类的newActivity的方法创建。
②在Instrumentation中,创建Activity是通过调用Activity自己的newInstance完成的。
③在创建完Activity之后,会调用Activity的attach方法,在这个方法中会初始化mWindow对象,它其实是一个PhoneWindow对象的实例。
④创建PhoneWindow对象是通过PolicyManager.makeNewWindow(this)方法完成的,PolicyManager中持有接口IPolicy唯一的实现类Policy的实例对象,接口IPolicy主要提供的就是处理Window的一些方法。
⑤在完成了activity的attach方法后,会通过Instrumentation调用callActivityOnCreate方法,最后调用到activity的performCreate方法,在这个方法中调用了activity的onCreate方法。
⑥当我们在onCreate方法中调用setContentView方法设置界面的时候,其实是在向mContentParent中addView。⑦ mContentParent其实是DecorView中的一个id是“com.android.internal.R.id.content”的布局,DecorView是Android界面的根布局,它继承自FrameLayout。
到此,Activity的创建和显示也算是学习完了,当然Android的源码远比本博客多核复杂,一篇博客也不可能面面俱到,对象activity细节中的一些东西,还是需要同学去自己研究咯。