注:本文分析的源码版本为Android 27
如有转载,请标明出处
(更新时间:2024-11-22)
一、应用启动
我们知道,普通java应用在运行时,都需要一个名为main的静态方法作为程序的入口点。
我们尝试在Activity的onCreate方法中,打印函数的调用栈:
java.lang.Exception
at com.pujh.led.MainActivity.onCreate(MainActivity.java:20)
at android.app.Activity.performCreate(Activity.java:7038)
at android.app.Activity.performCreate(Activity.java:7029)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
可以发现ActivityThread的main方法,它正是Android应用程序启动时的入口点。
public final class ActivityThread {
// 省略部分代码
public static void main(String[] args) {
// 省略部分代码
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
在main方法里做了以下操作:
- 调用
Looper.prepareMainLooper()方法,它会创建一个与当前线程(主线程)相关联的Looper对象。 - 创建一个
ActivityThread对象,并调用其attach()方法。 - 调用
Looper.loop()方法,让当前线程(主线程)去处理Looper内部MessageQueue中收到的消息。
接下来,我们开始分析ActivityThread的attach方法:
public final class ActivityThread {
final ApplicationThread mAppThread = new ApplicationThread();
// 省略部分代码
private void attach(boolean system) {
// 省略部分代码
if (!system) {
// 省略部分代码
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
// 省略后续代码
}
}
}
在attach方法里,首先通过ActivityManager.getService()方法,获取到AMS进程中的IActivityManager对象。
@SystemService(Context.ACTIVITY_SERVICE)
public class ActivityManager {
@UnsupportedAppUsage
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
@UnsupportedAppUsage
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
}
然后调用IActivityManager对象的attachApplication方法,并传入ApplicationThread实例。
其中ApplicationThread继承至IApplicationThread.Stub,即Binder进程间通信的本地实现类。它有很多与Activity生命周期相关的方法,大都以scheduleXXX命名:
我们可以将ApplicationThread理解成一个Callback,当AMS进程需要向App进程发送消息时,就会跨进程调用ApplicationThread对应的方法,从而通知到App。
至此,我们可以总结一下:
ActivityThread的main方法,是应用程序启动的入口。- Activity生命周期是由系统底层控制,通过
Binder机制,回调到ApplicationThread的scheduleXXX方法中。 ActivityThread和ApplicationThread针对每个应用,都只有一个实例。
二、Activity生命周期
这里我们需要知道:
Activity所有的生命周期,都是由
AMS(Activity Manager Service)统一管理,通过ApplicationThreadBinder对象,跨进程回调到一系列scheduleXXX方法,从而回调通知到ActivityThread。
AMS首先会调用到ApplicationThread的scheduleLaunchActivity方法,该方法会创建并启动Activity。
private class ApplicationThread extends IApplicationThread.Stub {
@Override
public final void scheduleLaunchActivity(......) {
ActivityClientRecord r = new ActivityClientRecord();
// 省略初始化ActivityClientRecord对象相关代码
sendMessage(H.LAUNCH_ACTIVITY, r);
}
}
在scheduleLaunchActivity方法中,创建并初始化了一个ActivityClientRecord对象,
ActivityClientRecord:用于记录与Activity相关的数据。
然后通过Handler,将该对象发送给到App主线程,最终调用到ActivityThread的handleLaunchActivity方法。
public final class ActivityThread {
// 省略部分代码
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// 省略部分代码
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
// 省略部分代码
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out paused, because it
// needs to be visible but isn't in the foreground. We accomplish this by going
// through the normal startup (because activities expect to go through onResume()
// the first time they run, before their window is displayed), and then pausing it.
// However, in this case we do -not- need to do the full pause cycle (of freezing
// and such) because the activity manager assumes it can just retain the current
// state it has.
performPauseActivityIfNeeded(r, reason);
// 省略部分代码
}
}
}
}
该方法中,调用了performLaunchActivity和handleResumeActivity,这里可以大胆猜测:
handleResumeActivity里应该调用了Activity的onResume方法performLaunchActivity自然而然调用了Activity的onCreate和onStart方法
带着这个猜测,开始分析performLaunchActivity方法。
public final class ActivityThread {
// 省略部分代码
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 省略部分代码
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
// 省略部分代码
} catch (Exception e) { /* 省略部分代码 */ }
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
// 省略部分代码
if (activity != null) {
// 省略部分代码
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);
// 省略部分代码
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
// 省略部分代码
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
// 省略部分代码
}
}
r.paused = true;
// 保存ActivityClientRecord
mActivities.put(r.token, r);
} catch { /* 省略catch相关代码 */ }
return activity;
}
上述代码主要执行了以下操作:
- 创建Activity对象
- 创建Application对象
- 调用activity的
attach方法 - 调用Activity的3个生命周期方法:
- 调用Instrumentation的
callActivityOnCreate方法->activity.onCreate() - 调用activity的
performStart方法->activity.onStart() - 调用Instrumentation的
callActivityOnPostCreate方法->activity.onPostCreate()
- 调用Instrumentation的
这里出现了一个Instrumentation对象,并且Activity和Application的创建、callActivityOnCreate方法,都与该类相关。
我们可以看一下Activity的attach方法。
public class Activity extends .... {
// 省略部分代码
private Window mWindow;
private WindowManager mWindowManager;
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,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
// 省略部分代码
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
// 省略部分代码
mWindowManager = mWindow.getWindowManager();
// 省略部分代码
}
// 省略部分代码
}
该方法主要用于初始化Activity的一些参数,同时会为Activity创建了一个mWindow对象,这也意味着:每个Activity都有各自的Window对象。
总结一下:
在handleLaunchActivity方法里,会回调以下生命周期:
onCreate()->onStart()->onPostCreate()->onResume()
注意:
如果ActivityClientRecord.startsNotResumed = true时,生命周期流程将会变为:
onCreate()->onStart()->onPostCreate()->onResume()->onPause()
三、界面加载
通过上节内容的介绍,我们知道在handleLaunchActivity方法里,会回调Activity的onCreate()生命周期方法。
而一般我们在创建Activity时,会在onCreate()中调用setContentView来设置布局文件。
下面,我们开始分析,我们自定义的布局文件是如何被加载出来的。
3.1、设置布局
首先分析Activity中的setContentView方法的源码。
public class Activity extends .... {
// 省略部分代码
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
// 省略部分代码
}
根据前面的分析可知,这里的getWindow()方法返回的是该Activity所特有的Window对象,在attach方法中,我们为其创建了一个PhoneWindow对象,所以我们直接看PhoneWindow的setContentView方法。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
// 省略部分代码
ViewGroup mContentParent;
@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);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
// 省略部分代码
}
代码还是比较清楚的,如果mContentParent == null,调用installDecor()方法,然后将传入的布局资源,加载到mContentParent中。
所以这里可以肯定:installDecor()方法中,肯定创建了mContentParent对象。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
// 省略部分代码
private DecorView mDecor;
ViewGroup mContentParent;
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
// 省略部分代码
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// 省略后续设置icon、title等代码
}
}
}
嗯,这里确实通过generateLayout方法创建了mContentParent对象,但在创建之前,先创建了一个DecorView对象,并将其作为参数传入generateLayout方法里。
而DecorView,我们只需要知道它继承至FrameLayout,并且是所有View的根View即可。
那我们分析generateLayout做了什么:
public class PhoneWindow extends Window implements MenuBuilder.Callback {
// 省略部分代码
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 省略部分代码:从主题文件中读取内容,并设置对应的Flag
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
//省略部分代码:通过features,为layoutResource设置不同布局资源id
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//省略部分代码:为mDecor设置background、elevation、title、titleColor等数据
mDecor.finishChanging();
return contentParent;
}
}
主要做了以下内容:
- 从主题中获取数据,并应用到当前
Window中。 - 然后根据
Window的features,让mDecor加载不同的基础布局文件。 - 从基础布局文件中,获得id为
com.android.internal.R.id.content的控件并返回。
至此,我们可以简单总结一下:
- 首先,Activity中的
Window对象会创建一个DecorView。 - 然后根据不同的主题,让
Window加载不同的基础布局资源到DecorView中。 - 获取这些布局资源中的
mContentParent,它的id为com.android.internal.R.id.content。 - 最后将自定义的布局资源加载到
mContentParent中。
3.2、渲染布局
在上述setContentView的流程中,已将所有的布局资源都加载完毕,而布局的加载又会涉及到addView方法。
一般来说,调用
addView方法,都会间接调用到requestLayout()和invalidate(true)方法,造成界面重新布局和刷新。
而我们也知道:
Activity在
onCreate时,界面并不会被加载出来。
这里仿佛出现了矛盾,那我们再分析分析,看看为什么调用了addView方法后,界面却没有被加载出来。
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
// 省略部分代码
public void addView(View child, int index, LayoutParams params) {
// 省略部分代码
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
// 省略部分代码
}
public class View implements ... {
// 省略部分代码
public void requestLayout() {
// 省略部分代码
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
// 省略部分代码
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
// 省略部分代码
if (skipInvalidate()) {
return;
}
// 省略后续代码
}
private boolean skipInvalidate() {
return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&
(!(mParent instanceof ViewGroup) ||
!((ViewGroup) mParent).isViewTransitioning(this));
}
// 省略部分代码
}
哦,原来此时DecorView没有父容器,导致setContentView只会加载布局,而不会去重新布局和刷新。
那什么时候,界面才会被加载出来呢?
只要学过Android生命周期的人,都知道:
- 当Activity处于
onCreate时,是不可见的。 - 当Activity处于
onStart时,可见,但不可交互。 - 当Activity处于
onResume时,才是可见可交互的。
那我们看看Activity的performStart方法实现:
public class Activity extends .... {
// 省略部分代码
final void performStart() {
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
mFragments.noteStateNotSaved();
mCalled = false;
mFragments.execPendingActions();
mInstrumentation.callActivityOnStart(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onStart()");
}
mFragments.dispatchStart();
mFragments.reportLoaderStart();
// 省略部分代码
mActivityTransitionState.enterReady(this);
}
这里只要调用了Instrumentation的callActivityOnStart方法,而callActivityOnStart方法内部的实现,只是简单调用了传入的Activity对象的onStart()方法。
emmmmm......好像有点不对啊,performStart方法里好像也没做什么操作啊,难不成界面渲染是在onResume()里?
带着疑惑,我们一起来看看handleResumeActivity方法:
public final class ActivityThread {
// 省略部分代码
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
// 省略部分代码
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
// 省略部分代码
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;
// 省略部分代码
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
// 省略后续代码
}
}
}
}
其中performResumeActivity也没做过多操作,只是调用了Activity的performResume()方法,间接调用到onResume,我们就不过多分析了。
这里比较核心的是,将DecorView添加到ViewManager中,这里间接调用到WindowManagerGlobal的addView方法,它是一个单例对象,注意这里的判定条件,可以看出,一个ActivityClientRecord对象,只会执行一次。
public final class WindowManagerGlobal {
// 省略部分代码
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// 省略部分代码
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// 省略部分代码
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.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
// 省略部分代码
}
首先创建了一个ViewRootImpl对象,然后将ViewRootImpl、DecorView和WindowManager.LayoutParams缓存到数组中。
接下来把DecorView设置到ViewRootImpl中,至此DecorView有了父容器,从而进而执行到ViewRootImpl的requestLayout()方法中,而该方法,会依次调用到scheduleTraversals() -> doTraversal(),最终执行到performTraversals()方法中。
而performTraversals()里,会执行View的绘制流程,包括测量、布局和绘制三部分,这个我们后面单独在View渲染章节来讲。我们只需明白,此时界面就已经被渲染出来了。
3.3、延伸
现在,我们已经知道了,Activity渲染到屏幕,是在onResume()之后才完成的,那为啥说,onStart()是可见但不可交互的呢?
这里就不卖关子了,其实官方所说的"可见",其实有一定的歧义,我认为:
"可见"只是针对于
已被渲染过的Activity,而不是正在创建的Activity。
参照下面这张图来解释一下:
- 1、对于创建的Activity,只是简单的走了生命周期,渲染是在
Resumed时完成的。 - 2、对于已被渲染过的Activity:
- 当它由
Resumed切换到Started状态,界面被部分覆盖,失去焦点,即无法进行交互。 - 当它由
Started切换到Created状态,界面被完全覆盖,即不可见。 - 当它由
Created切换到Started状态,界面再次被部分覆盖,依然获取不到焦点,无法交互。 - 当它由
Started切换到Resumed状态,界面完全显示。
- 当它由
正是因为这样,才会造成以下这个的问题:
Activity创建过程中,在
onCreate()、onStart()、onResume()方法里,都无法获取控件的宽高。
四、总结
本文讲解了Activity启动时,生命周期的调用和界面加载流程,总结起来,整个流程如下: