【Android 14源码分析】Activity启动流程-3

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
                  
                  
                                           – 服装学院的IT男

本篇已收录于Activity短暂的一生系列
欢迎一起学习讨论Android应用开发或者WMS
V:WJB6995
Q:707409815

正文

由于篇幅原因,整个启动流程分为以下3篇进行分析:

Activity启动流程-1

Activity启动流程-2

Activity启动流程-3

本篇介绍阶段三的逻辑,这部分的分析上篇阶段二的触发点是同级的,也就是在阶段一中 TaskFragment::resumeTopActivity 触发的。
进程是怎么创建的不是当前分析的重点,所以快速过一遍流程。

1 阶段三–触发进程创建

执行pause后会执行 ActivityTaskManagerService::startProcessAsync,最后也是通过 ActivityManagerService来触发启动进程的。
看看AMS这块执行进程创建的调用流程

# ActivityTaskManagerServicevoid startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,String hostingType) {try {if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"+ activity.processName);}// 发送消息,启动进程,调用 ActivityManagerInternal::startProcessfinal Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,isTop, hostingType, activity.intent.getComponent());mH.sendMessage(m);} finally {Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}}

这里通过 Handler 来完成,ActivityManagerInternal::startProcess 的实现在 ActivityManagerService 的内部类 LocalService 中。

# ActivityManagerService$LocalServicepublic void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,boolean isTop, 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, isTop),ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,false /* isolated */);}} finally {Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);}}@GuardedBy("this")final ProcessRecord startProcessLocked(String processName,ApplicationInfo info, boolean knownToBeDead, int intentFlags,HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,boolean isolated) {return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,false /* isSdkSandbox */, 0 /* sdkSandboxClientAppUid */,null /* sdkSandboxClientAppPackage */,null /* ABI override */, null /* entryPoint */,null /* entryPointArgs */, null /* crashHandler */);}

流程走到 ProcessList::startProcessLocked 。

# ProcessListboolean startProcessLocked(......) {......mService.mProcStartHandler.post(() -> handleProcessStart(app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,requiredAbi, instructionSet, invokeWith, startSeq));     ......   }private void handleProcessStart(......) {创建一个用于启动进程的 Runnable 对象final Runnable startRunnable = () -> {try {    // 调用 startProcess 方法启动进程,并获取启动结果 ProcessStartResultfinal Process.ProcessStartResult startResult = startProcess(app.getHostingRecord(),entryPoint, app, app.getStartUid(), gids, runtimeFlags, zygotePolicyFlags,mountExternal, app.getSeInfo(), requiredAbi, instructionSet, invokeWith,app.getStartTime());// 在锁定 ActivityManagerService 后,处理进程启动结果synchronized (mService) {// 更新应用的状态,如设置PID,更新生命周期状态等handleProcessStartedLocked(app, startResult, startSeq);}} catch (RuntimeException e) {......异常处理}};......}
    1. 通过 ProcessList::startProcess 来启动应用进程
    1. 启动玩之后 ProcessList::handleProcessStartedLocked 会更新应用的状态,如设置PID,更新生命周期状态等

ProcessList::handleProcessStartedLocked 经过几次重载会调用下面的方法

# ProcessListActivityManagerService mService = null;boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,long expectedStartSeq, boolean procAttached) {......StringBuilder buf = mStringBuilder;buf.append("Start proc ");buf.append(pid);buf.append(':');buf.append(app.processName);buf.append('/');UserHandle.formatUid(buf, app.getStartUid());if (app.getIsolatedEntryPoint() != null) {buf.append(" [");buf.append(app.getIsolatedEntryPoint());buf.append("]");}buf.append(" for ");buf.append(app.getHostingRecord().getType());if (app.getHostingRecord().getName() != null) {buf.append(" ");buf.append(app.getHostingRecord().getName());}// 输出日志mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.getStartUid());......}

1.1创建进程小结

创建进行逻辑我没深入了解过,所以简单看了下调用逻辑,以 AMS::reportUidInfoMessageLocked 结束的原因是因为其会打印下面的这个创建进程的关键日志:

07-26 19:19:05.477  8737  8782 I ActivityManager: Start proc 19643:com.example.myapplication/u0a198 for next-top-activity {com.example.myapplication/com.example.myapplication.MainActivity}

经常看日志看启动了哪个进程搜的日志就是在这里打印的。

这部分的调用链如下:

ActivityTaskManagerService::startProcessAsyncActivityManagerService$LocalService::startProcessActivityManagerService::startProcessLockedProcessList::startProcessLockedProcessList::handleProcessStartProcessList::startProcess                               -- 启动进程ProcessList::handleProcessStartedLockedActivityManagerService::reportUidInfoMessageLocked  -- 打印日志

2. 阶段三–应用进程创建

进程创建完成后会执行 ActivityThread::main 方法,所以应用端进程创建结束的逻辑从这个方法开始分析。

2.1 应用端处理

# ActivityThread// ApplicationThread 是 AMS 作为 C 端时,与应用进程通信的方式final ApplicationThread mAppThread = new ApplicationThread();public static void main(String[] args) {......// 主线程LooperLooper.prepareMainLooper();......ActivityThread thread = new ActivityThread();// 下一步thread.attach(false, startSeq);// 主线程LooperLooper.loop();}private void attach(boolean system, long startSeq) {......final IActivityManager mgr = ActivityManager.getService();try {//重点 *2. 将mAppThread告知AMS,用于AMS与应用进程通信mgr.attachApplication(mAppThread, startSeq);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}......}

应用端的调用链比较简单:

ActivityThread::mainLooper::prepareMainLooperActivityThread::initActivityThread::attachActivityManagerService::attachApplication  -- 跨进程Looper::loop

应用进程(电话)创建完毕后,在 main 方法里会执行 attach 就是要将自己的信息告知AMS,毕竟 AMS 是管理模块。

2.2 system_service端处理

system_service 端 ActivityManagerService 知道有进程启动了,这个行为也可能会触发系统组显示逻辑的改变,所以比如也会做响应处理

2.1 调用链

ActivityManagerService::attachApplicationActivityManagerService::attachApplicationLockedActivityThread::bindApplicationActivityTaskManagerService.LocalService::attachApplication  WindowContainer::forAllRootTasks  --- 省略forAllRootTasks等固定堆栈Task::forAllRootTasksWindowContainer::forAllActivitiesActivityRecord::forAllActivitiesRootWindowContainer.AttachApplicationHelper::testRootWindowContainer.AttachApplicationHelper::testActivityTaskSupervisor::realStartActivityLocked  -- 构建LaunchActivityItem

在这里插入图片描述
接上一篇知道如果进程启动了 ActivityTaskSupervisor::startSpecificActivity 就会走进去ActivityTaskSupervisor::realStartActivityLocked 。
但是可能会好奇怎么就知道要执行应用 MainActivity 到 onCreate 就一定是在这个方法里呢? 调试方法有很多,比如加 log ,打堆栈,但是对应这个逻辑比较简单的是,需要执行 Activity 启动到 onCreate 的控制在 LaunchActivityItem 中,而 LaunchActivityItem在 framework 的引用除了本身,就只有在 ActivityTaskSupervisor。

在这里插入图片描述

2.2 主流程

# ActivityManagerService// 当应用进程调用attachApplication 执行public final void attachApplication(IApplicationThread thread, long startSeq) {if (thread == null) {throw new SecurityException("Invalid application interface");}synchronized (this) {// 获取 应用进程的信息后执行attachApplicationLockedint callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();// 执行attachApplicationLocked(thread, callingPid, callingUid, startSeq);Binder.restoreCallingIdentity(origId);}}private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {// 需要启动应用的进程数据ProcessRecord app;......if (pid != MY_PID && pid >= 0) {synchronized (mPidsSelfLocked) {// 通过mPidsSelfLocked获取app = mPidsSelfLocked.get(pid);}......} ...... // 触发ActivityThread::bindApplication 逻辑if (app.getIsolatedEntryPoint() != null) {......} else if (instr2 != null) {// bindApplication......    } else {// 重点* 1. bindApplicationthread.bindApplication(processName, appInfo,app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,providerList, null, profilerInfo, null, null, null, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.isPersistent(),new Configuration(app.getWindowProcessController().getConfiguration()),app.getCompat(), getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, autofillOptions, contentCaptureOptions,app.getDisabledCompatChanges(), serializedSystemFontMap,app.getStartElapsedTime(), app.getStartUptime());}......if (!mConstants.mEnableWaitForFinishAttachApplication) {// 重点* 2. finishAttachApplicationInner(startSeq, callingUid, pid);} else {app.setPendingFinishAttach(true);}......}

ActivityManagerService::finishAttachApplicationInner 是 U 把原来的逻辑提取新增的方法。

# ActivityManagerServicepublic ActivityTaskManagerInternal mAtmInternal;private void finishAttachApplicationInner(long startSeq, int uid, int pid) {......if (normalMode) {try {// 重点 触发构建 LaunchActivityItem 流程didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());} catch (Exception e) {Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);badApp = true;}}......}

这里是触发 LaunchActivityItem 的流程主线, mAtmInternal是 ATMS 的内部类 LocalService 。

# ActivityTaskManagerService$LocalService@Overridepublic boolean attachApplication(WindowProcessController wpc) throws RemoteException {......return mRootWindowContainer.attachApplication(wpc);......}

流程来到 RootWindowContainer 预感要开始处理窗口显示逻辑。

# RootWindowContainerprivate final AttachApplicationHelper mAttachApplicationHelper = new AttachApplicationHelper();boolean attachApplication(WindowProcessController app) throws RemoteException {try {return mAttachApplicationHelper.process(app);} finally {mAttachApplicationHelper.reset();}}# RootWindowContainer// 实现 Consumer 接口private class AttachApplicationHelper implements Consumer<Task>, Predicate<ActivityRecord> {......boolean process(WindowProcessController app) throws RemoteException {mApp = app;for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {// 重点* 调用每个容器的 forAllRootTasksgetChildAt(displayNdx).forAllRootTasks(this);......}......}......}

这里看到传递了“this”,所以 AttachApplicationHelper 必然实现了 Consumer 接口, 直接看其 accept 实现即可。

# RootWindowContainer$AttachApplicationHelperprivate class AttachApplicationHelper implements Consumer<Task>, Predicate<ActivityRecord> {......boolean process(WindowProcessController app) throws RemoteException {mApp = app;for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {// 重点*1. 调用每个容器的 forAllRootTasksgetChildAt(displayNdx).forAllRootTasks(this);......}......}@Overridepublic void accept(Task rootTask) {......if (rootTask.getVisibility(null /* starting */)== TASK_FRAGMENT_VISIBILITY_INVISIBLE) {// 如果Task 不可见则不需要处理return;}// 执行 topRunningActivitymTop = rootTask.topRunningActivity();// 重点*2. 执行accept 让容器下的每个 ActivityRecord 执行 testrootTask.forAllActivities(this);}@Overridepublic boolean test(ActivityRecord r) {// 判断 ActivityRecord 是否满足需要启动条件if (r.finishing || !r.showToCurrentUser() || !r.visibleIgnoringKeyguard|| r.app != null || mApp.mUid != r.info.applicationInfo.uid|| !mApp.mName.equals(r.processName)) {return false;}try {// 重点*3. 执行 realStartActivityLocked  尝试实际启动 Activityif (mTaskSupervisor.realStartActivityLocked(r, mApp,mTop == r && r.getTask().canBeResumed(r) /* andResume */,true /* checkConfig */)) {mHasActivityStarted = true;}} catch (RemoteException e) {......}return false;}}

这部分的逻辑也就是一路执行,比较疑惑的点也许是进入 AttachApplicationHelper::proces 方法后的几个 Lambda 表达式容易绕晕。
简单梳理一下,首先是从 RootWindowContainer 开始执行的,目的就是想要执行到所有 ActivityRecord 然后判断一下它的情况是不是需要启动对应的 Activity 。

    1. AttachApplicationHelper::proces 里的 “getChildAt” 对应的是每个屏幕,也就是 DisplayContent ,然后再遍历其下的每个 RootTask ,让其执行 accept 函数
    1. AttachApplicationHelper::accept 目前是对象是 Task ,准确的说是 RootTask 。如果这个 Task 是可见的,则又开始遍历其下的所有 ActivityRecord ,让其执行 test 函数
    1. AttachApplicationHelper::test 方法是真正干活的地方,所有一堆条件判断这个 ActivityRecord 是否满足
    • r.finishing:活动是否正在结束
    • !r.showToCurrentUser() :活动是否对当前用户可见
    • !r.visibleIgnoringKeyguard :活动是否可见,即使有屏幕保护器
    • r.app != null :活动的应用程序是否已经存在
    • mApp.mUid != r.info.applicationInfo.uid :应用程序的UID是否与预期的不同
    • !mApp.mName.equals(r.processName):应用程序的名称是否与活动的进程名称不同
      感觉比较重要的就是前面3个条件,这个 ActivityRecord 是不是需要显示给用户,如果需要则执行 ActivityTaskSupervisor::realStartActivityLocked 试图启动 Activity 。

3. 阶段三总结

阶段三的流程相对来逻辑简单一些,知道个调用链就好,流程目的就是执行 ActivityTaskSupervisor::realStartActivityLocked 。

这部分的堆栈如下图:

在这里插入图片描述

加上应用端的调用链,完成调用链如下:

ActivityThread::mainLooper::prepareMainLooperActivityThread::initActivityThread::attachActivityManagerService::attachApplication  -- 跨进程ActivityManagerService::attachApplicationLockedActivityThread::bindApplicationActivityManagerService::finishAttachApplicationInnerActivityTaskManagerService$LocalService::attachApplicationRootWindowContainer::attachApplicationRootWindowContainer$AttachApplicationHelper::process -- 开始遍历WindowContainer::forAllRootTasks  --- 省略forAllRootTasks等固定堆栈Task::forAllRootTasks  -- 1. 遍历所有root TaskRootWindowContainer$AttachApplicationHelper::accept  -- 1.1 root Task执行acceptWindowContainer::forAllActivities  -- 2. 遍历下面的所有ActivityRecordActivityRecord::forAllActivities RootWindowContainer$AttachApplicationHelper::test  -- 2.1 ActivityRecord执行testActivityTaskSupervisor::realStartActivityLocked  -- 试图启动ActivityLooper::loop

对应时序图:

在这里插入图片描述
大概流程图如下:

在这里插入图片描述
这一阶段主要就是应用进程启动后,试图拉起对应的 Activity ,能不能启动的条件就是没有正在 pause 的 Activity 了。
主要逻辑就是触发 ActivityTaskSupervisor::realStartActivityLocked

4. 阶段四–真正启动Activity

阶段四其实就是触发应用端创建 Activity 。

4.1 realStartActivityLocked

这个方法是要真去触发 Activity 启动的,根据前面的流程图,阶段二,三的最终就是想执行到这个方法里面,来触发 Activity 启动。

这个方法的最终目的就是通过事务执行 LaunchActivityItem 和 PauseActivityItem ,也就是会触发应用端 TargetActivity 启动,并执行生命周期到 onCreate 和 onResume 。

# ActivityTaskSupervisor boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,boolean andResume, boolean checkConfig) throws RemoteException {//  重点* 1. 判断是否执行完了pause if (!mRootWindowContainer.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.// 如果还有Activity没完成pause,则打印日志并returnProtoLog.v(WM_DEBUG_STATES,"realStartActivityLocked: Skipping start of r=%s some activities pausing...",r);return false;}// 重点* 2. 表示ActivityRecord已连接到相应的进程r.setProcess(proc);......// event日志: wm_restart_activity EventLogTags.writeWmRestartActivity(r.mUserId, System.identityHashCode(r),task.mTaskId, r.shortComponentName);......// 重点* 3.1 创建事务,用于Activity启动// Create activity launch transaction.final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.token);final boolean isTransitionForward = r.isTransitionForward();// 获取Activity所在 TaskFragment Tokenfinal IBinder fragmentToken = r.getTaskFragment().getFragmentToken();// 重点* 3.2 将构建的 LaunchActivityItem 添加到 clientTransaction 中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.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),results, newIntents, r.takeOptions(), isTransitionForward,proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken));// 重点* 3.3 设置预期的最终状态为 ResumeActivityItemfinal ActivityLifecycleItem lifecycleItem;if (andResume) {// Resume逻辑,启动走的这。 表示需要执行到onCreatelifecycleItem = ResumeActivityItem.obtain(isTransitionForward);} else {//  Pause 逻辑lifecycleItem = PauseActivityItem.obtain();}clientTransaction.setLifecycleStateRequest(lifecycleItem);// 重点* 3.4 执行事务mService.getLifecycleManager().scheduleTransaction(clientTransaction);......}

根据代码的标注解释:

    1. 根据注释,如果有 Activity 正在 pause 则不允许任何 Activity 启动。换句话说就是想要启动一个 Activity 必须其他的 Activity 需要 pause 的都完成了 pause 流程
    1. 对应上一篇提到的 ActivityRecord::attachedToProcess 方法如果需要返回 true ,则必须在这里执行 setProcess 方法,否则 ActivityRecord 下的 app 变量就是 null
    1. 这里就是开始真正执行启动 Activity 的地方了,是通过事务执行的,分为以下几步
    • 3.1 构建一个事务
    • 3.2 设置 LaunchActivityItem ,这一步会将 Activity 的生命周期执行到 onCreate
    • 3.3 设置 ResumeActivityItem ,这一步会将 Activity 的生命周期执行到 onResume
    • 3.4 执行事务

后续逻辑就是在应用端执行 Activity 的创建,以及生命周期处理了,这部分本篇大概看一遍流程,以分析到 Activity 的创建为止。

4.2 创建Activity

这部分的调用链如下:

LaunchActivityItem::executeActivityThread::handleLaunchActivityActivityThread::performLaunchActivityInstrumentation::newActivity -- 创建ActivityActivity::attach  -- 处理Window相关Window::initWindow::setWindowManagerInstrumentation::callActivityOnCreate -- onCreate流程Activity::performCreateActivity::onCreate  --over

时序图图下:

在这里插入图片描述

接下来看 LaunchActivityItem::execute

# LaunchActivityItem 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, mActivityOptions, mIsForward, mProfilerInfo,client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,mTaskFragmentToken);client.handleLaunchActivity(r, pendingActions, null /* customIntent */);Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}

这里的 client 是 ClientTransactionHandler 类型, 而 ActivityThread 是 ClientTransactionHandler 子类。

重点是这边创建了 ActivityClientRecord ,第一个参数就是我们要找的token

逻辑来到应用进程 ActivityThread

# ActivityThreadpublic Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {......final Activity a = performLaunchActivity(r, customIntent);......}private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {......Activity activity = null;try {// 重点* 1. 通过Instrumentation 反射创建Activityjava.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);......}try {......// 重点* 2. 执行 attach 流程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.activityConfigCallback,r.assistToken, r.shareableActivityToken);......if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {重点* 3. onCreate流程mInstrumentation.callActivityOnCreate(activity, r.state);}}......}

这里有3个重点:

    1. Activity 是通过反射创建的。 到这里其实 Activity 启动流程基本分析完了
    1. 执行 Activity::attach 。这里会触发创建 Window
    1. 触发 Activity::onCreate

Activity 已经创建好了,那就剩下 onCreate 了。

# Instrumentationpublic void callActivityOnCreate(Activity activity, Bundle icicle) {prePerformCreate(activity);// onCreate流程activity.performCreate(icicle);postPerformCreate(activity);}# Activityfinal void performCreate(Bundle icicle) {performCreate(icicle, null);}final void performCreate(Bundle icicle, PersistableBundle persistentState) {......if (persistentState != null) {onCreate(icicle, persistentState);} else {// 执行onCreateonCreate(icicle);}......}

写过应用的都知道,默认都是一个参数的onCreate。

至此,Activity 启动流程分析完毕。

在 ActivityTaskSupervisor::realStartActivityLocked 方法看到给事务加了个 ResumeActivityItem , 因为 LaunchActivityItem 只是创建,但是创建完成后,需要执行到对应的生命周期。

正常情况都是希望执行到onResume,所以会设置 ResumeActivityItem 。

4.3 阶段四小结

在这里插入图片描述
无论是阶段二还是阶段三触发了 ActivityTaskSupervisor::realStartActivityLocked 方法,并且还满足启动 Activity 条件,则会触发应用端进程 Activity 的创建和生命周期的执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/437658.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】C++基础

目录 一. C关键字(C98) 二、C的第一个程序 三、命名空间 3.1.namespace的价值 3.2.namespace的定义 3.2.命名空间使用 总结&#xff1a;在项目当中第一、第二种方法搭配使用&#xff0c;第三种冲突风险非常大&#xff0c;仅适合练习使用。 四、C输入&输出 五、缺省…

python之with

with上下文管理是什么呢&#xff1f; 一般都是使用系统提供的一些with语句&#xff0c;列如我要去读取一些数据进行分析&#xff0c;就可以使用with open去读取某些数据&#xff0c;或者我要把一些图片给他保存到某些地方&#xff0c;可以用with给他写入。 上下午管理器with是…

html5 + css3(上)

目录 HTML认知web标准vscode的简介和使用注释标题和段落换行和水平线标签文本格式化标签图片图片-基本使用图片-属性 绝对路径相对路径音频标签视频标签超链接 HTML基础列表列表-无序和有序列表-自定义 表格表格-使用表格-表格标题和表头单元格表格-结构标签&#xff08;了解&a…

SQL:函数以及约束

目录 介绍 函数 字符串函数 数值函数 日期函数 流程函数 约束 总结 介绍 说到函数我们都不陌生,在C,C,java等语言中都有库函数,我们在平时也是经常使用,函数就是一段代码,我们既可以自定义实现,又可以使用库里内置的函数;从来更加简洁方便的完成业务;同样的在SQL中也有…

五子棋双人对战项目(4)——匹配模块(解读代码)

目录 一、约定前后端交互接口的参数 1、websocket连接路径 2、构造请求、响应对象 二、用户在线状态管理 三、房间管理 1、房间类&#xff1a; 2、房间管理器&#xff1a; 四、匹配器(Matcher) 1、玩家实力划分 2、加入匹配队列&#xff08;add&#xff09; 3、移除…

【C语言指南】数据类型详解(上)——内置类型

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C语言指南》 期待您的关注 目录 引言 1. 整型&#xff08;Integer Types&#xff09; 2. 浮点型&#xff08;Floating-Point …

java发送邮件email实战

1.首先在项目中增加依赖&#xff0c;在pom文件中添加如下坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>2.发邮件工具类如下 package com.example.demo.…

力扣 —— 多数元素 轮转数组

多数元素 题目(简单) 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a; nums [3,2,3] 输…

第24天sql注入(小迪安全学习)

前置知识&#xff08;搭建环境失败&#xff0c;搞不了实验了&#xff0c;学下理论知识吧&#xff09; sql注入 脚本代码在实现代码与数据库进行数据通讯时&#xff08;从数据库中取出相关数据&#xff09;&#xff0c;将定义的SQL语句进行执行查询数据时其中的SQL语句能通过参…

VSOMEIP代码阅读整理(1) - 网卡状态监听

一. 概述 在routing进程所使用的配置文件中&#xff0c;存在如下配置项目&#xff1a;{"unicast" : "192.168.56.101",..."service-discovery" :{"enable" : "true","multicast" : "224.244.224.245",…

Oracle 表空间时间点恢复

已有一个数据库全备&#xff0c;在PDB中恢复被drop掉的表空间 1.新建表空间 create tablespace PITR_TBS datafile /u01/app/oracle/oradata/PRODCDB/PDBPROD2/PITR_TBS01.dbf size 10m; 2.使用RMAN备份. backup as compressed backupset database INCLUDE CURRENT CONTROLFI…

从零开始搭建UVM平台(八)-加入agent

书接上回&#xff1a; 从零开始搭建UVM平台&#xff08;一&#xff09;-只有uvm_driver的验证平台 从零开始搭建UVM平台&#xff08;二&#xff09;-加入factory机制 从零开始搭建UVM平台&#xff08;三&#xff09;-加入objection机制 从零开始搭建UVM平台&#xff08;四&…

Cortex微控制器软件接口标准(CMSIS)

Cortex微控制器软件接口标准 目前&#xff0c;软件开发已经是嵌入式系统行业公认的主要开发成本&#xff0c;通过将所有Cortex-M芯片供应商产品的软件接口标准化&#xff0c;能有效降低这一成本&#xff0c;尤其是进行新产品开发或者将现有项目或软件移植到基于不同厂商MCU的产…

react-问卷星项目(4)

项目实战 使用CSS 尽量不要使用内联CSS 内联style代码多&#xff0c;性能差&#xff0c;扩展性差外链css文件可复用代码&#xff0c;可单独缓存文件 元素内联style 和HTMl元素的style相似必须用JS写法&#xff0c;不能是字符串&#xff0c;里面必须是对象 <span style…

进阶数据库系列(十三):PostgreSQL 分区分表

概述 在组件开发迭代的过程中&#xff0c;随着使用时间的增加&#xff0c;数据库中的数据量也不断增加&#xff0c;因此数据库查询越来越慢。 通常加速数据库的方法很多&#xff0c;如添加特定的索引&#xff0c;将日志目录换到单独的磁盘分区&#xff0c;调整数据库引擎的参…

无人化焦炉四大车系统 武汉正向科技 工业机车无人远程控制系统

焦炉四大车无人化系统介绍 采用格雷母线光编码尺双冗余定位技术&#xff0c;炉门视觉定位自学习技术&#xff0c;wifi5G无线通讯技术&#xff0c;激光雷达安全识别技术&#xff0c;焦化智慧调度&#xff0c;手机APP监控功能。 焦炉四大车无人化系统功能 该系统能自动生成生产…

IDTL:茶叶病害识别数据集(猫脸码客 第205期)

Identifying Disease in Tea Leaves茶叶病害识别数据集 一、引言 在农业领域&#xff0c;茶叶作为一种重要的经济作物&#xff0c;其生产过程中的病害防治是确保茶叶质量和产量的关键环节。然而&#xff0c;传统的病害识别方法主要依赖于人工观察和经验判断&#xff0c;这不仅…

Nature Machine Intelligence 基于强化学习的扑翼无人机机翼应变飞行控制

尽管无人机技术发展迅速&#xff0c;但复制生物飞行的动态控制和风力感应能力&#xff0c;仍然遥不可及。生物学研究表明&#xff0c;昆虫翅膀上有机械感受器&#xff0c;即钟形感受器campaniform sensilla&#xff0c;探测飞行敏捷性至关重要的复杂气动载荷。 近日&#xff0…

STM32引脚PB3、PB4、PA15作为输入输出的特殊配置

一、问题描述 简单描述&#xff1a; 最近做的一个项目中&#xff0c;PB3端口配置为输入&#xff0c;不管外部输入是高电平还是低电平&#xff0c;一直读取到的是低电平。 调试过程&#xff1a;在撰写代码过程中&#xff0c;又发现新的问题&#xff0c;Enter按键无法控制屏幕数…

【Python】ftfy 使用指南:修复 Unicode 编码问题

ftfy&#xff08;fixes text for you&#xff09;是一个专为修复各种文本编码错误而设计的 Python 工具。它的主要目标是将损坏的 Unicode 文本恢复为正确的 Unicode 格式。ftfy 并非用于处理非 Unicode 编码&#xff0c;而是旨在修复因为编码不一致、解码错误或混合编码导致的…