Android11 应用启动流程

应用层调用startActivity,会跨进程调用导致ATMS的startActivityAsUser方法被调用

//frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
private int startActivityAsUser(IApplicationThread caller, String callingPackage,@Nullable String callingFeatureId, Intent intent, String resolvedType,IBinder resultTo, String resultWho, int requestCode, int startFlags,ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {assertPackageMatchesCallingUid(callingPackage);enforceNotIsolatedCaller("startActivityAsUser");userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");// TODO: Switch to user app stacks here.return getActivityStartController().obtainStarter(intent, "startActivityAsUser").setCaller(caller).setCallingPackage(callingPackage).setCallingFeatureId(callingFeatureId).setResolvedType(resolvedType).setResultTo(resultTo).setResultWho(resultWho).setRequestCode(requestCode).setStartFlags(startFlags).setProfilerInfo(profilerInfo).setActivityOptions(bOptions).setUserId(userId).execute();}

getActivityStartController().obtainStarter返回的是一个ActivityStarter对象,先通过各个set方法,将数据保存到其mRequest成员中,最后调用execute方法

//frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
int execute() {//省略res = executeRequest(mRequest);//省略
}

executeRequest

//frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
private int executeRequest(Request request) {//省略final IApplicationThread caller = request.caller;Intent intent = request.intent;//...从request中取出之前通过set保存的值//省略final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,callingPackage, callingFeatureId, intent, resolvedType, aInfo,mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode,request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions,sourceRecord);//省略mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,restrictedBgActivity, intentGrants);}

主要是创建ActivityRecord 并调用startActivityUnchecked方法
startActivityUnchecked

//frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, Task inTask,boolean restrictedBgActivity, NeededUriGrants intentGrants) {int result = START_CANCELED;final ActivityStack startedActivityStack;try {mService.deferWindowLayout();Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);} finally {Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);startedActivityStack = handleStartResult(r, result);mService.continueWindowLayout();}postStartActivityProcessing(r, result, startedActivityStack);return result;}

startActivityInner

//frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, Task inTask,boolean restrictedBgActivity, NeededUriGrants intentGrants) {//省略,主要是一些参数的计算,比如启动模式之类if (mTargetStack == null) {mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, targetTask, mOptions);//创建Task并挂载到层级结构树}if (newTask) {final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)? mSourceRecord.getTask() : null;setNewTask(taskToAffiliate);//将ActivityRecord挂载到上面的Task下//省略} else if (mAddingToTask) {addOrReparentStartingActivity(targetTask, "adding to task");}//省略mTargetStack.startActivityLocked(mStartActivity,topStack != null ? topStack.getTopNonFinishingActivity() : null, newTask,mKeepCurTransition, mOptions);//启动窗口//省略if (mDoResume) {mRootWindowContainer.resumeFocusedStacksTopActivities(mTargetStack, mStartActivity, mOptions);//继续处理}
}

startActivityInner主要完成

  1. 层级结构树节点的创建并挂载
  2. 启动窗口相关
  3. 调用RootWindowContainer的resumeFocusedStacksTopActivities继续处理

resumeFocusedStacksTopActivities

 boolean resumeFocusedStacksTopActivities(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {if (!mStackSupervisor.readyToResume()) {return false;}boolean result = false;if (targetStack != null && (targetStack.isTopStackInDisplayArea()|| getTopDisplayFocusedStack() == targetStack)) {result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);}//省略

ActivityStack的resumeTopActivityUncheckedLocked方法直接调用resumeTopActivityInnerLocked
ActivityStack.resumeTopActivityInnerLocked

//frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options){//省略ActivityRecord next = topRunningActivity(true /* focusableOnly */);//得到需要打开的Activity//省略boolean pausing = taskDisplayArea.pauseBackStacks(userLeaving, next);//调用startPausingLocked去暂停上一个应用//省略if (pausing) {if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,"resumeTopActivityLocked: Skip resume: need to start pausing");// At this point we want to put the upcoming activity's process// at the top of the LRU list, since we know we will be needing it// very soon and it would be a waste to let it get killed if it// happens to be sitting towards the end.if (next.attachedToProcess()) {next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,true /* activityChange */, false /* updateOomAdj */,false /* addPendingTopUid */);} else if (!next.isProcessRunning()) {// Since the start-process is asynchronous, if we already know the process of next// activity isn't running, we can start the process earlier to save the time to wait// for the current activity to be paused.final boolean isTop = this == taskDisplayArea.getFocusedStack();mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,isTop ? "pre-top-activity" : "pre-activity");//开启新进程}//省略
}

resumeTopActivityInnerLocked 首先是要去暂停上一个应用,比如在Launcher上点击图标启动应用,需要暂停Launcher,然后为要启动的应用新建线程。

startPausingLocked
pauseBackStacks会调用到startPausingLocked去暂停应用
startPausingLocked

//frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,ActivityRecord resuming) {//省略if (prev.attachedToProcess()) {if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);try {EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),prev.shortComponentName, "userLeaving=" + userLeaving);mAtmService.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;}//省略
}

Android 11 在AMS和Activity之间的跨进程通讯有变动,现在是采取事物的方式,scheduleTransaction用于传递事务,注意第三个参数,最终导致PauseActivityItem的execute方法被执行

//frameworks/base/core/java/android/app/servertransaction/PauseActivityItem.java
@Overridepublic void execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");android.util.Log.d("test","PauseActivityItem:",new Exception());client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,"PAUSE_ACTIVITY_ITEM");Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}

最终Activity的 去执行onPause这个生命周期。
PauseActivityItem的execute执行完之后,会继续执行其postExecute方法

//frameworks/base/core/java/android/app/servertransaction/PauseActivityItem.java
@Overridepublic void postExecute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {if (mDontReport) {return;}try {// TODO(lifecycler): Use interface callback instead of AMS.ActivityTaskManager.getService().activityPaused(token);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}

ATMS.activityPaused

//frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java@Overridepublic final void activityPaused(IBinder token) {final long origId = Binder.clearCallingIdentity();synchronized (mGlobalLock) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");final ActivityRecord r = ActivityRecord.forTokenLocked(token);if (r != null) {r.activityPaused(false);}Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}Binder.restoreCallingIdentity(origId);}

ActivityRecord.activityPaused

//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
void activityPaused(boolean timeout) {if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,"Activity paused: token=" + appToken + ", timeout=" + timeout);final ActivityStack stack = getStack();if (stack != null) {removePauseTimeout();if (stack.mPausingActivity == this) {if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + this+ (timeout ? " (due to timeout)" : " (pause complete)"));mAtmService.deferWindowLayout();try {stack.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);} finally {mAtmService.continueWindowLayout();}return;} else {//省略}

ActivityStack.completePauseLocked

//frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {//省略if (resumeNext) {final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);} else {checkReadyForSleep();final ActivityRecord top = topStack != null ? topStack.topRunningActivity() : null;if (top == null || (prev != null && top != prev)) {// If there are no more activities available to run, do resume anyway to start// something. Also if the top activity on the stack is not the just paused// activity, we need to go ahead and resume it to ensure we complete an// in-flight app switch.mRootWindowContainer.resumeFocusedStacksTopActivities();}}}//省略
}

可以看出,上一个应用暂停完成之后,又调用到RootWindowContainer的resumeFocusedStacksTopActivities方法。这是因为在启动新进程那条调用链上,如果上一个应用还没暂停,就不会继续往下执行,所以应用暂停完成之后,需要再次调用确保真正启动应用的地方会继续往下执行。

startProcessAsync

关于进程的创建,在Android11 FallbackHome启动和关闭流程分析 一文中,已经分析过,子进程fork之后,ActivityThread的main方法

//frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {//省略Looper.prepareMainLooper();//省略ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);//省略Looper.loop();throw new RuntimeException("Main thread loop 

在attach方法中,调用AMS的attachApplication方法
AMS.attachApplication

//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Overridepublic final void attachApplication(IApplicationThread thread, long startSeq) {if (thread == null) {throw new SecurityException("Invalid application interface");}synchronized (this) {int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();attachApplicationLocked(thread, callingPid, callingUid, startSeq);Binder.restoreCallingIdentity(origId);}}

attachApplicationLocked

//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {//省略thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,null, null, null, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.isPersistent(),new Configuration(app.getWindowProcessController().getConfiguration()),app.compat, getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, autofillOptions, contentCaptureOptions,app.mDisabledCompatChanges);//执行Application的onCreate方法//省略if (normalMode) {try {didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());} catch (Exception e) {Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);badApp = true;}}//省略
}

ActivityTaskManagerService$LocalService.attachApplication

//frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@Overridepublic boolean attachApplication(WindowProcessController wpc) throws RemoteException {synchronized (mGlobalLockWithoutBoost) {if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);}try {return mRootWindowContainer.attachApplication(wpc);} finally {Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}}}

RootWindowContainer.attachApplication

//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
boolean attachApplication(WindowProcessController app) throws RemoteException {boolean didSomething = false;for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {mTmpRemoteException = null;mTmpBoolean = false; // Set to true if an activity was started.final DisplayContent display = getChildAt(displayNdx);for (int areaNdx = display.getTaskDisplayAreaCount() - 1; areaNdx >= 0; --areaNdx) {final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(areaNdx);for (int taskNdx = taskDisplayArea.getStackCount() - 1; taskNdx >= 0; --taskNdx) {final ActivityStack rootTask = taskDisplayArea.getStackAt(taskNdx);if (rootTask.getVisibility(null /*starting*/) == STACK_VISIBILITY_INVISIBLE) {break;}final PooledFunction c = PooledLambda.obtainFunction(RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,PooledLambda.__(ActivityRecord.class), app,rootTask.topRunningActivity());rootTask.forAllActivities(c);//1c.recycle();if (mTmpRemoteException != null) {throw mTmpRemoteException;}}}didSomething |= mTmpBoolean;}if (!didSomething) {ensureActivitiesVisible(null, 0, false /* preserve_windows */);}return didSomething;}

注释1处,对于满足条件的每个Task,都会去执行startActivityForAttachedApplicationIfNeeded方法

//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r,WindowProcessController app, ActivityRecord top) {//省略try {if (mStackSupervisor.realStartActivityLocked(r, app,top == r && r.isFocusable() /*andResume*/, true /*checkConfig*/)) {mTmpBoolean = true;}} catch (RemoteException e) {//省略}return false;}
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,boolean andResume, boolean checkConfig) throws RemoteException {//省略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.if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,"realStartActivityLocked: Skipping start of r=" + r+ " some activities pausing...");return false;}// Create activity launch transaction.final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.appToken);final DisplayContent dc = r.getDisplay().mDisplayContent;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.getSavedState(), r.getPersistentSavedState(), results, newIntents,dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),r.assistToken, r.createFixedRotationAdjustmentsIfNeeded()));// 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);//省略
}

也是采用事物的方式,和应用进程相互通信,最后去执行Activity 的 onCreate,onResume生命周期。

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

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

相关文章

数字信号处理及MATLAB仿真(4)——量化的其他概念

上回书说到AD转换的两个步骤——量化与采样两个步骤。现在更加深入的去了解以下对应的概念。学无止境,要不断地努力才有好的收获。万丈高楼平地起,唯有打好基础,才能踏实前行。 不说了,今天咱们继续说说这两个步骤,首先…

每日刷题(二分图,二分查找,dfs搜索)

目录 1.P3853 [TJOI2007] 路标设置 2.P1129 [ZJOI2007] 矩阵游戏 3.P1330 封锁阳光大学 4.Trees 5.P1141 01迷宫 1.P3853 [TJOI2007] 路标设置 P3853 [TJOI2007] 路标设置 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 先求出每个路标之间的距离,再二分查找每…

新媒体运营都需要掌握哪些技术?沈阳新媒体运营免费培训

新媒体运营需要掌握的技术包括内容创作、FAB产品介绍法、用户运营、社群运营、活动策划和数据分析。这个岗位在现代社会中的重要性日益突出,随着互联网的发展,新媒体已成为人们获取信息的主要渠道之一,而新媒体运营则是通过各种新媒体平台进行…

数据库系统原理练习 | 作业2-第2章关系数据库(附答案)

整理自博主本科《数据库系统原理》专业课完成的课后作业,以便各位学习数据库系统概论的小伙伴们参考、学习。 *文中若存在书写不合理的地方,欢迎各位斧正。 专业课本: 目录 一、选择题 二、填空题 三、简答题 四、关系代数 1.课本p70页&…

hive中reverse函数

目录 前言基本函数介绍实战 前言 reverse函数,是一个常用的字符串处理函数,很多编程语言都有。最近开发中,遇到一个reverse解决的需求,发现自己尚未总结过,遂补上。 基本函数介绍 SELECT reverse(string_column) FR…

虚拟机安装Linux CENTOS 07 部署NET8 踩坑大全

首先下载centos07镜像,建议使用阿里云推荐的地址: https://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/?spma2c6h.25603864.0.0.59b5f5ad5Nfr0X 其实这里就已经出现第一个坑了 centos 07 /usr/lib64/ 的 libstdc.so只支持到19; GLI…

数据湖表格式 Hudi/Iceberg/DeltaLake/Paimon TPCDS 性能对比(Spark 引擎)

当前,业界流行的集中数据湖表格式 Hudi/Iceberg/DeltaLake,和最近出现并且在国内比较火的 Paimon。我们现在看到的很多是针对流处理场景的读写性能测试,那么本篇文章我们将回归到大数据最基础的场景,对海量数据的批处理查询。本文…

Java中的线程同步机制有哪些?

Java中的线程同步机制是一套用于协调线程间的数据访问及活动的机制,该机制用于保障线程安全以及实现这些线程的共同目标。Java平台提供的线程同步机制主要包括以下几个方面: 1. 锁(Lock) 锁是Java中最基本的线程同步机制之一&am…

飞书、钉钉、企业微信的大模型“三国杀”

文:互联网江湖 作者:刘致呈 曾经在一次内部的周年会上,字节跳动CEO梁汝波曾表示对飞书和火山引擎的研发投入不低于抖音和TikTok。言下之意,飞书在字节内部的重要性比肩抖音。 业务的重要性从时间上也看得出来,要知道…

静态时序分析:Leaf Cell(叶单元)

相关阅读​​​​​​​静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html 在DC中,leaf cell(叶单元)有时会出现在描述中,例如set_input_delay的-reference_pin选项的参数,就必须是一个端口或…

C# Winform之propertyGrid控件使用详解和分组设置

PropertyGrid 控件在 WinForms 中是一个非常有用的工具,它允许用户查看和编辑一个对象的属性。这个控件非常适合用于配置对话框或任何需要动态显示对象属性的地方。下面我会详细介绍 PropertyGrid 的使用方法和如何对属性进行分组。 使用详解 1. 添加 PropertyGri…

《昇思25天学习打卡营第18天|onereal》

RNN实现情感分类 概述 情感分类是自然语言处理中的经典任务,是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型,实现如下的效果: 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This film…

AI版Siri要明年见,研究表明ChatGPT暂无法取代程序员,Kimi推出浏览器插件

ChatGPT狂飙160天,世界已经不是之前的样子。 更多资源欢迎关注 根据彭博社记者马克古尔曼的最新消息,苹果公司今年不会推出全新的Apple Intelligence驱动的Siri,该公司计划在明年1月开始测试,并在iOS 18.4中才推出正式版本。 此前…

景联文科技以高质量多模态数据集赋能AI大模型,精准匹配提升模型性能

在人工智能的浪潮中,语料数据如同建筑的基石,其质量、规模和运用策略直接决定了AI模型的表现和应用的广泛性。 景联文科技在AI领域深耕多年,打磨了高质量多模态数据集,致力于为不同训练阶段的算法精准匹配高质量数据资源。 3000万…

STM32中断(NVIC和EXIT)

CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240个外部中断,并且具有 256 级的可编程中断设置。但STM32 并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有 76 个中断,包括16 个内核中断和 60 个可屏蔽中断&am…

Dify中的RAG和知识库

一.RAG 基本架构 当用户提问 “美国总统是谁?” 时,系统并不是将问题直接交给大模型来回答,而是先将用户问题在知识库中进行向量搜索,通过语义相似度匹配的方式查询到相关的内容(拜登是美国现任第46届总统…&#xff0…

对比多种方法执行命令行命令

在这两种方法中,一种是使用argparse模块来模拟命令行参数的解析,另一种是使用subprocess模块来直接执行一个命令行命令。下面是对两种方法的详细比较: 使用argparse模拟命令行参数 这种方法主要用于在Python脚本内部测试或集成其他使用argp…

深入剖析C++的 “属性“(Attribute specifier sequence)

引言 在阅读开源项目源代码是,发现了一个有趣且特殊的C特性:属性。 属性(attribute specifier sequences)是在C11标准引入的。在C11之前,编译器特有的扩展被广泛用来提供额外的代码信息。例如,GNU编译器&…

AcWing 3587:连通图 ← dfs(邻接矩阵 or 链式前向星)

【题目来源】https://www.acwing.com/problem/content/3590/【题目描述】 给定一个无向图和其中的所有边,判断这个图是否所有顶点都是连通的。【输入格式】 输入包含若干组数据。 每组数据第一行包含两个整数 n 和 m,表示无向图的点和边数。 接下来 m 行…

Java面试题系列 - 第5天

题目:Java Lambda表达式与Stream API的深度应用 背景说明:Java 8引入了Lambda表达式和Stream API,极大地提升了Java函数式编程的能力,使代码更简洁、更易读。掌握Lambda表达式和Stream API的使用,对于优化数据处理流程…