Android 6.0 应用启动全流程源码分析

时间:2022-12-15 04:33:58

今天是国庆,首先祝大家国庆快乐!漫漫国庆长假,然而我却只能宅宿舍,但时间总不能这样白白浪费了,这样的时候,没出去浪,那么,就总结一下前段时间通过Android源码分析了一遍Activity的启动流程吧。好了,废话不多说,直接进入主题。PS:以下分析是针对API 23。

前奏分析

首先,做过Android开发的肯定知道如何启动一个Activity吧,没错,就一个startActivity方法。那么显然,我们找到分析的入口了。那么,问题来了,这个App一开始的第一个Activity是如何被系统启动起来的呢?也是通过普通的startActivity方法吗?答案是肯定的。其实大家不必奇怪,你只需要把系统的桌面当成是一个Activity即可。那么这样一来,在一个Activity里面调用startActivity方法大家很熟悉了吧。

深入主题

通过上面我们知道我们App启动流程的分析的入口是startActivity,那这个startActivity从哪里调呢?它是在LauncherActivity这个类中被调用的,代码如下。

    @Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = intentForPosition(position);
startActivity(intent);
}

这个onListItemClick方法有没有觉得很熟悉,如果你把桌面的那一排排图标理解成一个个按钮,是不是突然明白为啥会在这里调用了?好,找到入口我们继续。

接下来我们看看startActivity是如何实现的。

    @Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}

其实源码中是重写了几个startActivity方法,但最后都会调用到startActivityForResult这个方法,那么好,点进去往下看。

    @Override
public void startActivityForResult(
String who, Intent intent, int requestCode, @Nullable Bundle options) {
Uri referrer = onProvideReferrer();
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, who, requestCode,
ar.getResultCode(), ar.getResultData());
}
cancelInputsAndStartExitTransition(options);
}

嗯,startActivityForResult这个方法的实现就有点长了。没关系,我们看重点,我们看源码只需要把握总体思想,对于不是从事系统层开发的同学们,不要总是拘泥与细节。那么重点在哪呢?看这个方法,execStartActivity。这个方法是通过Instrumentation这个类去调用的,这个类需要熟悉一下,因为,Activity中的很多生命周期的调用,都需要通过这个类。那么接着往下。进入Instrumentation的execStartActivity方法。

    public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {

//这里省略部分代码

try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}

由于这个方法的源码比较长,那么只贴出了重点,在哪,很显然又出现了
startActivity这个方法。那么问题来了,这个startActivity方法的调用者ActivityManagerNative.getDefault()是什么呢?这里不贴源码, 我直接说明一下。通过源码分析getDefault 的定义会发现getDefault 方法返回的是一个IActivityManager类型的对象,但IActivityManager是一个接口,真实返回的其实是一个ActivityManagerService。该类继承自ActivityManagerNative,而ActivityManagerNative 则实现了IActivityManager 接口,同时还继承了Binder ,很显然ActivityManagerService 是一个Binder 对象。了解AIDL的同学一定很熟悉这个类了。这个ActivityManagerService 是系统服务来的,其位于system_Server 这个进程中,很显然,这里是使用了IPC了。那么我们接着进入ActivityManagerServicestartActivity 方法。

进入该方法之后又会调用该类的startActivityAsUser 方法。

    @Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, options, false, userId, null, null);
}

相信大家都看到了这里面有一行注释Switch to user app stacks here. 大概意思是要切换到用户的APP栈了。接着我门依然关注重点,看startActivityMayWait 这个方法。是通过ActivityStackSupervisor 去调用的。那么这个里面的方法也很长,就不贴具体代码了,感兴趣的同学可以自行查看。这里面还会接着调用到startActivityLocked ,然后这里面又会调用到startActivityUncheckedLocked ,接着调用到 ActivityStackresumeTopActivitiesLocked方法。然后是resumeTopActivityInnerLocked ,这方法很复杂…一定不要在里面迷路,这里直接看到里面有一句是通过ActivityStackSupervisor 去调用的startSpecificActivityLocked。嗯,进入这个方法后,重点来了。先看源码。

    void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);

r.task.stack.setLaunchTime(r);

if (app != null && app.thread != null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}

// If a dead object exception was thrown -- fall through to
// restart the application.
}

mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);

从源码中可以看出,这里对 ProcessRecord进行了判断,ProcessRecord就是响应的进程记录,如果存在相应的进程,就启动相应的 Activity, 否则将创建进程。创建进程的话则是通过AMS调用startProcessLocked 方法。在里面会调用Process.start,并且指定了 ActivityThread作为进程的入口,进程启动后,将调用 ActivityThreadmain 方法。相信大家对main 方法是很熟悉的。那么接下来看一下Process.start 这个方法。

    public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}

可以看到,在Process.start 方法中,实际调用的是 startViaZygote方法,在这个方法里通过 openZygoteSocketIfNeeded打开 Zygote 的 socket,并通过 zygoteSendArgsAndGetResult进行交互。接着继续往下就会通过JNI调用到系统底层的内核代码了。这里不再展开分析,感兴趣的同学可以自行查找相关资料。这里大概说一下:其实在Android里面的每一个App进程都是通过Zygote 进程fork出来的。而且Zygote是属于系统进程来的,所以这里需要建立socket与其进行交互,为App启动创建新的进程。

通过上面的分析我们知道,接下来新的进程创建了将会进入ActivityThreadmain 方法,代码如下:

    public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();

// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());

AndroidKeyStoreProvider.install();

// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);

Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}

嗯,代码比较长,老规矩,看重点。这里首先调用Looper.prepareMainLooper 去创建一个主线程的Looper,这个主线程的Looper是很重要的,系统对于App的很多管理都需要通过这个Looper去将消息传递到ActivityThreadH 里面去执行。这里面当然包括了Acticity的工作流程。接着,为整个应用创建了一个ActivityThread 对象。attach 方法之后, 通过thread.getHandler方法去获取到主线程的Handler,其实就是上面说的那个H ,这里具体咋样可以自行查阅源码。然后就调用Looper.loop() 让主线程的Looper进入轮询状态,等待消息过来处理。至此UI线程启动完毕。

看到这里,也许大家懵了,不对啊,这就完事了?貌似少了点什么啊?Activity的启动去哪儿了?一开始我也懵逼了。后来经过代码分析,发现原来疏漏了一些代码细节。让我们回到attach 这个方法,这里调用的时候是传入了false 的。那么我们看看其内部。

    private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
// Watch for getting close to heap limit.
BinderInternal.addGcWatcher(new Runnable() {
@Override public void run() {
if (!mSomeActivitiesChanged) {
return;
}
Runtime runtime = Runtime.getRuntime();
long dalvikMax = runtime.maxMemory();
long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
if (dalvikUsed > ((3*dalvikMax)/4)) {
if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
+ " total=" + (runtime.totalMemory()/1024)
+ " used=" + (dalvikUsed/1024));
mSomeActivitiesChanged = false;
try {
mgr.releaseSomeActivities(mAppThread);
} catch (RemoteException e) {
}
}
}
});
} else {

//这里省略无关代码

}
}

上面已经说过了,调用这个attach 的时候传入的是false ,那么接下来肯定会通过ActivityManagerService(AMS) 调用到attachApplication。里面会调用到attachApplicationLocked,然后就是在这个方法里面,又通过ApplicationThread 调用了bindApplication 在这里面。

    public final void bindApplication(String packageName, ApplicationInfo info,
List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo,
Bundle testArgs, IInstrumentationWatcher testWatcher,
IUiAutomationConnection uiAutomationConnection, int debugMode,
boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
Bundle coreSettings) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeString(packageName);
info.writeToParcel(data, 0);
data.writeTypedList(providers);
if (testName == null) {
data.writeInt(0);
} else {
data.writeInt(1);
testName.writeToParcel(data, 0);
}
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
data.writeBundle(testArgs);
data.writeStrongInterface(testWatcher);
data.writeStrongInterface(uiAutomationConnection);
data.writeInt(debugMode);
data.writeInt(openGlTrace ? 1 : 0);
data.writeInt(restrictedBackupMode ? 1 : 0);
data.writeInt(persistent ? 1 : 0);
config.writeToParcel(data, 0);
compatInfo.writeToParcel(data, 0);
data.writeMap(services);
data.writeBundle(coreSettings);
mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}

进去这个方法后大家别懵逼,要知道ApplicationThread 其实是作为AMS在IPC过程中的服务端来的。这里大家如果熟悉AIDL的用法的话,应该很容易看得懂。当调用到mRemote.transact 的时候,其实是进入到了ApplicationThreadonTransact ,然后匹配到带过去的参数BIND_APPLICATION_TRANSACTION ,就会调用到下面的代码:

        case BIND_APPLICATION_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
String packageName = data.readString();
ApplicationInfo info =
ApplicationInfo.CREATOR.createFromParcel(data);
List<ProviderInfo> providers =
data.createTypedArrayList(ProviderInfo.CREATOR);
ComponentName testName = (data.readInt() != 0)
? new ComponentName(data) : null;
ProfilerInfo profilerInfo = data.readInt() != 0
? ProfilerInfo.CREATOR.createFromParcel(data) : null;
Bundle testArgs = data.readBundle();
IBinder binder = data.readStrongBinder();
IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder);
binder = data.readStrongBinder();
IUiAutomationConnection uiAutomationConnection =
IUiAutomationConnection.Stub.asInterface(binder);
int testMode = data.readInt();
boolean openGlTrace = data.readInt() != 0;
boolean restrictedBackupMode = (data.readInt() != 0);
boolean persistent = (data.readInt() != 0);
Configuration config = Configuration.CREATOR.createFromParcel(data);
CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
HashMap<String, IBinder> services = data.readHashMap(null);
Bundle coreSettings = data.readBundle();
bindApplication(packageName, info, providers, testName, profilerInfo, testArgs,
testWatcher, uiAutomationConnection, testMode, openGlTrace,
restrictedBackupMode, persistent, config, compatInfo, services, coreSettings);
return true;

我们直接看到最下面,又调用了bindApplication ,注意,这个 里面的bindApplication 方法,是直接在ApplicationThread 里面调用的了,不是刚刚那个AMS里面调用的,在AMS里面调用是由于是跨进程调用,会进入到带方法里面,而在ApplicationThread里面调用的话,才是真正的bindApplication的的实现。里面代码就不贴了,最好会调用到sendMessage(H.BIND_APPLICATION, data) 。嗯,这条消息一发就进入到了之前提到过的那个H 里面去处理这条消息,注意,那个H 是运行在UI线程的。然后接下来调用到handleBindApplication,然后就是在handleBindApplication 这个方法里面,为这个应用创建了唯一的Application对象,和唯一的Instrumentation 对象,还记得前面说过吗,通过这个类,系统就可以管理Activity的生命周期了。注意,每一个App只有一个该对象,并且每一个Activity里面都有它的引用。后面也调用了ApplicationonCreate方法。到此,Application创建了。我们继续回到前面的attach 方法,往下走。好戏还在后面。

后面就会通过ActivityStackSupervisor 调用attachApplicationLocked,并且传入了一个ProcessRecord 对象。里面会调用realStartActivityLocked 这个方法。然后这个方法里面又会通过ApplicationThread 调用scheduleLaunchActivity。注意了,这里又是一次IPC的过程,大致过程和上面创建Application 的时候一样,最终还是会调用到scheduleLaunchActivity 的真正实现。完了还是老样子,这个方法里面最好会发送一条消息到H 里面去处理:sendMessage(H.LAUNCH_ACTIVITY, r) 。完了里面会进入到如下分支:

                case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;

嗯,调用了handleLaunchActivity ,进去看看:

        // If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;

if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}

// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);

if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);

// 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);

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.
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
// We need to keep around the original state, in case
// we need to be created again. But we only do this
// for pre-Honeycomb apps, which always save their state
// when pausing, so we can not have them save their state
// when restarting from a paused state. For HC and later,
// we want to (and can) let the state be saved as the normal
// part of stopping the activity.
if (r.isPreHoneycomb()) {
r.state = oldState;
}
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPause()");
}

} catch (SuperNotCalledException e) {
throw e;

} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to pause activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
r.paused = true;
}
} else {
// If there was an error, for any reason, tell the activity
// manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
// Ignore
}
}
}

代码有点长,但是关键出现了。看Activity a = performLaunchActivity(r, customIntent) 呐,经历了千山万水,我们熟悉的Activity 终于被创建了。是通过调用performLaunchActivity 方法去创建的。这个方法大家需要熟悉一下。里面涉及到了很多关键操作,例如,里面调用了ActivityonCreateonStart 方法。然后继续往下看,如果Activity 被创建成功的话,会调用handleResumeActivity ,然后会调用到performResumeActivity,然后会调用到ActivityperformResume方法。

    final void performResume() {
performRestart();

mFragments.execPendingActions();

mLastNonConfigurationInstances = null;

mCalled = false;
// mResumed is set by the instrumentation
mInstrumentation.callActivityOnResume(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onResume()");
}

// invisible activities must be finished before onResume() completes
if (!mVisibleFromClient && !mFinished) {
Log.w(TAG, "An activity without a UI must call finish() before onResume() completes");
if (getApplicationInfo().targetSdkVersion
> android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
throw new IllegalStateException(
"Activity " + mComponent.toShortString() +
" did not call finish() prior to onResume() completing");
}
}

// Now really resume, and install the current status bar and menu.
mCalled = false;

mFragments.dispatchResume();
mFragments.execPendingActions();

onPostResume();
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onPostResume()");
}
}

我们可以看到,这个方法一开头调用了performRestart 这个方法一进去就会判断该Activity 是否有调用过onStop ,如果没有调用,那么整个方法相当于没有调用,如果调用了,那么会依次调用onRestartonStart 方法。呐,之前都是看看谷歌画的Activity 生命周期图,今天总算看到了源码真面目了吧,为啥什么周期这么走总算搞懂了吧。嗯,过了performRestart方法,会通过 Instrumentation 去调用callActivityOnResume :

    public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume();

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());
}
}
}
}

嗯,终于看到了activity.onResume()了。至此,整个Activity 也启动了,那么应用也算是开启成功了。好了,大功告成。

总结

相信很多同学对于整个App得到启动流程都是看得云里雾里的,特别是多次和AMS之间IPC的过程,那么这里就直接献上某位大神的关于ApplicationThread 和AMS之间IPC得到 结构简图,感谢!
Android 6.0 应用启动全流程源码分析

该文章一来是作为自己的学习总结和记录,二来是分享给有需要的开发人员,如有不正之处,望不吝指出。深表感激!