blog

アクティビティの起動プロセス

その後、親クラスが呼び出され、Activityのメソッドが呼び出され、アプリケーションが新しいタスクスタックに引き上げられたことを示すマーカーが追加されます。 注目すべきは...

Aug 24, 2020 · 29 min. read
シェア

主なクラス

ActivityThread		アプリケーションプロセスのメインスレッドコード
ApplicationThread アプリケーションプロセス側、サーバー側との通信に使用されるIPCオブジェクト
Instrument 	アクティビティとアプリケーションのライフサイクルの呼び出しを担当する
ActivityTaskManagerService AMSの管理を担当する
ActivityStarter		起動モード、起動フラグ関連を担当する。
ActivityStack		
ActivityStackSuprevisor
ClientTransactionItemサブクラス:LaunchActivityItemなど。
ClientTransaction:関連情報とライフサイクルの状態を処理する
TransactionExecutor 主な機能は、ClientTransactionを実行することである。
ClientLifecycleManager ライフサイクル管理コール

デスクトップアイコンをクリックしてLaucherプロセスを起動します。

ランチャーアプリは、各デスクトップアイコンの作成時にクリックコールバックを登録します。

	// packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
 public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) {
 BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
 .inflate(R.layout.app_icon, parent, false);
 favorite.applyFromWorkspaceItem(info);
 favorite.setOnClickListener(ItemClickHandler.INSTANCE);	// コールバックメソッド
 favorite.setOnFocusChangeListener(mFocusHandler);
 return favorite;
 }

ItemClickHandleronClickメソッドstartAppShortcutOrInfoActivityアイコンがクリックされると、メソッドがトリガーされ、メソッドが実行されます。

public class ItemClickHandler {
 private static void onClick(View v, String sourceContainer) {
 // Make sure that rogue clicks don't get through while allapps is launching, or after the
 // view has detached (it's possible for this to happen if the view is removed mid touch).
 if (v.getWindowToken() == null) return;
 Launcher launcher = Launcher.getLauncher(v.getContext());
 if (!launcher.getWorkspace().isFinishedSwitchingState()) return;
 Object tag = v.getTag();
 if (tag instanceof WorkspaceItemInfo) {
 // デスクトップ・コンポーネントをクリックする
 onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher, sourceContainer);
 } else if (tag instanceof FolderInfo) {
 // フォルダーアイコンをクリックする
 if (v instanceof FolderIcon) {
 onClickFolderIcon(v);
 }
 } else if (tag instanceof AppInfo) {
 // アプリケーション・アイコンをクリックする
 startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher,
 sourceContainer == null ? CONTAINER_ALL_APPS: sourceContainer);
 } else if (tag instanceof LauncherAppWidgetInfo) {
 // クリックしてウィジェットを開始する
 if (v instanceof PendingAppWidgetHostView) {
 onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
 }
 }
 }
 
 private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher,
 @Nullable String sourceContainer) {
 Intent intent;
 if (item instanceof PromiseAppInfo) {
 PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
 intent = promiseAppInfo.getMarketIntent(launcher);
 } else {
 intent = item.getIntent();
 }
 if (intent == null) {
 throw new IllegalArgumentException("Input must have a valid intent");
 }
 if (item instanceof WorkspaceItemInfo) {
 WorkspaceItemInfo si = (WorkspaceItemInfo) item;
 if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)
 && Intent.ACTION_VIEW.equals(intent.getAction())) {
 // make a copy of the intent that has the package set to null
 // we do this because the platform sometimes disables instant
 // apps temporarily (triggered by the user) and fallbacks to the
 // web ui. This only works though if the package isn't set
 intent = new Intent(intent);
 intent.setPackage(null);
 }
 }
 if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation()) {
 // Preload the icon to reduce latency b/w swapping the floating view with the original.
 FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
 }
 
 // ランチャーのstartActivitySafelyメソッドを呼び出す
 launcher.startActivitySafely(v, intent, item, sourceContainer);
 }
}
public class Launcher extends BaseDraggingActivity implements LauncherExterns,
 Callbacks, LauncherProviderChangeListener, UserEventDelegate,
 InvariantDeviceProfile.OnIDPChangeListener {
 
 public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
 @Nullable String sourceContainer) {
 if (!hasBeenResumed()) {
 // Workaround an issue where the WM launch animation is clobbered when finishing the
 // recents animation into launcher. Defer launching the activity until Launcher is
 // next resumed.
 addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
 UiFactory.clearSwipeSharedState(true /* finishAnimation */);
 return true;
 }
 boolean success = super.startActivitySafely(v, intent, item, sourceContainer);
 if (success && v instanceof BubbleTextView) {
 // This is set to the view that launched the activity that navigated the user away
 // from launcher. Since there is no callback for when the activity has finished
 // launching, enable the press state and keep this reference to reset the press
 // state when we return to launcher.
 BubbleTextView btv = (BubbleTextView) v;
 btv.setStayPressed(true);
 addOnResumeCallback(btv);
 }
 return success;
 }
}

BaseDraggingActivityActivityのstartActivityメソッドはそして、親クラスのstartActivitySafelyメソッドが呼び出され、その中で、アプリケーションが新しいタスクスタックに引き上げられることを示すフラグが追加さ れます。

public abstract class BaseDraggingActivity extends BaseActivity
 implements WallpaperColorInfo.OnChangeListener {
 public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
 @Nullable String sourceContainer) {
 if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
 Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
 return false;
 }
 Bundle optsBundle = (v != null) ? getActivityLaunchOptionsAsBundle(v) : null;
 UserHandle user = item == null ? null : item.user;
 // FLAGを追加する_ACTIVITY_NEW_TASK 
 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 if (v != null) {
 intent.setSourceBounds(getViewBounds(v));
 }
 try {
 boolean isShortcut = (item instanceof WorkspaceItemInfo)
 && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
 || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
 && !((WorkspaceItemInfo) item).isPromise();
 if (isShortcut) {
 // Shortcuts need some special checks due to legacy reasons.
 startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
 } else if (user == null || user.equals(Process.myUserHandle())) {
 // Could be launching some bookkeeping activity
 startActivity(intent, optsBundle);
 AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
 Process.myUserHandle(), sourceContainer);
 } else {
 LauncherAppsCompat.getInstance(this).startActivityForProfile(
 intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
 AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
 sourceContainer);
 }
 getUserEventDispatcher().logAppLaunch(v, intent);
 getStatsLogManager().logAppLaunch(v, intent);
 return true;
 } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
 Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
 }
 return false;
 }

Activity

mMainThread.getApplicationThread()注目すべきは、Launcherアプリケーション・プロセスのApplicationThreadオブジェクトを取得することです。これは、IPC通信用のサーバー側オブジェクトであり、後でAMS側でこのオブジェクトを介してアプリケーションのスケジューリングに使用されます。

public class Activity {
	public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
 @Nullable Bundle options) {
 if (mParent == null) {
 options = transferSpringboardActivityOptions(options);
 Instrumentation.ActivityResult ar =
 mInstrumentation.execStartActivity(
 this, mMainThread.getApplicationThread(), mToken, this,
 intent, requestCode, options);
 if (ar != null) {
 mMainThread.sendActivityResult(
 mToken, mEmbeddedID, requestCode, ar.getResultCode(),
 ar.getResultData());
 }
 if (requestCode >= 0) {
 // If this start is requesting a result, we can avoid making
 // the activity visible until the result is received. Setting
 // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
 // activity hidden during this time, to avoid flickering.
 // This can only be done when a result is requested because
 // that guarantees we will get information back when the
 // activity is finished, no matter what happens to it.
 mStartedActivity = true;
 }
 cancelInputsAndStartExitTransition(options);
 // TODO Consider clearing/flushing other event sources and events for child windows.
 } else {
 if (options != null) {
 mParent.startActivityFromChild(this, intent, requestCode, options);
 } else {
 // Note we want to go through this method for compatibility with
 // existing applications that may have overridden it.
 mParent.startActivityFromChild(this, intent, requestCode);
 }
 }
 }
}

Instrumentationは、AndroidでActivityを起動し、アプリケーションとシステム間のインタラクションを監視するために使用されるハンズオンクラスです。

public class Instrumentation {
	public ActivityResult execStartActivity(
 Context who, IBinder contextThread, IBinder token, Activity target,
 Intent intent, int requestCode, Bundle options) {
 // IPC通信用オブジェクト
 IApplicationThread whoThread = (IApplicationThread) contextThread;
 Uri referrer = target != null ? target.onProvideReferrer() : null;
 if (referrer != null) {
 intent.putExtra(Intent.EXTRA_REFERRER, referrer);
 }
 ... ...
 try {
 intent.migrateExtraStreamToClipData();
 intent.prepareToLeaveProcess(who);
 // ActivityTaskManagerのstartActivityメソッドを呼び出す
 int result = ActivityTaskManager.getService()
 .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;
 }
}

ActivityTaskManagerServiceこれはIPC通信によってシステムサーバーに呼び出されます。

ActivityTaskManagerService

ActivityStarterの内部クラスであるRequestオブジェクトは、Activityの開始に必要なすべての情報を保持します。

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
	public final int startActivity(IApplicationThread caller, String callingPackage,
 Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
 int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
 return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
 resultWho, requestCode, startFlags, profilerInfo, bOptions,
 UserHandle.getCallingUserId());
 }
 
 int startActivityAsUser(IApplicationThread caller, String callingPackage,
 Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
 int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
 boolean validateIncomingUser) {
 enforceNotIsolatedCaller("startActivityAsUser");
 userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
 Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
 // ActivityStarterオブジェクトの取得
 return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
 .setCaller(caller)
 .setCallingPackage(callingPackage)
 .setResolvedType(resolvedType)
 .setResultTo(resultTo)
 .setResultWho(resultWho)
 .setRequestCode(requestCode)
 .setStartFlags(startFlags)
 .setProfilerInfo(profilerInfo)
 .setActivityOptions(bOptions)
 .setMayWait(userId)
 .execute();
 }
}
class ActivityStarter {
	int execute() {
 try {
 if (mRequest.mayWait) {
 //  
 return startActivityMayWait(mRequest.caller, mRequest.callingUid,
 mRequest.callingPackage, mRequest.realCallingPid, mRequest.realCallingUid,
 mRequest.intent, mRequest.resolvedType,
 mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
 mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
 mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
 mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
 mRequest.inTask, mRequest.reason,
 mRequest.allowPendingRemoteAnimationRegistryLookup,
 mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);
 } 
 ··· ···
 } finally {
 onExecutionComplete();
 }
 }
 
 // 一連のstartActivity呼び出しの後、最終的にstartActivityUncheckedになる。
 private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
 IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
 int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
 ActivityRecord[] outActivity, boolean restrictedBgActivity) {
 
 // Activityを開始するためのさまざまな設定を初期化する
 setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
 voiceInteractor, restrictedBgActivity);
 final int preferredWindowingMode = mLaunchParams.mWindowingMode;
	
 // 起動フラグを計算し、mLaunchFlagsに代入する。
 computeLaunchingTaskFlags();
 computeSourceStack();
 mIntent.setFlags(mLaunchFlags);
		
 // 再利用できるアクティビティがあるかどうかを判断する
 ActivityRecord reusedActivity = getReusableIntentActivity();
 mSupervisor.getLaunchParamsController().calculate(
 reusedActivity != null ? reusedActivity.getTaskRecord() : mInTask,
 r.info.windowLayout, r, sourceRecord, options, PHASE_BOUNDS, mLaunchParams);
 mPreferredDisplayId =
 mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
 : DEFAULT_DISPLAY;
 // Do not start home activity if it cannot be launched on preferred display. We are not
 // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
 // fallback to launch on other displays.
 if (r.isActivityTypeHome() && !mRootActivityContainer.canStartHomeOnDisplay(r.info,
 mPreferredDisplayId, true /* allowInstrumenting */)) {
 Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
 return START_CANCELED;
 }
 if (reusedActivity != null) {
 	// reusedActivitynullの場合
 ··· ···
 }
 ··· ···
 
 mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
 mOptions);
 if (mDoResume) {
 final ActivityRecord topTaskActivity =
 mStartActivity.getTaskRecord().topRunningActivityLocked();
 if (!mTargetStack.isFocusable()
 || (topTaskActivity != null && topTaskActivity.mTaskOverlay
 && mStartActivity != topTaskActivity)) {
 // If the activity is not focusable, we can't resume it, but still would like to
 // make sure it becomes visible as it starts (this will also trigger entry
 // animation). An example of this are PIP activities.
 // Also, we don't want to resume activities in a task that currently has an overlay
 // as the starting activity just needs to be in the visible paused state until the
 // over is removed.
 mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);
 // Go ahead and tell window manager to execute app transition for this activity
 // since the app transition will not be triggered through the resume channel.
 mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
 } else {
 // If the target stack was not previously focusable (previous top running activity
 // on that stack was not visible) then any prior calls to move the stack to the
 // will not update the focused stack. If starting the new activity now allows the
 // task stack to be focusable, then ensure that we now update the focused stack
 // accordingly.
 if (mTargetStack.isFocusable()
 && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
 mTargetStack.moveToFront("startActivityUnchecked");
 }
 // 最も重要なステップ
 mRootActivityContainer.resumeFocusedStacksTopActivities(
 mTargetStack, mStartActivity, mOptions);
 }
 } else if (mStartActivity != null) {
 mSupervisor.mRecentTasks.add(mStartActivity.getTaskRecord());
 }
 mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
 mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTaskRecord(),
 preferredWindowingMode, mPreferredDisplayId, mTargetStack);
 return START_SUCCESS;
 }
}
class RootActivityContainer extends ConfigurationContainer
 implements DisplayManager.DisplayListener {
 
 boolean resumeFocusedStacksTopActivities(
 ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
 if (!mStackSupervisor.readyToResume()) {
 return false;
 }
 boolean result = false;
 if (targetStack != null && (targetStack.isTopStackOnDisplay()
 || getTopDisplayFocusedStack() == targetStack)) {
 result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
 }
		
 ··· ···
 return result;
 }
}
class ActivityStack extends ConfigurationContainer {
	boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
 if (mInResumeTopActivity) {
 // Don't even start recursing.
 return false;
 }
 boolean result = false;
 try {
 // Protect against recursion.
 mInResumeTopActivity = true;
 result = resumeTopActivityInnerLocked(prev, options);
 // When resuming the top activity, it may be necessary to pause the top activity (for
 // example, returning to the lock screen. We suppress the normal pause logic in
 // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the
 // end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here
 // to ensure any necessary pause logic occurs. In the case where the Activity will be
 // shown regardless of the lock screen, the call to
 // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.
 final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
 if (next == null || !next.canTurnScreenOn()) {
 checkReadyForSleep();
 }
 } finally {
 mInResumeTopActivity = false;
 }
 return result;
 }
 
 private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
 if (!mService.isBooting() && !mService.isBooted()) {
 // Not ready yet!
 return false;
 }
 boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
 // 現在Resumed状態にあるActivityがある場合、それは最初にPause状態に変換される。
 if (mResumedActivity != null) {
 if (DEBUG_STATES) Slog.d(TAG_STATES,
 "resumeTopActivityLocked: Pausing " + mResumedActivity);
 pausing |= startPausingLocked(userLeaving, false, next, false);
 }
 
 // 指定されたアクティビティを再び開始する
 mStackSupervisor.startSpecificActivityLocked(next, true, true);
 return true;
 }
 
}

startSpecificActivityLockedまずstartPausingLockedフローを分析し、指定されたアプリケーションをプルアップするメソッドに戻りましょう。

class ActivityStack extends ConfigurationContainer {
	final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
 ActivityRecord resuming, boolean pauseImmediately) {
 ··· ···
 ActivityRecord prev = mResumedActivity;
 if (prev == null) {
 if (resuming == null) {
 Slog.wtf(TAG, "Trying to pause when nothing is resumed");
 mRootActivityContainer.resumeFocusedStacksTopActivities();
 }
 return false;
 }
 if (prev == resuming) {
 Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
 return false;
 }
 if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSING: " + prev);
 else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
 mPausingActivity = prev;
 mLastPausedActivity = prev;
 mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
 || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
 prev.setState(PAUSING, "startPausingLocked");
 prev.getTaskRecord().touchActiveTime();
 clearLaunchTime(prev);
 mService.updateCpuStats();
 if (prev.attachedToProcess()) {
 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
 try {
 EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
 prev.shortComponentName, "userLeaving=" + userLeaving);
				
 // アプリケーションのライフサイクルをスケジュールするために、ClientLifecycleManagerオブジェクトを取得する。
 mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
 prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
 prev.configChangeFlags, pauseImmediately));
 } catch (Exception e) {
 // Ignore exception, if process died other code will cleanup.
 Slog.w(TAG, "Exception thrown during pause", e);
 mPausingActivity = null;
 mLastPausedActivity = null;
 mLastNoHistoryActivity = null;
 }
 } else {
 mPausingActivity = null;
 mLastPausedActivity = null;
 mLastNoHistoryActivity = null;
 }
 }
}

ClientLifecycleManagerオブジェクトは、アプリケーションのライフサイクル管理を支援するためのクラスで、内部的にはIPC通信用のApplicationThreadオブジェクトを保持しています。

class ClientLifecycleManager {
	void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
 final IApplicationThread client = transaction.getClient();
 transaction.schedule();
 if (!(client instanceof Binder)) {
 // If client is not an instance of Binder - it's a remote call and at this point it is
 // safe to recycle the object. All objects used for local calls will be recycled after
 // the transaction is executed on client in ActivityThread.
 transaction.recycle();
 }
 }
}
public class ClientTransaction implements Parcelable, ObjectPoolItem {
	public void schedule() throws RemoteException {
 mClient.scheduleTransaction(this);
 }
}
public final class ActivityThread extends ClientTransactionHandler {
	public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
 ActivityThread.this.scheduleTransaction(transaction);
 }
 
 // whatEXECUTEするには_TRANSACTION
 private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
 if (DEBUG_MESSAGES) {
 Slog.v(TAG,
 "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj);
 }
 Message msg = Message.obtain();
 msg.what = what;
 msg.obj = obj;
 msg.arg1 = arg1;
 msg.arg2 = arg2;
 if (async) {
 msg.setAsynchronous(true);
 }
 
 mH.sendMessage(msg);
 }
}
class H extends Handler {
	public void handleMessage(Message msg) {
 	case EXECUTE_TRANSACTION:
 final ClientTransaction transaction = (ClientTransaction) msg.obj;
 	// 重要なメソッド
 mTransactionExecutor.execute(transaction);
 if (isSystem()) {
 // Client transactions inside system process are recycled on the client side
 // instead of ClientLifecycleManager to avoid being cleared before this
 // message is handled.
 transaction.recycle();
 }
 // TODO(lifecycler): Recycle locally scheduled transactions.
 break;
 }
}

TransactionExecutor実行されるexecuteLifecycleStatePauseActivityItem.executeここでメソッドに戻り、メソッドを実行します。

public void execute(ClientTransaction transaction) {
 if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");
 final IBinder token = transaction.getActivityToken();
 if (token != null) {
 final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
 mTransactionHandler.getActivitiesToBeDestroyed();
 final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token);
 if (destroyItem != null) {
 if (transaction.getLifecycleStateRequest() == destroyItem) {
 // It is going to execute the transaction that will destroy activity with the
 // token, so the corresponding to-be-destroyed record can be removed.
 activitiesToBeDestroyed.remove(token);
 }
 if (mTransactionHandler.getActivityClient(token) == null) {
 // The activity has not been created but has been requested to destroy, so all
 // transactions for the token are just like being cancelled.
 Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:
"
 + transactionToString(transaction, mTransactionHandler));
 return;
 }
 }
 }
 if (DEBUG_RESOLVER) Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
		
		// 重要なメソッド1
 executeCallbacks(transaction);
		
 	// 重要な方法2
 executeLifecycleState(transaction);
 mPendingActions.clear();
 if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
 }
private void executeLifecycleState(ClientTransaction transaction) {
 	// このlifecycleItemはATMSから持ってきたもので、実際にはPauseActivityItemオブジェクトである。
 final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
 if (lifecycleItem == null) {
 // No lifecycle request, return early.
 return;
 }
 final IBinder token = transaction.getActivityToken();
 final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
 if (DEBUG_RESOLVER) {
 Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
 + lifecycleItem + " for activity: "
 + getShortActivityName(token, mTransactionHandler));
 }
 if (r == null) {
 // Ignore requests for non-existent client records for now.
 return;
 }
 // Cycle to the state right before the final requested state.
 cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);
 // Execute the final transition with proper parameters.
 lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
 lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
 }
public class PauseActivityItem extends ActivityLifecycleItem {
	@Override
 public void execute(ClientTransactionHandler client, IBinder token,
 PendingTransactionActions pendingActions) {
 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
 // クライアントはアクティビティのThreadオブジェクトである。
 client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
 "PAUSE_ACTIVITY_ITEM");
 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
 }
}
public final class ActivityThread extends ClientTransactionHandler {
	@Override
 public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
 int configChanges, PendingTransactionActions pendingActions, String reason) {
 ActivityClientRecord r = mActivities.get(token);
 if (r != null) {
 if (userLeaving) {
 performUserLeavingActivity(r);
 }
 r.activity.mConfigChangeFlags |= configChanges;
 performPauseActivity(r, finished, reason, pendingActions);
 // Make sure any pending writes are now committed.
 if (r.isPreHoneycomb()) {
 QueuedWork.waitToFinish();
 }
 mSomeActivitiesChanged = true;
 }
 }
 
 private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
 if (r.paused) {
 // You are already paused silly...
 return;
 }
 // Always reporting top resumed position loss when pausing an activity. If necessary, it
 // will be restored in performResumeActivity().
 reportTopResumedActivityChanged(r, false /* onTop */, "pausing");
 try {
 r.activity.mCalled = false;
 // InstrumentのcallActivityOnPauseに移る。
 mInstrumentation.callActivityOnPause(r.activity);
 if (!r.activity.mCalled) {
 throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent)
 + " 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 "
 + safeToComponentShortString(r.intent) + ": " + e.toString(), e);
 }
 }
 r.setState(ON_PAUSE);
 }
}

InstrumentのcallActivityOnPauseメソッドに移ります。

public class Instrumentation {
	public void callActivityOnPause(Activity activity) {
 activity.performPause();
 }
}

このアクティビティはユーザーのアクティビティで、このシナリオではLauncher.javaです。

public class Activity {
	final void performPause() {
 dispatchActivityPrePaused();
 mDoReportFullyDrawn = false;
 mFragments.dispatchPause();
 mCalled = false;
 // テンプレート・メソッド、ユーザー定義のonPauseメソッド
 onPause();
 writeEventLog(LOG_AM_ON_PAUSE_CALLED, "performPause");
 mResumed = false;
 if (!mCalled && getApplicationInfo().targetSdkVersion
 >= android.os.Build.VERSION_CODES.GINGERBREAD) {
 throw new SuperNotCalledException(
 "Activity " + mComponent.toShortString() +
 " did not call through to super.onPause()");
 }
 dispatchActivityPostPaused();
 }
}

ActivityStackManagerServicestartSpecificActivityLockedさて、この時点でLauncher ActivityはonPauseコールバックを完了し、以下は指定されたアプリケーションをプルアップするためのメソッドに戻ります。

アプリケーションプロセスの開始

1.アクティビティが配置されているアプリケーションが既に起動しており、プロセスを作成する必要がない場合、2.アクティビティが必要とするプロセスを先に作成する必要がある場合です;

public class ActivityStackSupervisor implements RecentTasks.Callbacks {
	void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
 // Is this activity's application already running?
 final WindowProcessController wpc =
 mService.getProcessController(r.processName, r.info.applicationInfo.uid);
 boolean knownToBeDead = false;
 if (wpc != null && wpc.hasThread()) {
 try {
 // アクティビティが属するアプリケーションがすでに開始されている場合
 realStartActivityLocked(r, wpc, 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.
 knownToBeDead = true;
 }
 // Suppress transition until the new activity becomes ready, otherwise the keyguard can
 // appear for a short amount of time before the new process with the new activity had the
 // ability to set its showWhenLocked flags.
 if (getKeyguardController().isKeyguardLocked()) {
 r.notifyUnknownVisibilityLaunched();
 }
 try {
 if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
 + r.processName);
 }
 
 // HandlerメッセージがstartProcessをトリガーして新しいプロセスを作成する。
 final Message msg = PooledLambda.obtainMessage(
 ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
 r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
 mService.mH.sendMessage(msg);
 } finally {
 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
 }
 }
}

mService.mH.sendMessage(msg);ActivityManagerServiceこのメッセージは、内部クラスLocalServiceのstartProcessメソッドにトリガーされ、呼び出しのカスケードの後、最終的にProcess.startメソッドを呼び出します。

public final class LocalService extends ActivityManagerInternal {
	public void startProcess(String processName, ApplicationInfo info,
 boolean knownToBeDead, String hostingType, ComponentName hostingName) {
 try {
 if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"
 + processName);
 }
 synchronized (ActivityManagerService.this) {
 startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
 new HostingRecord(hostingType, hostingName),
 false /* allowWhileBooting */, false /* isolated */,
 true /* keepIfLarge */);
 }
 } finally {
 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 }
 }
 
 final ProcessRecord startProcessLocked(String processName,
 ApplicationInfo info, boolean knownToBeDead, int intentFlags,
 HostingRecord hostingRecord, boolean allowWhileBooting,
 boolean isolated, boolean keepIfLarge) {
 return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
 hostingRecord, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
 null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
 null /* crashHandler */);
 }
 
 private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
 ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
 String seInfo, String requiredAbi, String instructionSet, String invokeWith,
 long startTime) {
 try {
 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
 app.processName);
 checkSlow(startTime, "startProcess: asking zygote to start proc");
 final Process.ProcessStartResult startResult;
 if (hostingRecord.usesWebviewZygote()) {
 startResult = startWebView(entryPoint,
 app.processName, uid, uid, gids, runtimeFlags, mountExternal,
 app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
 app.info.dataDir, null, app.info.packageName,
 new String[] {PROC_START_SEQ_IDENT + app.startSeq});
 } else if (hostingRecord.usesAppZygote()) {
 final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
 startResult = appZygote.getProcess().start(entryPoint,
 app.processName, uid, uid, gids, runtimeFlags, mountExternal,
 app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
 app.info.dataDir, null, app.info.packageName,
 /*useUsapPool=*/ false,
 new String[] {PROC_START_SEQ_IDENT + app.startSeq});
 } else {
 // 重要なメソッドは、Process.start 
 startResult = Process.start(entryPoint,
 app.processName, uid, uid, gids, runtimeFlags, mountExternal,
 app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
 app.info.dataDir, invokeWith, app.info.packageName,
 new String[] {PROC_START_SEQ_IDENT + app.startSeq});
 }
 checkSlow(startTime, "startProcess: returned from zygote!");
 return startResult;
 } finally {
 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 }
 }
}

Process.startは、Zygoteを介してアプリケーションプロセスを作成します。

public class Process {
	public static ProcessStartResult start(@NonNull final String processClass,
 @Nullable final String niceName,
 int uid, int gid, @Nullable int[] gids,
 int runtimeFlags,
 int mountExternal,
 int targetSdkVersion,
 @Nullable String seInfo,
 @NonNull String abi,
 @Nullable String instructionSet,
 @Nullable String appDataDir,
 @Nullable String invokeWith,
 @Nullable String packageName,
 @Nullable String[] zygoteArgs) {
 return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
 runtimeFlags, mountExternal, targetSdkVersion, seInfo,
 abi, instructionSet, appDataDir, invokeWith, packageName,
 /*useUsapPool=*/ true, zygoteArgs);
 }
}

Zygoteは、新しいプロセスをForkするように、Zygoteのサーバー側に送信されたソケットメッセージを介してZygoteは、分析するために別の記事に残されるプロセスを作成し、新しいプロセスは、メインのActivityThreadメソッドのメインスレッドで実行されます。

ActivityThread

Zygoteによってアプリケーションプロセスが起動されると、まずActivityThread.mainメソッドが実行され、Mainメソッドの中でメインスレッドのLooperの作成など、いくつかの初期化作業が行われます。最も重要なのはattachメソッドで、アプリケーションプロセスが引き上げられたことをIPCメッセージでAMSに通知します。

public final class ActivityThread extends ClientTransactionHandler {
 private void attach(boolean system, long startSeq) {
 sCurrentActivityThread = this;
 mSystemThread = system;
 if (!system) {
 android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
 UserHandle.myUserId());
 RuntimeInit.setApplicationObject(mAppThread.asBinder());
 // ActivityManagerServiceのipcオブジェクトが実行される。
 final IActivityManager mgr = ActivityManager.getService();
 try {
 mgr.attachApplication(mAppThread, startSeq);
 } catch (RemoteException ex) {
 throw ex.rethrowFromSystemServer();
 }
 // Watch for getting close to heap limit.
 ··· ···
 } 
}
public class ActivityManagerService extends IActivityManager.Stub
 implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
	public final void attachApplication(IApplicationThread thread, long startSeq) {
 synchronized (this) {
 int callingPid = Binder.getCallingPid();
 final int callingUid = Binder.getCallingUid();
 final long origId = Binder.clearCallingIdentity();
 attachApplicationLocked(thread, callingPid, callingUid, startSeq);
 Binder.restoreCallingIdentity(origId);
 }
 }
}

そして、LocalServiceのattachApplicationメソッドに進みます。

public final class LocalService extends ActivityManagerInternal {
	public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
 synchronized (mGlobalLockWithoutBoost) {
 return mRootActivityContainer.attachApplication(wpc);
 }
 }
}
class RootActivityContainer extends ConfigurationContainer
 implements DisplayManager.DisplayListener {
 
	boolean attachApplication(WindowProcessController app) throws RemoteException {
 final String processName = app.mName;
 boolean didSomething = false;
 for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
 final ActivityDisplay display = mActivityDisplays.get(displayNdx);
 final ActivityStack stack = display.getFocusedStack();
 if (stack != null) {
 stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
 final ActivityRecord top = stack.topRunningActivityLocked();
 final int size = mTmpActivityList.size();
 for (int i = 0; i < size; i++) {
 final ActivityRecord activity = mTmpActivityList.get(i);
 if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
 && processName.equals(activity.processName)) {
 try {
 // ActivityStackSuperVisorのrealStartActivityLockedメソッドの呼び出し
 if (mStackSupervisor.realStartActivityLocked(activity, app,
 top == activity /* andResume */, true /* checkConfig */)) {
 didSomething = true;
 }
 } catch (RemoteException e) {
 Slog.w(TAG, "Exception in new application when starting activity "
 + top.intent.getComponent().flattenToShortString(), e);
 throw e;
 }
 }
 }
 }
 }
 if (!didSomething) {
 ensureActivitiesVisible(null, 0, false /* preserve_windows */);
 }
 return didSomething;
 }
}
public class ActivityStackSupervisor implements RecentTasks.Callbacks {
 boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
 boolean andResume, boolean checkConfig) throws RemoteException {
 try {
 // アプリケーションを開始するトランザクションを作成する
 final ClientTransaction clientTransaction = ClientTransaction.obtain(
 proc.getThread(), r.appToken);
 final DisplayContent dc = r.getDisplay().mDisplayContent;
 // LaunchActivityITtem 
 clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
 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, proc.getReportedProcState(),
 r.icicle, r.persistentState, results, newIntents,
 dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
 	r.assistToken));
 // Set desired final state.
 final ActivityLifecycleItem lifecycleItem;
 if (andResume) {
 lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
 } else {
 lifecycleItem = PauseActivityItem.obtain();
 }
 clientTransaction.setLifecycleStateRequest(lifecycleItem);
 // Schedule transaction.
 mService.getLifecycleManager().scheduleTransaction(clientTransaction);
 }
}
public class LaunchActivityItem extends ClientTransactionItem {
 public void execute(ClientTransactionHandler client, IBinder token,
 PendingTransactionActions pendingActions) {
 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
 ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
 mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
 mPendingResults, mPendingNewIntents, mIsForward,
 mProfilerInfo, client, mAssistToken);
 // clientこのオブジェクトは、アクティビティのThreadオブジェクトである。
 client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
 }
}
public final class ActivityThread extends ClientTransactionHandler {
 public Activity handleLaunchActivity(ActivityClientRecord r,
 PendingTransactionActions pendingActions, Intent customIntent) {
 	
 // アクティビティを作成する 
 final Activity a = performLaunchActivity(r, customIntent);
 if (a != null) {
 r.createdConfig = new Configuration(mConfiguration);
 reportSizeConfigurations(r);
 if (!r.activity.mFinished && pendingActions != null) {
 pendingActions.setOldState(r.state);
 pendingActions.setRestoreInstanceState(true);
 pendingActions.setCallOnPostCreate(true);
 }
 } else {
 // If there was an error, for any reason, tell the activity manager to stop us.
 try {
 ActivityTaskManager.getService()
 .finishActivity(r.token, Activity.RESULT_CANCELED, null,
 Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
 } catch (RemoteException ex) {
 throw ex.rethrowFromSystemServer();
 }
 }
 return a;
 }
 
 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
 ActivityInfo aInfo = r.activityInfo;
		
 // コンテキストの作成
 ContextImpl appContext = createBaseContextForActivity(r);
 Activity activity = null;
 try {
 java.lang.ClassLoader cl = appContext.getClassLoader();
 // InstrumentリフレクションによってActivityオブジェクトを作成する
 activity = mInstrumentation.newActivity(
 cl, component.getClassName(), r.intent);
 StrictMode.incrementExpectedActivityCount(activity.getClass());
 r.intent.setExtrasClassLoader(cl);
 r.intent.prepareToEnterProcess();
 if (r.state != null) {
 r.state.setClassLoader(cl);
 }
 } catch (Exception e) {
 }
 try {
 // アプリケーションの作成
 Application app = r.packageInfo.makeApplication(false, mInstrumentation);
 if (activity != null) {
 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
 Configuration config = new Configuration(mCompatConfiguration);
 if (r.overrideConfig != null) {
 config.updateFrom(r.overrideConfig);
 }
 Window window = null;
 if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
 window = r.mPendingRemoveWindow;
 r.mPendingRemoveWindow = null;
 r.mPendingRemoveWindowManager = null;
 }
 appContext.setOuterContext(activity);
 // 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,
 r.assistToken);
 activity.mCalled = false;
 if (r.isPersistable()) {
 mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
 } else {
 // onCreateメソッドの実行
 mInstrumentation.callActivityOnCreate(activity, r.state);
 }
 if (!activity.mCalled) {
 throw new SuperNotCalledException(
 "Activity " + r.intent.getComponent().toShortString() +
 " did not call through to super.onCreate()");
 }
 r.activity = activity;
 }
 return activity;
 }
}

この時点でActivityが作成され、onCreateメソッド、onStart、onResumeコールバックが実行されます。新しいActivityがonResumeを実行した後、元のトップオブスタックのActivityがonStopライフサイクルの実行を開始します。

Read next

システム・アーキテクチャ設計ノート - テスト自動化とオブジェクト指向テスト

ソフトウェアテストの作業負荷は大きいですが、テストは非常に自動化作業のかなりの部分を実施するためにコンピュータを適用する可能性が高いです。その理由は、テストの操作の多くは、反復的な非知的創造性、作業に細心の注意を払う必要があり、コンピュータは、これらのタスクを完了するために人間の代わりに最適です。 テストの自動化は、開発作業全体の品質、コスト、サイクルタイムに大きな影響を与えます。いくつかの適切な考慮事項...

Aug 24, 2020 · 5 min read