8.2.1 Activity的创建
最后更新于:2022-04-02 05:52:04
我们已经知道了Activity的生命周期,如onCreate、onDestroy等,但大家是否考虑过这样一个问题:
- 如果没有创建Activity,那么onCreate和onDestroy就没有任何意义,可这个Activity究竟是在哪里创建的?。
第4章中的“Zygote分裂”一节已讲过,Zygote在响应请求后会fork一个子进程,这个子进程是App对应的进程,它的入口函数是ActivityThread类的main函数。ActivityThread类中有一个handleLaunchActivity函数,它就是创建Activity的地方。一起来看这个函数,代码如下所示:
**ActivityThread.java**
~~~
private final voidhandleLaunchActivity(ActivityRecord r, Intent customIntent) {
//①performLaunchActivity返回一个Activity
Activitya = performLaunchActivity(r, customIntent);
if(a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
//②调用handleResumeActivity
handleResumeActivity(r.token, false, r.isForward);
}
......
}
~~~
handleLaunchActivity函数中列出了两个关键点,下面对其分别介绍。
1. 创建Activity
第一个关键函数performLaunchActivity返回一个Activity,这个Activity就是App中的那个Activity(仅考虑App中只有一个Activity的情况),它是怎么创建的呢?其代码如下所示:
handleLaunchActivity函数中列出了两个关键点,下面对其分别介绍。
**ActivityThread.java**
~~~
private final ActivityperformLaunchActivity(ActivityRecord r,
Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
......//完成一些准备工作
//Activity定义在Activity.java中
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
/*
mInstrumentation为Instrumentation类型,源文件为Instrumentation.java。
它在newActivity函数中根据Activity的类名通过Java反射机制来创建对应的Activity,
这个函数比较复杂,待会我们再分析它。
*/
activity = mInstrumentation.newActivity(
cl,component.getClassName(), r.intent);
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
}catch (Exception e) {
......
}
try {
Application app =
r.packageInfo.makeApplication(false,mInstrumentation);
if (activity != null) {
//在Activity中getContext函数返回的就是这个ContextImpl类型的对象
ContextImpl appContext = new ContextImpl();
......
//下面这个函数会调用Activity的onCreate函数
mInstrumentation.callActivityOnCreate(activity, r.state);
......
return activity;
}
~~~
好了,performLaunchActivity函数的作用明白了吧?
- 根据类名以Java反射的方法创建一个Activity。
- 调用Activity的onCreate函数,开始SDK中大书特书Activity的生命周期。
那么,在onCreate函数中,我们一般会做什么呢?在这个函数中,和UI相关的重要工作就是调用setContentView来设置UI的外观。接下去,需要看handleLaunchActivity中第二个关键函数handleResumeActivity。
2. 分析handleResumeActivity
上面已创建好了一个Activity,再来看handleResumeActivity。它的代码如下所示:
**ActivityThread.java**
~~~
final void handleResumeActivity(IBinder token,boolean clearHide,
boolean isForward) {
boolean willBeVisible = !a.mStartedActivity;
if (r.window == null && !a.mFinished&& willBeVisible) {
r.window= r.activity.getWindow();
//①获得一个View对象
Viewdecor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//②获得ViewManager对象
ViewManagerwm = a.getWindowManager();
......
//③把刚才的decor对象加入到ViewManager中
wm.addView(decor,l);
}
......//其他处理
}
~~~
上面有三个关键点。这些关键点似乎已经和UI部分(如View、Window)有联系了。那么这些联系是在什么时候建立的呢?在分析上面代码中的三个关键点之前,请大家想想在前面的过程中,哪些地方会和UI挂上钩呢?
- 答案就在onCreate函数中,Activity一般都在这个函数中通过setContentView设置UI界面。
看来,必须先分析setContentView,才能继续后面的征程。
3. 分析setContentView
setContentView有好几个同名函数,现在只看其中的一个就可以了。代码如下所示:
**Activity.java**
~~~
public void setContentView(View view) {
//getWindow返回的是什么呢?一起来看看。
getWindow().setContentView(view);
}
public Window getWindow() {
returnmWindow; //返回一个类型为Window的mWindow,它是什么?
}
~~~
上面出现了两个和UI有关系的类:View和Window[①]。来看SDK文档是怎么描述这两个类的。这里先给出原文描述,然后进行对应翻译:
- Window:abstract base class for a top-levelwindow look and behavior policy. An instance of this class should be used asthe top-level view added to the window manager. It provides standard UIpolicies such as a background, title area, default key processing, etc.
中文的意思是:Window是一个抽象基类,用于控制顶层窗口的外观和行为。做为顶层窗口它有什么特殊的职能呢?即绘制背景和标题栏、默认的按键处理等。
这里面有一句比较关键的话:它将做为一个顶层的view加入到Window Manager中。
- View:This class represents the basicbuilding block for user interface components. A View occupies a rectangulararea on the screen and is responsible for drawing and event handling.
View的概念就比较简单了,它是一个基本的UI单元,占据屏幕的一块矩形区域,可用于绘制,并能处理事件。
从上面的View和Window的描述,再加上setContentView的代码,我们能想象一下这三者的关系,如图8-2所示:
:-: ![](http://img.blog.csdn.net/20150802162126884?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图8-2 Window/View的假想关系图
根据上面的介绍,大家可能会产生两个疑问:
- Window是一个抽象类,它实际的对象到底是什么类型?
- Window Manager究竟是什么?
如果能有这样的疑问,就说明我们非常细心了。下面试来解决这两个问题。
(1)Activity的Window
据上文讲解可知,Window是一个抽象类。它实际的对象到底属于什么类型?先回到Activity创建的地方去看看。下面正是创建Activity时的代码,可当时没有深入地分析。
~~~
activity = mInstrumentation.newActivity(
cl,component.getClassName(), r.intent);
~~~
代码中调用了Instrumentation的newActivity,再去那里看看。
**Instrumentation.java**
~~~
public Activity newActivity(Class>clazz, Context context,
IBinder token, Application application, Intent intent,
ActivityInfo info, CharSequencetitle, Activity parent,
String id,Object lastNonConfigurationInstance)
throws InstantiationException, IllegalAccessException{
Activity activity = (Activity)clazz.newInstance();
ActivityThread aThread = null;
//关键函数attach!!
activity.attach(context, aThread, this, token, application, intent,
info, title,parent, id, lastNonConfigurationInstance,
new Configuration());
return activity;
}
~~~
看到关键函数attach了吧?Window的真相马上就要揭晓了,让我们用咆哮体②来表达内心的激动之情吧!!!!
**Activity.java**
~~~
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) {
......
//利用PolicyManager来创建Window对象
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
......
//创建WindowManager对象
mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
if(mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
//保存这个WindowManager对象
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
~~~
此刻又有一点失望吧?这里冒出了个PolicyManager类,Window是由它的makeNewWindow函数所创建,因此还必须再去看看这个PolicyManager。
(2)水面下的冰山——PolicyManager
PolicyManager定义于PolicyManager.java文件,该文件在一个非常独立的目录下,现将其单独列出来:
- frameworks/policies/base/phone/com/android/internal/policy/impl
* * * * *
**注意**,上面路径中的灰色目录phone是针对智能手机这种小屏幕的;另外还有一个平级的目录叫mid,是针对Mid设备的。mid目录的代码比较少,可能目前还没有开发完毕。
* * * * *
下面来看这个PolicyManager,它比较简单。
**PolicyManager.java**
~~~
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static{
//
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
//创建Policy对象
sPolicy = (IPolicy)policyClass.newInstance();
}catch (ClassNotFoundException ex) {
......
}
private PolicyManager() {}
//通过Policy对象的makeNewWindow创建一个Window
publicstatic Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
......
}
~~~
这里有一个单例的sPolicy对象,它是Policy类型,请看它的定义。
(3)真正的Window
Policy类型的定义代码如下所示:
**Policy.java**
~~~
public class Policy implements IPolicy {
private static final String TAG = "PhonePolicy";
private static final String[] preload_classes = {
"com.android.internal.policy.impl.PhoneLayoutInflater",
"com.android.internal.policy.impl.PhoneWindow",
"com.android.internal.policy.impl.PhoneWindow$1",
"com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback",
"com.android.internal.policy.impl.PhoneWindow$DecorView",
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
};
static{
//加载所有的类
for (String s : preload_classes) {
try {
Class.forName(s);
} catch (ClassNotFoundException ex) {
......
}
}
}
public PhoneWindow makeNewWindow(Contextcontext) {
//makeNewWindow返回的是PhoneWindow对象
return new PhoneWindow(context);
}
......
}
~~~
至此,终于知道了代码:
~~~
mWindow = PolicyManager.makeNewWindow(this);
~~~
返回的Window,原来是一个PhoneWindow对象。它的定义在PhoneWindow.java中。
mWindow的真实身份搞清楚了,还剩下个WindowManager。现在就来揭示其真面目。
(4)真正的WindowManager
先看WindowManager创建的代码,如下所示:
**Activity.java**
~~~
......//创建mWindow对象
//调用mWindow的setWindowManager函数
mWindow.setWindowManager(null, mToken,mComponent.flattenToString());
.....
~~~
上面的函数设置了PhoneWindow的WindowManager,不过第一个参数是null,这是什么意思?在回答此问题之前,先来看PhoneWindow的定义,它是从Window类派生。
**PhoneWindow.java::PhoneWindow定义**
~~~
public class PhoneWindow extends Windowimplements MenuBuilder.Callback
~~~
前面调用的setWindowManager函数,其实是由PhoneWindow的父类Window类来实现的,来看其代码,如下所示:
**Window.java**
~~~
public void setWindowManager(WindowManagerwm,IBinder appToken, String appName) { //注意,传入的wm值为null
mAppToken = appToken;
mAppName = appName;
if(wm == null) {
//如果wm为空的话,则创建WindowManagerImpl对象
wm = WindowManagerImpl.getDefault();
}
//mWindowManager是一个LocalWindowManager
mWindowManager = new LocalWindowManager(wm);
}
~~~
LocalWindowManager是在Window中定义的内部类,请看它的构造函数,其定义如下所示:
**Window.java::LocalWindowManager定义**
~~~
private class LocalWindowManager implementsWindowManager {
LocalWindowManager(WindowManager wm) {
mWindowManager = wm;//还好,只是简单地保存了传入的wm参数
mDefaultDisplay = mContext.getResources().getDefaultDisplay(
mWindowManager.getDefaultDisplay());
}
......
~~~
如上面代码所示,LocalWindowManager将保存一个WindowManager类型的对象,这个对象的实际类型是WindowManagerImpl。而WindowManagerImpl又是什么呢?来看它的代码,如下所示:
**WindowManagerImpl.java**
~~~
public class WindowManagerImpl implementsWindowManager {
......
public static WindowManagerImpl getDefault()
{
return mWindowManager; //返回的就是WindowManagerImpl对象
}
private static WindowManagerImpl mWindowManager= new WindowManagerImpl();
}
~~~
看到这里,是否有点头晕眼花?很多朋友读我的一篇与此内容相关的博文后,普遍也有如此反应。对此,试配制了一剂治晕药方,如图8-3所示:
:-: ![](http://img.blog.csdn.net/20150802162145510?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图8-3 Window和WindowManger的家族图谱
根据上图,可得出以下结论:
- Activity的mWindow成员变量其真实类型是PhoneWindow,而mWindowManager成员变量的真实类型是LocalWindowManager。
- LocalWindowManager和WindowManagerImpl都实现了WindowManager接口。这里采用的是Proxy模式,表明LocalWindowManager将把它的工作委托WindowManagerImpl来完成。
(5)setContentView的总结
了解了上述知识后,重新回到setContentView函数。这次希望能分析得更深入些。
**Activity.java**
~~~
public void setContentView(View view) {
getWindow().setContentView(view);//getWindow返回的是PhoneWindow
}
~~~
一起来看PhoneWindow的setContentView函数,代码如下所示:
**PhoneWindow**
~~~
public void setContentView(View view) {
//调用另一个setContentView
setContentView(view,
new ViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));
}
public void setContentView(View view,ViewGroup.LayoutParams params) {
//mContentParent为ViewGroup类型,它的初值为null
if(mContentParent == null) {
installDecor();
}else {
mContentParent.removeAllViews();
}
//把view加入到ViewGroup中
mContentParent.addView(view, params);
......
}
~~~
mContentParent是一个ViewGroup类型,它从View中派生,所以也是一个UI单元。从它名字中“Group”所表达的意思分析,它还可以包含其他的View元素。这又是什么意思呢?
- 也就是说,在绘制一个ViewGroup时,它不仅需要把自己的样子画出来,还需要把它包含的View元素的样子也画出来。读者可将它想象成一个容器,容器中的元素就是View。
这里采用的是23种设计模式中的Composite模式,它是UI编程中常用的模式之一。
再来看installDecor函数,其代码如下所示:
**PhoneWindow.java**
~~~
private void installDecor() {
if (mDecor == null) {
//创建mDecor,它为DecorView类型,从FrameLayout派生
mDecor= generateDecor();
......
}
if(mContentParent == null) {
//得到这个mContentParent
mContentParent = generateLayout(mDecor);
//创建标题栏
mTitleView= (TextView)findViewById(com.android.internal.R.id.title);
......
}
~~~
generateLayout函数的输入参数为mDecor,输出为mContentParent,代码如下所示:
**PhoneWindow**
~~~
protected ViewGroup generateLayout(DecorViewdecor){
......
intlayoutResource;
intfeatures = getLocalFeatures();
if((features & ((1 << FEATURE_LEFT_ICON) |(1 <
';