今天是国庆,首先祝大家国庆快乐!漫漫国庆长假,然而我却只能宅宿舍,但时间总不能这样白白浪费了,这样的时候,没出去浪,那么,就总结一下前段时间通过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了。那么我们接着进入ActivityManagerService
的startActivity
方法。
进入该方法之后又会调用该类的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
,接着调用到 ActivityStack
的 resumeTopActivitiesLocked
方法。然后是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
作为进程的入口,进程启动后,将调用 ActivityThread
的 main
方法。相信大家对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启动创建新的进程。
通过上面的分析我们知道,接下来新的进程创建了将会进入ActivityThread
的 main
方法,代码如下:
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
去将消息传递到ActivityThread
的 H
里面去执行。这里面当然包括了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
的时候,其实是进入到了ApplicationThread
的onTransact
,然后匹配到带过去的参数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里面都有它的引用。后面也调用了Application
的onCreate
方法。到此,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
方法去创建的。这个方法大家需要熟悉一下。里面涉及到了很多关键操作,例如,里面调用了Activity
的onCreate
和 onStart
方法。然后继续往下看,如果Activity
被创建成功的话,会调用handleResumeActivity
,然后会调用到performResumeActivity
,然后会调用到Activity
的performResume
方法。
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
,如果没有调用,那么整个方法相当于没有调用,如果调用了,那么会依次调用onRestart
和onStart
方法。呐,之前都是看看谷歌画的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得到 结构简图,感谢!
该文章一来是作为自己的学习总结和记录,二来是分享给有需要的开发人员,如有不正之处,望不吝指出。深表感激!