Androidプログラムはシステムの中でどのように起動を完成します
一部のプログラムの呼び出しにはそのプログラムエントリがあり、Javaプログラムのエントリがmainメソッドにあることはよく知られています.Androidとしてアプリケーションの起動はActivity Threadクラスによって実行されます.システムでは、このクラスについて次のように説明します.
このクラスは、アプリケーション・プロセスにおけるプライマリ・スレッドの実行、アクティビティのスケジュールおよび実行、ブロードキャスト、およびアクティビティ管理者の要求を管理する他の操作です.
Appを開くと、まずこのクラスのmainメソッドが呼び出されます.つまり、ここでプログラムの起動を制御しています.
ActivityThread#main
mainメソッドで重要な操作:
step 1: Looper.prepareMainLooper():現在のスレッドをlooperとして初期化し、アプリケーション内のmain looperとしてマークします.その後、対応するloop()に従って、受信したメッセージをメッセージキューにポーリングします.
Step 2:現在のActivity Threadオブジェクトをインスタンス化し、attachを呼び出します.メソッドの呼び出しの解析を続行します.
ActivityThread#attach
上記の方法で入力されるパラメータはfalseなのでifの論理を実行します.この中の主な操作はActivity Management.を通じてgetService()はActivityManagerServiceオブジェクトを返し、attachApplicationメソッドを呼び出してApplicationThreadオブジェクトを関連付けます.次に、attachApplicationメソッドにアクセスして分析します.
ActivityManagerService#attachApplication
ActivityManagerService#attachApplicationLocked
上記の方法では、まずProcessRecordを使用してプロセスの情報を記録し、後でProcessRecordをActivity StackSupervisorに関連付け、Activity StackSupervisorですべてのアクティブスタックを実行できるようにしました.
ActivityStackSupervisor#attachApplicationLocked
アクティブスタックを巡回した後、まずメソッドtopRunningActivity Lockedを実行し、次に現在のスレッドが一致するアクティビティを見つけてrealStartActivity Lockedメソッドを実行します.
この方法でActivityを起動するためのいくつかの準備が完了し、scheduleLaunchActivityメソッドが呼び出されます.ここで呼び出されているのはApplicationThreadの方法であることがわかります.
ActivityThread.ApplicationThread#scheduleLaunchActivity
ここでは,Activity状態が変化すると対応するメッセージが送信されるHandlerを呼び出すメッセージ通信メカニズムを見ることができる.また、ApplicationThreadクラスには、異なる起動状態を表し、各状態の変更はHandlerに送信されて処理される多くの異なるscheduleXXXクラスの方法が含まれていることも見られます.
これにより,Application実行中にActivityの動作状態遷移はHandlerメッセージメカニズムによって行われると結論した.このメッセージキューはActivity Threadでmainメソッドでポーリングされます.
このクラスは、アプリケーション・プロセスにおけるプライマリ・スレッドの実行、アクティビティのスケジュールおよび実行、ブロードキャスト、およびアクティビティ管理者の要求を管理する他の操作です.
Appを開くと、まずこのクラスのmainメソッドが呼び出されます.つまり、ここでプログラムの起動を制御しています.
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());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("" );
//step 1:
Looper.prepareMainLooper();
//step 2:
ActivityThread thread = new ActivityThread();
thread.attach(false);
// handler
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");
}
mainメソッドで重要な操作:
step 1: Looper.prepareMainLooper():現在のスレッドをlooperとして初期化し、アプリケーション内のmain looperとしてマークします.その後、対応するloop()に従って、受信したメッセージをメッセージキューにポーリングします.
Step 2:現在のActivity Threadオブジェクトをインスタンス化し、attachを呼び出します.メソッドの呼び出しの解析を続行します.
ActivityThread#attach
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("" ,
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
//step 1: ActivityManagerService ApplicationThread
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
// 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) {
throw e.rethrowFromSystemServer();
}
}
}
});
} else {
// Don't set application object here -- if the system crashes,
// we can't display an alert, we just want to die die die.
android.ddm.DdmHandleAppName.setAppName("system_process",
UserHandle.myUserId());
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate Application():" + e.toString(), e);
}
}
// add dropbox logging to libcore
DropBox.setReporter(new DropBoxReporter());
ViewRootImpl.ConfigChangedCallback configChangedCallback
= (Configuration globalConfig) -> {
synchronized (mResourcesManager) {
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
null /* compat */)) {
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
mResourcesManager.getConfiguration().getLocales());
// This actually changed the resources! Tell everyone about it.
if (mPendingConfiguration == null
|| mPendingConfiguration.isOtherSeqNewer(globalConfig)) {
mPendingConfiguration = globalConfig;
sendMessage(H.CONFIGURATION_CHANGED, globalConfig);
}
}
}
};
//
ViewRootImpl.addConfigCallback(configChangedCallback);
}
上記の方法で入力されるパラメータはfalseなのでifの論理を実行します.この中の主な操作はActivity Management.を通じてgetService()はActivityManagerServiceオブジェクトを返し、attachApplicationメソッドを呼び出してApplicationThreadオブジェクトを関連付けます.次に、attachApplicationメソッドにアクセスして分析します.
ActivityManagerService#attachApplication
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
ActivityManagerService#attachApplicationLocked
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
// Find the application record that is being attached... either via
// the pid if we are running in multiple processes, or just pull the
// next app record if we are emulating process with anonymous threads.
// step 1:
ProcessRecord app;
long startTime = SystemClock.uptimeMillis();
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
} else {
app = null;
}
// pid , ApplicationThread
if (app == null) {
Slog.w(TAG, "No pending application record for pid " + pid
+ " (IApplicationThread " + thread + "); dropping process");
EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
if (pid > 0 && pid != MY_PID) {
killProcessQuiet(pid);
//TODO: killProcessGroup(app.info.uid, pid);
} else {
try {
thread.scheduleExit();
} catch (Exception e) {
// Ignore exceptions.
}
}
return false;
}
// If this application record is still attached to a previous
// process, clean it up now.
if (app.thread != null) {
handleAppDiedLocked(app, true, true);
}
// Tell the process all about itself.
if (DEBUG_ALL) Slog.v(
TAG, "Binding process pid " + pid + " to record " + app);
// ProcessRecord ,
final String processName = app.processName;
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
startProcessLocked(app, "link fail", processName);
return false;
}
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
app.debugging = false;
app.cached = false;
app.killedByAm = false;
app.killed = false;
// We carefully use the same state that PackageManager uses for
// filtering, since we use this flag to decide if we need to install
// providers when user is unlocked later
app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
checkTime(startTime, "attachApplicationLocked: before bindApplication");
if (!normalMode) {
Slog.i(TAG, "Launching preboot mode app: " + app);
}
if (DEBUG_ALL) Slog.v(
TAG, "New app record " + app
+ " thread=" + thread.asBinder() + " pid=" + pid);
try {
int testMode = ApplicationThreadConstants.DEBUG_OFF;
if (mDebugApp != null && mDebugApp.equals(processName)) {
testMode = mWaitForDebugger
? ApplicationThreadConstants.DEBUG_WAIT
: ApplicationThreadConstants.DEBUG_ON;
app.debugging = true;
if (mDebugTransient) {
mDebugApp = mOrigDebugApp;
mWaitForDebugger = mOrigWaitForDebugger;
}
}
String profileFile = app.instr != null ? app.instr.mProfileFile : null;
ParcelFileDescriptor profileFd = null;
int samplingInterval = 0;
boolean profileAutoStop = false;
boolean profileStreamingOutput = false;
if (mProfileApp != null && mProfileApp.equals(processName)) {
mProfileProc = app;
profileFile = mProfileFile;
profileFd = mProfileFd;
samplingInterval = mSamplingInterval;
profileAutoStop = mAutoStopProfiler;
profileStreamingOutput = mStreamingOutput;
}
boolean enableTrackAllocation = false;
if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
enableTrackAllocation = true;
mTrackAllocationApp = null;
}
// If the app is being launched for restore or full backup, set it up specially
boolean isRestrictedBackupMode = false;
if (mBackupTarget != null && mBackupAppName.equals(processName)) {
isRestrictedBackupMode = mBackupTarget.appInfo.uid >= FIRST_APPLICATION_UID
&& ((mBackupTarget.backupMode == BackupRecord.RESTORE)
|| (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
}
if (app.instr != null) {
notifyPackageUse(app.instr.mClass.getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
+ processName + " with config " + getGlobalConfiguration());
ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackageLocked(appInfo);
if (profileFd != null) {
profileFd = profileFd.dup();
}
ProfilerInfo profilerInfo = profileFile == null ? null
: new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop,
profileStreamingOutput);
// We deprecated Build.SERIAL and it is not accessible to
// apps that target the v2 security sandbox. Since access to
// the serial is now behind a permission we push down the value.
String buildSerial = Build.UNKNOWN;
if (appInfo.targetSandboxVersion != 2) {
buildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE))
.getSerial();
}
// Check if this is a secondary process that should be incorporated into some
// currently active instrumentation. (Note we do this AFTER all of the profiling
// stuff above because profiling can currently happen only in the primary
// instrumentation process.)
if (mActiveInstrumentation.size() > 0 && app.instr == null) {
for (int i = mActiveInstrumentation.size() - 1; i >= 0 && app.instr == null; i--) {
ActiveInstrumentation aInstr = mActiveInstrumentation.get(i);
if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
if (aInstr.mTargetProcesses.length == 0) {
// This is the wildcard mode, where every process brought up for
// the target instrumentation should be included.
if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
app.instr = aInstr;
aInstr.mRunningProcesses.add(app);
}
} else {
for (String proc : aInstr.mTargetProcesses) {
if (proc.equals(app.processName)) {
app.instr = aInstr;
aInstr.mRunningProcesses.add(app);
break;
}
}
}
}
}
}
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
mStackSupervisor.mActivityMetricsLogger.notifyBindApplication(app);
if (app.instr != null) {
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
}
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
// todo: Yikes! What should we do? For now we will try to
// start another process, but that could easily get us in
// an infinite loop of restarting processes...
Slog.wtf(TAG, "Exception thrown during bind of " + app, e);
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
startProcessLocked(app, "bind fail", processName);
return false;
}
// Remove this record from the list of starting applications.
mPersistentStartingProcesses.remove(app);
if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
"Attach application locked removing on hold: " + app);
mProcessesOnHold.remove(app);
boolean badApp = false;
boolean didSomething = false;
// See if the top visible activity is waiting to run in this process...
// step2 :
if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
// Find any services that should be running in this process...
if (!badApp) {
try {
didSomething |= mServices.attachApplicationLocked(app, processName);
checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
badApp = true;
}
}
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
didSomething |= sendPendingBroadcastsLocked(app);
checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it 'bad'
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
}
}
// Check whether the next backup agent is in this process...
if (!badApp && mBackupTarget != null && mBackupTarget.app == app) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
"New app is backup target, launching agent for " + app);
notifyPackageUse(mBackupTarget.appInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
try {
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
mBackupTarget.backupMode);
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
badApp = true;
}
}
if (badApp) {
app.kill("error during init", true);
handleAppDiedLocked(app, false, true);
return false;
}
if (!didSomething) {
updateOomAdjLocked();
checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
}
return true;
}
上記の方法では、まずProcessRecordを使用してプロセスの情報を記録し、後でProcessRecordをActivity StackSupervisorに関連付け、Activity StackSupervisorですべてのアクティブスタックを実行できるようにしました.
ActivityStackSupervisor#attachApplicationLocked
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
final String processName = app.processName;
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!isFocusedStack(stack)) {
continue;
}
//step 1:
ActivityRecord hr = stack.topRunningActivityLocked();
if (hr != null) {
if (hr.app == null && app.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
//step 2:
if (realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting activity "
+ hr.intent.getComponent().flattenToShortString(), e);
throw e;
}
}
}
}
}
if (!didSomething) {
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
return didSomething;
}
アクティブスタックを巡回した後、まずメソッドtopRunningActivity Lockedを実行し、次に現在のスレッドが一致するアクティビティを見つけてrealStartActivity Lockedメソッドを実行します.
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
if (!allPausedActivitiesComplete()) {
// While there are activities pausing we skipping starting any new activities until
// pauses are complete. NOTE: that we also do this for activities that are starting in
// the paused state because they will first be resumed then paused on the client side.
if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
"realStartActivityLocked: Skipping start of r=" + r
+ " some activities pausing...");
return false;
}
r.startFreezingScreenLocked(app, 0);
if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */, true /* isTop */)) {
// We only set the visibility to true if the activity is allowed to be visible based on
// keyguard state. This avoids setting this into motion in window manager that is later
// cancelled due to later calls to ensure visible activities that set visibility back to
// false.
r.setVisibility(true);
}
// schedule launch ticks to collect information about slow apps.
r.startLaunchTickingLocked();
// Have the window manager re-evaluate the orientation of the screen based on the new
// activity order. Note that as a result of this, it can call back into the activity
// manager with a new orientation. We don't care about that, because the activity is not
// currently running so we are just restarting it anyway.
if (checkConfig) {
final int displayId = r.getDisplayId();
final Configuration config = mWindowManager.updateOrientationFromAppTokens(
getDisplayOverrideConfiguration(displayId),
r.mayFreezeScreenLocked(app) ? r.appToken : null, displayId);
// Deferring resume here because we're going to launch new activity shortly.
// We don't want to perform a redundant launch of the same record while ensuring
// configurations and trying to resume top activity of focused stack.
mService.updateDisplayOverrideConfigurationLocked(config, r, true /* deferResume */,
displayId);
}
if (mKeyguardController.isKeyguardLocked()) {
r.notifyUnknownVisibilityLaunched();
}
final int applicationInfoUid =
(r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
Slog.wtf(TAG,
"User ID for activity changing for " + r
+ " appInfo.uid=" + r.appInfo.uid
+ " info.ai.uid=" + applicationInfoUid
+ " old=" + r.app + " new=" + app);
}
r.app = app;
app.waitingToKill = null;
r.launchCount++;
r.lastLaunchTime = SystemClock.uptimeMillis();
if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
int idx = app.activities.indexOf(r);
if (idx < 0) {
app.activities.add(r);
}
mService.updateLruProcessLocked(app, true, null);
mService.updateOomAdjLocked();
final TaskRecord task = r.getTask();
if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE ||
task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) {
setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE", false);
}
final ActivityStack stack = task.getStack();
try {
if (app.thread == null) {
throw new RemoteException();
}
List results = null;
List newIntents = null;
if (andResume) {
// We don't need to deliver new intents and/or set results if activity is going
// to pause immediately after launch.
results = r.results;
newIntents = r.newIntents;
}
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
"Launching: " + r + " icicle=" + r.icicle + " with results=" + results
+ " newIntents=" + newIntents + " andResume=" + andResume);
EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId,
System.identityHashCode(r), task.taskId, r.shortComponentName);
if (r.isHomeActivity()) {
// Home process is the root process of the task.
mService.mHomeProcess = task.mActivities.get(0).app;
}
mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY);
r.sleeping = false;
r.forceNewConfig = false;
mService.showUnsupportedZoomDialogIfNeededLocked(r);
mService.showAskCompatModeDialogLocked(r);
r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
ProfilerInfo profilerInfo = null;
if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
if (mService.mProfileProc == null || mService.mProfileProc == app) {
mService.mProfileProc = app;
final String profileFile = mService.mProfileFile;
if (profileFile != null) {
ParcelFileDescriptor profileFd = mService.mProfileFd;
if (profileFd != null) {
try {
profileFd = profileFd.dup();
} catch (IOException e) {
if (profileFd != null) {
try {
profileFd.close();
} catch (IOException o) {
}
profileFd = null;
}
}
}
profilerInfo = new ProfilerInfo(profileFile, profileFd,
mService.mSamplingInterval, mService.mAutoStopProfiler,
mService.mStreamingOutput);
}
}
}
app.hasShownUi = true;
app.pendingUiClean = true;
app.forceProcessStateUpTo(mService.mTopProcessState);
// Because we could be starting an Activity in the system process this may not go across
// a Binder interface which would create a new Configuration. Consequently we have to
// always create a new Configuration here.
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
mService.getGlobalConfiguration(), r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global and
// override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, !andResume,
mService.isNextTransitionForward(), profilerInfo);
if ((app.info.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
// This may be a heavy-weight process! Note that the package
// manager will ensure that only activity can run in the main
// process of the .apk, which is the only thing that will be
// considered heavy-weight.
if (app.processName.equals(app.info.packageName)) {
if (mService.mHeavyWeightProcess != null
&& mService.mHeavyWeightProcess != app) {
Slog.w(TAG, "Starting new heavy weight process " + app
+ " when already running "
+ mService.mHeavyWeightProcess);
}
mService.mHeavyWeightProcess = app;
Message msg = mService.mHandler.obtainMessage(
ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
msg.obj = r;
mService.mHandler.sendMessage(msg);
}
}
} catch (RemoteException e) {
if (r.launchFailed) {
// This is the second time we failed -- finish activity
// and give up.
Slog.e(TAG, "Second failure launching "
+ r.intent.getComponent().flattenToShortString()
+ ", giving up", e);
mService.appDiedLocked(app);
stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
"2nd-crash", false);
return false;
}
// This is the first time we failed -- restart process and
// retry.
r.launchFailed = true;
app.activities.remove(r);
throw e;
}
r.launchFailed = false;
if (stack.updateLRUListLocked(r)) {
Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
}
if (andResume) {
// As part of the process of launching, ActivityThread also performs
// a resume.
stack.minimalResumeActivityLocked(r);
} else {
// This activity is not starting in the resumed state... which should look like we asked
// it to pause+stop (but remain visible), and it has done so and reported back the
// current icicle and other state.
if (DEBUG_STATES) Slog.v(TAG_STATES,
"Moving to PAUSED: " + r + " (starting in paused state)");
r.state = PAUSED;
}
// Launch the new version setup screen if needed. We do this -after-
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
// switch back to it faster and look better.
if (isFocusedStack(stack)) {
mService.startSetupActivityLocked();
}
// Update any services we are bound to that might care about whether
// their client may have activities.
if (r.app != null) {
mService.mServices.updateServiceConnectionActivitiesLocked(r.app);
}
return true;
}
この方法でActivityを起動するためのいくつかの準備が完了し、scheduleLaunchActivityメソッドが呼び出されます.ここで呼び出されているのはApplicationThreadの方法であることがわかります.
ActivityThread.ApplicationThread#scheduleLaunchActivity
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List pendingResults, List pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
r.overrideConfig = overrideConfig;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}
ここでは,Activity状態が変化すると対応するメッセージが送信されるHandlerを呼び出すメッセージ通信メカニズムを見ることができる.また、ApplicationThreadクラスには、異なる起動状態を表し、各状態の変更はHandlerに送信されて処理される多くの異なるscheduleXXXクラスの方法が含まれていることも見られます.
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
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, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
SomeArgs args = (SomeArgs) msg.obj;
handlePauseActivity((IBinder) args.arg1, false,
(args.argi1 & USER_LEAVING) != 0, args.argi2,
(args.argi1 & DONT_REPORT) != 0, args.argi3);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY_FINISHING: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
SomeArgs args = (SomeArgs) msg.obj;
handlePauseActivity((IBinder) args.arg1, true, (args.argi1 & USER_LEAVING) != 0,
args.argi2, (args.argi1 & DONT_REPORT) != 0, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
....
これにより,Application実行中にActivityの動作状態遷移はHandlerメッセージメカニズムによって行われると結論した.このメッセージキューはActivity Threadでmainメソッドでポーリングされます.