Android APP启动过程分析(1) —— Activity、Window、DecorView

前言:本篇文章通过对APP启动过程(从点击图标—>Activity可见)的分析,来搞懂这一过程遇到的几个重要的知识点,以解心中的疑惑,同时通过对整个过程的分析,让孤立的知识点串联起来,有个更好的理解和掌握。

程序入口——ActivityThread.main()

Android程序的入口是ActivityThread类中的main()方法,就是当我们点击一个APP图标时,系统最开始执行的地方。以后当被人问到这个问题时,就不要回答“Activity的onCreate()方法”了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class ActivityThread {
......
public static void main(String[] args) {
.....
Looper.prepareMainLooper(); //1
ActivityThread thread = new ActivityThread(); //2
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler(); //3
}
......
}
......
final ApplicationThread mAppThread = new ApplicationThread(); //4
final Looper mLooper = Looper.myLooper();
final H mH = new H()
}

执行的操作

1、调用prepareMainLooper()在主线程中创建一个消息队列。
2、创建ActivityThread对象。
3、创建Handler对象。
4、创建ApplicationThread对象

过程讲解

1、ActivityThread:每个应用程序都对应着一个ActivityThread实例,其中的main()方法是程序的入口,通过一系列的初始化操作,它最终的操作是通过调用ActivityThread.handleMessage()方法接收系统传来的消息,创建Activity。
Q:消息是由谁传递过来的?请看2.

2、ApplicationThread:每个应用程序对应着一个ApplicationThread对象,它是一个Binder对象,Binder是用来进行IPC(进程间的通信)操作的。所以它的作用是接收AMS传来的远程消息,例如AMS发送star某个Activity,然后ApplicationThread再通过Handler(线程间的通信)传递给ActivityThreadActivityThread调用handleMessage()方法来处理这个消息,进行创建Activity的操作。由此可见,ApplicationThreadActivityThread与AMS之间信息传递的桥梁。
Q:AMS又是啥?请看3.

3、AMS(ActivityManagerService):AMS是Android中的一个核心服务,主要负责系统中四大组建(有的文章说不包括BroadcastReceiver)的启动、切换、调度等工作。所以当我们点击打开一个APP时,AMS会向ApplicationThread发送请求开启一个Activity,然后由ApplicationThread传递给ActivityThread来进行具体操作,上面我们已经提到。

现在我们已经知道了消息传递的大体流程,当ActivityThread收到AMS传来的启动一个Activity的消息后,就会调用handleMessage()方法创建指定的Activity,具体如何做的呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final class ActivityThread {
......
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
......
handleLaunchActivity(r, null);
......
break;
}
}
}
}

从代码可以看出,它调用了handleLaunchActivity()方法,那我们进入看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public final class ActivityThread {
......
Activity a = performLaunchActivity(r, customIntent);
......
if (a != null) {
......
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
......
}
.......
}
}

该方法又调用了performLaunchActivity()方法来返回一个Activity,想必Activity应该就是在这个函数中创建完成的了。此方法执行后,又调用了handleResumeActivity()方法,我们先看performLaunchActivity()做了什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final class ActivityThread {
......
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
Activity activity = null;
......
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
......
return activity;
}
}

performLaunchActivity()

performLaunchActivity()做了哪些事

1、从ActivityClientRecord中得到接下来要启动Activity的ComponentName信息。

2、从ActivityClientRecord中得到接下来要启动Activity的信息
通过Instrumentation.newActivity方法,利用LoadedApk的类加载器尝试创建Activity的对象。

3、通过LoadedApk.makeApplication方法尝试创建Application对象,在这个方法里会通过`Class.newInstance()方法创建Application的对象,并且它的onCreate方法也是在此时被调用的。

4、为Activity创建ContextImpl的对象,调用Activity.attach()方法。

5、调用Activity.setTheme方法,如果Activity申明时指定了theme的话。

6、调用Activity.onCreate()方法,然后是onStart()方法,如果之前有保存的状态那么还会调用onRestoreInstanceState()方法,最后调用onPostCreate()方法,至此整个launch过程算是结束了。

进入onCreate()方法

下面终于进入到onCreate()方法了,首要执行的就是setContentView(R.layout.main),这个方法的目的是设置我们在xml文件中定义的布局,我们进入这个方法看看。

1
2
3
4
5
6
7
8
9
10
11
12
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
……
private Window mWindow;
mWindow = new PhoneWindow(this, window);
public Window getWindow() {
return mWindow;
}

该方法中调用了mWindow()setContentView()方法,因为我们可以从代码中看出getWindow()返回的是mWindowmWindowWindow类型的,而Window是一个抽象类,它的实现类是PhoneWindow。所以, getWindow().setContentView()方法就是调用的PhoneWindowsetContentView()方法,然后我们进入看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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.
private ViewGroup mContentParent;
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}

首先,判断mContentParent是否为空,为空则执行installDecor()mContentParent是什么?从代码中的注释可以看到说“mContentParent是mDecor本身或者是mDecor的一个子元素
”,mDcorDecorView的实例,大家可能还不明白,所以要先搞清DecorView是什么,然后我们继续往下看,看看installDecor()方法做了什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(); // 1
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); // 2
...
}
}
}

从代码可以看出,它调用了generateDecor()方法:

1
2
3
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}

好了,可以看出,这里实例化出了一个DecorView,那DecorView是什么呢?

1
2
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
}

由代码可见,DecorView继承自FrameLayout,而FrameLayout继承自ViewGroup,所以DecorView是一个ViewGroup。其实,它是ViewTree的最顶层的一个View,它是一个FrameLayout布局,代表了整个应用的界面,由TitlView和ContentView两个子元素,而ContentView则是上面提到的mContentParent,那为什么刚才注释里说“mContentParent是mDecor本身或者是mDecor的一个子元素
”呢?我们进入installDecor()中注释的2号代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 从主题文件中获取样式信息
TypedArray a = getWindowStyle();
...
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
if(...){
...
}
// Inflate the window decor.
// 加载窗口布局
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if(...){
...
}
View in = mLayoutInflater.inflate(layoutResource, null); //加载layoutResource
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //往DecorView中添加子View,即mContentParent
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); // 这里获取的就是mContentParent
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);
}
}
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
registerSwipeCallbacks();
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
...
return contentParent;
}

由以上代码可以看出:首先根据设置的主题样式来设置DecorView的风格,比如说有没有titlebar之类的,接着为DecorView添加子View,而这里的子View则是上面提到的mContentParent,如果上面设置了FEATURE_NO_ACTIONBAR,那么DecorView就只有mContentParent一个子View,这也解释了上面的疑问:mContentParent是DecorView本身或者是DecorView的一个子元素。

DecorView

handleResumeActivity()

performLaunchActivity()执行完了之后,接着会执行handleResumeActivity()方法,看看源码会执行哪些操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
//...
ActivityClientRecord r = performResumeActivity(token, clearHide); // 这里会调用到onResume()方法
if (r != null) {
final Activity a = r.activity;
//...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow(); // 获得window对象
View decor = r.window.getDecorView(); // 获得DecorView对象
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager(); // 获得windowManager对象
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); // 调用addView方法
}
//...
}
}
}

该方法内部会先获得与该Activity关联的几个对象:
1、WIndow对象
2、DecorView对象
3、WindowManager对象,它的实现类是WIndowManagerImpl
接着会调用WIndowManagerImpladdView()方法:

1
2
3
4
5
6
7
8
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
...
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
}

这里面又调用了mGlobaladdView()方法,mGlobalWindowManagerGlobal的一个实例,我们进入该方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
...
root = new ViewRootImpl(view.getContext(), display); // 1
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); // 2
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}

1、首先实例化一个ViewRootImpl类。

2、然后调用ViewRootImplsetView()方法,把DecorView作为参数传进去。在这里,ViewRootImplWMSDecorView之间沟通的一个桥梁。

3、在这个方法内部,会通过跨进程的方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终添加到Window上。

4、最后通过WMS调用ViewRootImpl.performTraverals方法开始View的测量、布局、绘制流程。这个下一篇再讲解。


通过前面的讲解,我们可以如此总结:

Android应用程序窗口 = Activity + PhoneWindow + DecorView

DecorView = TitleView + ContentView(layout.xml文件)

画图表示如下(每一层的大小都是一样的,是覆盖关系,这里是为了方便表示出每一层):

Android应用程序窗口模型

参考:
1、http://www.voidcn.com/blog/u012827296/article/p-4991222.html

2、http://www.jianshu.com/p/687010ccad66

3、http://www.jianshu.com/p/98c98d1ae3c3