安卓Launcher启动详解

目录

  • 一、概述
    • 1.1 核心源码
    • 1.2 主要代码作用
  • 二、源码分析
    • 2.1 第一阶段SystemServer 启动HomeActivity的调用阶段
    • 2.2 [RootActivityContainer.java] startHomeOnDisplay()
      • 2.2.1 [ActivityTaskManagerService.java] getHomeIntent()
      • 2.2.2 [RootActivityContainer.java] resolveHomeActivity()
    • 2.3 [ActivityStartController.java ] startHomeActivity()
      • 2.3.1 [ActivityStarter.java] execute()
      • 2.3.2 [ActivityStarter.java] startActivity()
      • 2.3.3 [RootActivityContainer.java] resumeFocusedStacksTopActivities()
      • 2.3.4 [ActivityStackSupervisor.java] startSpecificActivityLocked()
      • 2.3.5 [ActivityManagerService.java] startProcess()
      • 2.3.6 [ZygoteProcess.java] attemptZygoteSendArgsAndGetResult()
    • 2.4 第二阶段Zygote fork一个Launcher进程的阶段
      • 2.4.1 [ZygoteInit.java] main()
      • 2.4.2 [ZygoteConnection.java] processOneCommand()
      • 2.4.3 [ZygoteConnection.java] handleChildProc()
    • 2.5 第三个阶段,Launcher在自己的进程中进行onCreate等后面的动作
      • 2.5.1 [ActivityThread.java] main()
      • 2.5.2 [ActivityManagerService.java] attachApplication()
      • 2.5.3 [ActivityStackSupervisor.java] realStartActivityLocked()
      • 2.5.4 [TransactionExecutor.java] execute()
      • 2.5.5 [ActivityThread.java] handleLaunchActivity()
      • 2.5.6 [ActivityThread.java] performLaunchActivity()
  • 三、总结
  • 四、相关日志


一、概述

在Android的中,桌面应用Launcher由Launcher演变到Launcher2,再到现在的Launcher3,Google也做了很多改动。

Launcher不支持桌面小工具动画效果,Launcher2添加了动画效果和3D初步效果支持,从Android 4.4 (KK)开始Launcher默认使用Launcher3, Launcher3加入了透明状态栏,增加overview模式,可以调整workspace上页面的前后顺序,可以动态管理屏幕数量,widget列表与app list分开显示等功能。

本文主要研究Launcher3的启动过程。

1.1 核心源码

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
/frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
/frameworks/base/core/java/com/android/internal/os/Zygote.java
/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
/frameworks/base/services/java/com/android/server/SystemServer.java
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
/frameworks/base/services/core/java/com/android/server/am/ProcessList.java
/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
/frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java
/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
/frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
/frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
/frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java
/frameworks/base/services/core/java/com/android/server/wm/ClientLifecycleManager.java
/frameworks/base/core/java/android/os/Process.java
/frameworks/base/core/java/android/os/ZygoteProcess.java
/frameworks/base/core/java/android/app/ActivityThread.java
/frameworks/base/core/java/android/app/Activity.java
/frameworks/base/core/java/android/app/ActivityManagerInternal.java
/frameworks/base/core/java/android/app/servertransaction/ClientTransaction.java
/frameworks/base/core/java/android/app/servertransaction/ClientTransaction.aidl
/frameworks/base/core/java/android/app/ClientTransactionHandler.java
/frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java
/frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java
/frameworks/base/core/java/android/app/Instrumentation.java
/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

从上面的代码路径可以看出,Android10.0中 Activity的相关功能被放到了wm的目录中,在Android9.0中是在am目录中,Google 最终的目的是把activity 和window融合,在Android10中只是做了简单的代码路径的变更,正在的功能还要到后面的版本才能慢慢融合。

1.2 主要代码作用

名称作用
Instrumentation负责调用Activity和Application生命周期。
ActivityTaskManagerService负责Activity管理和调度等工作。ATM是Android10中新增内容
ActivityManagerService负责管理四大组件和进程,包括生命周期和状态切换。
ActivityTaskManagerInternal是由ActivityTaskManagerService对外提供的一个抽象类,真正的实现是在 ActivityTaskManagerService#LocalService
ActivityThread管理应用程序进程中主线程的执行
ActivityStackSupervisor负责所有Activity栈的管理
TransactionExecutor主要作用是执行ClientTransaction
ClientLifecycleManager生命周期的管理调用

二、源码分析

上一节在AMS启动过程中,知道了AMS启动完成前,在systemReady()中会去调用startHomeOnAllDisplays()来启动Launcher,本次就从startHomeOnAllDisplays()函数入口,来看看Launcher是如何被启动起来的。

[ActivityManagerService.java]

public void systemReady(final Runnable goingCallback, TimingsTraceLog 
traceLog) {...//启动Home ActivitymAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");...
}

Launcher的启动由三部分启动:

SystemServer完成启动Launcher Activity的调用
Zygote()进行Launcher进程的Fork操作
进入ActivityThread的main(),完成最终Launcher的onCreate操作

接下来分别从源码部分来分析这三个启动过程。

2.1 第一阶段SystemServer 启动HomeActivity的调用阶段

调用栈

[ActivityTaskManagerService.java] startHomeOnAllDisplays()

说明:ActivityTaskManagerInternal是 ActivityTaskManagerService的一个抽象类,正在的实现是在ActivityTaskManagerService的LocalService,所以mAtmInternal.startHomeOnAllDisplays()最终调用的是ActivityTaskManagerService的startHomeOnAllDisplays()方法

源码

public boolean startHomeOnAllDisplays(int userId, String reason) {synchronized (mGlobalLock) {//一路调用到 RootActivityContainer 的startHomeOnDisplay()方法,参考[2.2]return mRootActivityContainer.startHomeOnAllDisplays(userId, reason);}
}

2.2 [RootActivityContainer.java] startHomeOnDisplay()

说明:在[2.1]中,获取的displayId为DEFAULT_DISPLAY, 首先通过getHomeIntent 来构建一个category为CATEGORY_HOME的Intent,表明是Home Activity;然后通过resolveHomeActivity()从系统所用已安装的引用中,找到一个符合HomeItent的Activity,最终调用startHomeActivity()来启动Activity

源码

boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,boolean fromHomeKey) {...if (displayId == DEFAULT_DISPLAY) {//构建一个category为CATEGORY_HOME的Intent,表明是Home Activity,参考[2.2.1]homeIntent = mService.getHomeIntent();//通过PKMS从系统所用已安装的引用中,找到一个符合HomeItent的Activity参考[2.2.2]aInfo = resolveHomeActivity(userId, homeIntent); } ...//启动Home Activity,参考[2.3]mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,displayId);return true;
}

2.2.1 [ActivityTaskManagerService.java] getHomeIntent()

说明:构建一个category为CATEGORY_HOME的Intent,表明是Home Activity。

Intent.CATEGORY_HOME = "android.intent.category.HOME"

这个category会在Launcher3的 AndroidManifest.xml中配置,表明是Home Acivity

源码

Intent getHomeIntent() {Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);intent.setComponent(mTopComponent);intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);//不是生产模式,add一个CATEGORY_HOMEif (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {intent.addCategory(Intent.CATEGORY_HOME);}return intent;
}

2.2.2 [RootActivityContainer.java] resolveHomeActivity()

说明:通过Binder跨进程通知PackageManagerService从系统所用已安装的引用中,找到一个符合HomeItent的Activity。

源码

ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {final int flags = ActivityManagerService.STOCK_PM_FLAGS;final ComponentName comp = homeIntent.getComponent(); //系统正常启动时,component为nullActivityInfo aInfo = null;...if (comp != null) {// Factory test.aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);} else {//系统正常启动时,走该流程final String resolvedType =homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());//resolveIntent做了两件事:1.通过queryIntentActivities来查找符合HomeIntent需求Activities//            2.通过chooseBestActivity找到最符合Intent需求的Activity信息final ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(homeIntent, resolvedType, flags, userId);if (info != null) {aInfo = info.activityInfo;}}...aInfo = new ActivityInfo(aInfo);aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);return aInfo;
}

2.3 [ActivityStartController.java ] startHomeActivity()

说明:正在的启动Home Activity入口。obtainStarter() 方法返回的是 ActivityStarter 对象,它负责 Activity 的启动,一系列 setXXX() 方法传入启动所需的各种参数,最后的 execute() 是真正的启动逻辑。另外如果home activity处于顶层的resume activity中,则Home Activity 将被初始化,但不会被恢复。并将保持这种状态,直到有东西再次触发它。需要进行另一次恢复。

源码

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {....//返回一个 ActivityStarter 对象,它负责 Activity 的启动//一系列 setXXX() 方法传入启动所需的各种参数,最后的 execute() 是真正的启动逻辑//最后执行 ActivityStarter的execute方法mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason).setOutActivity(tmpOutRecord).setCallingUid(0).setActivityInfo(aInfo).setActivityOptions(options.toBundle()).execute();  //参考[2.3.1]mLastHomeActivityStartRecord = tmpOutRecord[0];final ActivityDisplay display =mService.mRootActivityContainer.getActivityDisplay(displayId);final ActivityStack homeStack = display != null ? display.getHomeStack() : null;if (homeStack != null && homeStack.mInResumeTopActivity) {//如果home activity 处于顶层的resume activity中,则Home Activity 将被初始化,但不会被恢复(以避免递归恢复),//并将保持这种状态,直到有东西再次触发它。需要进行另一次恢复。mSupervisor.scheduleResumeTopActivities();}
}

2.3.1 [ActivityStarter.java] execute()

说明:在[2.3]中obtainStarter没有调用setMayWait的方法,因此mRequest.mayWait为false,走startActivity流程。

源码

int execute() {...if (mRequest.mayWait) {return startActivityMayWait(...)} else {return startActivity(...) //参考[2.3.2]}...
}

2.3.2 [ActivityStarter.java] startActivity()

说明:延时布局,然后通过startActivityUnchecked()来处理启动标记 flag ,要启动的任务栈等,最后恢复布局。

源码

private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,ActivityRecord[] outActivity, boolean restrictedBgActivity) {...try {//延时布局mService.mWindowManager.deferSurfaceLayout();//调用 startActivityUnchecked ,一路调用到resumeFocusedStacksTopActivities(),参考[2.3.4]result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);} finally {//恢复布局mService.mWindowManager.continueSurfaceLayout();}...
}

2.3.3 [RootActivityContainer.java] resumeFocusedStacksTopActivities()

说明:获取栈顶的Activity,恢复它

源码

boolean resumeFocusedStacksTopActivities(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {...//如果秒表栈就是栈顶Activity,启动resumeTopActivityUncheckedLocked()if (targetStack != null && (targetStack.isTopStackOnDisplay()|| getTopDisplayFocusedStack() == targetStack)) {result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);...if (!resumedOnDisplay) {// 获取  栈顶的 ActivityRecordfinal ActivityStack focusedStack = display.getFocusedStack();if (focusedStack != null) {//最终调用startSpecificActivityLocked(),参考[2.3.4]focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);}}}
}

2.3.4 [ActivityStackSupervisor.java] startSpecificActivityLocked()

说明:发布消息以启动进程,以避免在ATM锁保持的情况下调用AMS时可能出现死锁,最终调用到ATM的startProcess()。

源码

void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {...//发布消息以启动进程,以避免在ATM锁保持的情况下调用AMS时可能出现死锁//最终调用到AMS的startProcess(),参考[2.3.5]final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());mService.mH.sendMessage(msg);...
}

2.3.5 [ActivityManagerService.java] startProcess()

说明:一路调用Process start(),最终到ZygoteProcess的attemptUsapSendArgsAndGetResult(),用来fork一个新的Launcher的进程。

源码

public void startProcess(String processName, ApplicationInfo info,boolean knownToBeDead, String hostingType, ComponentName hostingName) {..//同步操作,避免死锁synchronized (ActivityManagerService.this) {//调用startProcessLocked,然后到 Process的start,最终到ZygoteProcess的attemptUsapSendArgsAndGetResult()//用来fork一个新的Launcher的进程,参考[2.3.6]startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,new HostingRecord(hostingType, hostingName),false /* allowWhileBooting */, false /* isolated */,true /* keepIfLarge */);}...
}

2.3.6 [ZygoteProcess.java] attemptZygoteSendArgsAndGetResult()

说明:通过Socket连接Zygote进程,把之前组装的msg发给Zygote,其中processClass =“android.app.ActivityThread”,通过Zygote进程来Fork出一个新的进程,并执行 "android.app.ActivityThread"的main方法。

源码

private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {try {//传入的zygoteState为openZygoteSocketIfNeeded(),里面会通过abi来检查是第一个zygote还是第二个final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;zygoteWriter.write(msgStr);  //把应用进程的一些参数写给前面连接的zygote进程,包括前面的processClass ="android.app.ActivityThread"zygoteWriter.flush(); //进入Zygote进程,处于阻塞状态, 参考[2.4]//从socket中得到zygote创建的应用pid,赋值给 ProcessStartResult的对象Process.ProcessStartResult result = new Process.ProcessStartResult();result.pid = zygoteInputStream.readInt();result.usingWrapper = zygoteInputStream.readBoolean();if (result.pid < 0) {throw new ZygoteStartFailedEx("fork() failed");}return result;} catch (IOException ex) {zygoteState.close();Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "+ ex.toString());throw new ZygoteStartFailedEx(ex);}
}

2.4 第二阶段Zygote fork一个Launcher进程的阶段

说明:Zygote的启动过程前面有详细讲到过。SystemServer的AMS服务向启动Home Activity发起一个fork请求,Zygote进程通过Linux的fork函数,孵化出一个新的进程。

由于Zygote进程在启动时会创建Java虚拟机,因此通过fork而创建的Launcher程序进程可以在内部获取一个Java虚拟机的实例拷贝。fork采用copy-on-write机制,有些类如果不做改变,甚至都不用复制,子进程可以和父进程共享这部分数据,从而省去不少内存的占用。

2.4.1 [ZygoteInit.java] main()

说明:Zygote先fork出SystemServer进程,接着进入循环等待,用来接收Socket发来的消息,用来fork出其他应用进程,比如Launcher。

源码

public static void main(String argv[]) {...Runnable caller;....if (startSystemServer) {//Zygote Fork出的第一个进程 SystmeServerRunnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);if (r != null) {r.run();return;}}...//循环等待fork出其他的应用进程,比如Launcher//最终通过调用processOneCommand()来进行进程的处理,参考[2.4.2]caller = zygoteServer.runSelectLoop(abiList);...if (caller != null) {caller.run(); //执行返回的Runnable对象,进入子进程}
}

2.4.2 [ZygoteConnection.java] processOneCommand()

说明:通过forkAndSpecialize()来fork出Launcher的子进程,并执行handleChildProc,进入子进程的处理。

源码

Runnable processOneCommand(ZygoteServer zygoteServer) {int pid = -1;...//Fork子进程,得到一个新的pid/fork子进程,采用copy on write方式,这里执行一次,会返回两次///pid=0 表示Zygote  fork子进程成功//pid > 0 表示子进程 的真正的PIDpid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion);...if (pid == 0) {// in child, fork成功,第一次返回的pid = 0...//参考[2.4.3]return handleChildProc(parsedArgs, descriptors, childPipeFd,parsedArgs.mStartChildZygote);} else {//in parent...childPipeFd = null;handleParentProc(pid, descriptors, serverPipeFd);return null;}
}

2.4.3 [ZygoteConnection.java] handleChildProc()

说明:进行子进程的操作,最终获得需要执行的ActivityThread的main()。

源码

private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,FileDescriptor pipeFd, boolean isZygote) {...if (parsedArgs.mInvokeWith != null) {...throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");} else {if (!isZygote) {// App进程将会调用到这里,执行目标类的main()方法return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,parsedArgs.mRemainingArgs, null /* classLoader */);} else {return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,parsedArgs.mRemainingArgs, null /* classLoader */);}}
}

zygoteInit 进行一些环境的初始化、启动Binder进程等操作:

public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,ClassLoader classLoader) {RuntimeInit.commonInit(); //初始化运行环境 ZygoteInit.nativeZygoteInit(); //启动Binder线程池 //调用程序入口函数  return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

把之前传来的"android.app.ActivityThread" 传递给findStaticMain:

protected static Runnable applicationInit(int targetSdkVersion, String[] argv,ClassLoader classLoader) {...// startClass: 如果AMS通过socket传递过来的是 ActivityThreadreturn findStaticMain(args.startClass, args.startArgs, classLoader);
}

通过反射,拿到ActivityThread的main()方法:

protected static Runnable findStaticMain(String className, String[] argv,ClassLoader classLoader) {Class<?> cl;try {cl = Class.forName(className, true, classLoader);} catch (ClassNotFoundException ex) {throw new RuntimeException("Missing class when invoking static main " + className,ex);}Method m;try {m = cl.getMethod("main", new Class[] { String[].class });} catch (NoSuchMethodException ex) {throw new RuntimeException("Missing static main on " + className, ex);} catch (SecurityException ex) {throw new RuntimeException("Problem getting static main on " + className, ex);}int modifiers = m.getModifiers();if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {throw new RuntimeException("Main method is not public and static on " + className);}return new MethodAndArgsCaller(m, argv);
}

把反射得来的ActivityThread main()入口返回给ZygoteInit的main,通过caller.run()进行调用:

static class MethodAndArgsCaller implements Runnable {/** method to call */private final Method mMethod;/** argument array */private final String[] mArgs;public MethodAndArgsCaller(Method method, String[] args) {mMethod = method;mArgs = args;}//调用ActivityThread的main()public void run() {try {mMethod.invoke(null, new Object[] { mArgs });} catch (IllegalAccessException ex) {throw new RuntimeException(ex);} catch (InvocationTargetException ex) {Throwable cause = ex.getCause();if (cause instanceof RuntimeException) {throw (RuntimeException) cause;} else if (cause instanceof Error) {throw (Error) cause;}throw new RuntimeException(ex);}}
}

2.5 第三个阶段,Launcher在自己的进程中进行onCreate等后面的动作

从[2.4]可以看到,Zygote fork出了Launcher的进程,并把接下来的Launcher启动任务交给了ActivityThread来进行,接下来就从ActivityThread main()来分析Launcher的创建过程。

2.5.1 [ActivityThread.java] main()

说明:主线程处理, 创建ActivityThread对象,调用attach进行处理,最终进入Looper循环

源码

public static void main(String[] args) {// 安装选择性的系统调用拦截AndroidOs.install();...//主线程处理Looper.prepareMainLooper();...//之前SystemServer调用attach传入的是true,这里到应用进程传入false就行ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);...//一直循环,如果退出,说明程序关闭Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

调用ActivityThread的attach进行处理

private void attach(boolean system, long startSeq) {sCurrentActivityThread = this;mSystemThread = system;if (!system) {//应用进程启动,走该流程...RuntimeInit.setApplicationObject(mAppThread.asBinder());//获取AMS的本地代理类final IActivityManager mgr = ActivityManager.getService();try {//通过Binder调用AMS的attachApplication方法,参考[2.5.2]mgr.attachApplication(mAppThread, startSeq);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}...} else {//通过system_server启动ActivityThread对象...}// 为 ViewRootImpl 设置配置更新回调,当系统资源配置(如:系统字体)发生变化时,通知系统配置发生变化ViewRootImpl.ConfigChangedCallback configChangedCallback= (Configuration globalConfig) -> {synchronized (mResourcesManager) {...}};ViewRootImpl.addConfigCallback(configChangedCallback);
}

2.5.2 [ActivityManagerService.java] attachApplication()

说明:清除一些无用的记录,最终调用ActivityStackSupervisor.java的 realStartActivityLocked(),进行Activity的启动

源码

public final void attachApplication(IApplicationThread thread, long startSeq) {synchronized (this) {//通过Binder获取传入的pid信息int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();attachApplicationLocked(thread, callingPid, callingUid, startSeq);Binder.restoreCallingIdentity(origId);}
}
private final boolean attachApplicationLocked(IApplicationThread thread,int pid, int callingUid, long startSeq) {...//如果当前的Application记录仍然依附到之前的进程中,则清理掉if (app.thread != null) {handleAppDiedLocked(app, true, true);}·//mProcessesReady这个变量在AMS的 systemReady 中被赋值为true,//所以这里的normalMode也为trueboolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);...//上面说到,这里为true,进入StackSupervisor的attachApplication方法//去真正启动Activityif (normalMode) {...//调用ATM的attachApplication(),最终层层调用到ActivityStackSupervisor.java的 realStartActivityLocked()//参考[2.5.3]didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());...}...return true;
}

2.5.3 [ActivityStackSupervisor.java] realStartActivityLocked()

说明:真正准备去启动Activity,通过clientTransaction.addCallback把LaunchActivityItem的obtain作为回调参数加进去,再调用ClientLifecycleManager.scheduleTransaction()得到LaunchActivityItem的execute()方法进行最终的执行。

源码:

boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,boolean andResume, boolean checkConfig) throws RemoteException {// 直到所有的 onPause() 执行结束才会去启动新的 activityif (!mRootActivityContainer.allPausedActivitiesComplete()) {...return false;}try {// Create activity launch transaction.// 添加 LaunchActivityItemfinal ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.appToken);//LaunchActivityItem.obtain(new Intent(r.intent)作为回调参数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));...// 设置生命周期状态final ActivityLifecycleItem lifecycleItem;if (andResume) {lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());} else {lifecycleItem = PauseActivityItem.obtain();}clientTransaction.setLifecycleStateRequest(lifecycleItem);// Schedule transaction.// 重点关注:调用 ClientLifecycleManager.scheduleTransaction(),得到上面addCallback的LaunchActivityItem的execute()方法//参考[2.5.4]mService.getLifecycleManager().scheduleTransaction(clientTransaction);} catch (RemoteException e) {if (r.launchFailed) {// 第二次启动失败,finish activitystack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,"2nd-crash", false);return false;}// 第一次失败,重启进程并重试r.launchFailed = true;proc.removeActivity(r);throw e;}} finally {endDeferResume();}...return true;
}

2.5.4 [TransactionExecutor.java] execute()

说明:执行之前realStartActivityLocked()中的clientTransaction.addCallback

源码: [TransactionExecutor.java]

public void execute(ClientTransaction transaction) {...// 执行 callBack,参考上面的调用栈,执行回调方法,//最终调用到ActivityThread的handleLaunchActivity()参考[2.5.5]executeCallbacks(transaction);// 执行生命周期状态executeLifecycleState(transaction);mPendingActions.clear();
}

2.5.5 [ActivityThread.java] handleLaunchActivity()

说明:主要干了两件事,第一件:初始化WindowManagerGlobal;第二件:调用performLaunchActivity方法

源码:[ActivityThread.java]

public Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {...//初始化WindowManagerGlobalWindowManagerGlobal.initialize();...//调用performLaunchActivity,来处理Activity,参考[2.5.6]final Activity a = performLaunchActivity(r, customIntent);..return a;
}

2.5.6 [ActivityThread.java] performLaunchActivity()

说明:获取ComponentName、Context,反射创建Activity,设置Activity的一些内容,比如主题等; 最终调用callActivityOnCreate()来执行Activity的onCreate()方法

源码

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {// 获取 ComponentNameComponentName component = r.intent.getComponent();...// 获取 ContextContextImpl appContext = createBaseContextForActivity(r);Activity activity = null;try {// 反射创建 Activityjava.lang.ClassLoader cl = appContext.getClassLoader();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 {// 获取 ApplicationApplication app = r.packageInfo.makeApplication(false, mInstrumentation);if (activity != null) {...//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);if (customIntent != null) {activity.mIntent = customIntent;}...int theme = r.activityInfo.getThemeResource();if (theme != 0) {// 设置主题activity.setTheme(theme);}activity.mCalled = false;// 执行 onCreate()if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {mInstrumentation.callActivityOnCreate(activity, r.state);}...r.activity = activity;}//当前状态为ON_CREATEr.setState(ON_CREATE);...} catch (SuperNotCalledException e) {throw e;} catch (Exception e) {...}return activity;
}

callActivityOnCreate先执行activity onCreate的预处理,再去调用Activity的onCreate,最终完成Create创建后的内容处理

public void callActivityOnCreate(Activity activity, Bundle icicle,PersistableBundle persistentState) {prePerformCreate(activity); //activity onCreate的预处理activity.performCreate(icicle, persistentState);//执行onCreate()postPerformCreate(activity); //activity onCreate创建后的一些信息处理
}

performCreate()主要调用Activity的onCreate()

final void performCreate(Bundle icicle, PersistableBundle persistentState) {...if (persistentState != null) {onCreate(icicle, persistentState);} else {onCreate(icicle);}...
}

至此,看到了最熟悉的Activity的onCreate(),Launcher的启动完成,Launcher被真正创建起来。


三、总结

看到onCreate()后,进入到最熟悉的Activity的入口,Launcher的启动告一段落。整个Android的启动流程完整的分析完成。

Launcher的启动经过了三个阶段:

第一个阶段:SystemServer完成启动Launcher Activity的调用
第二个阶段:Zygote()进行Launcher进程的Fork操作
第三个阶段:进入ActivityThread的main(),完成最终Launcher的onCreate操作


四、相关日志

开机启动流程中关于Launcher关键字的log。

6027: 05-08 18:46:58.502  1504  1504 I SystemServer: StartGestureLauncher行  6028: 05-08 18:46:58.502  1504  1504 I SystemServiceManager: Starting com.android.server.GestureLauncherService行  6029: 05-08 18:46:58.502  1504  1504 D SystemServerTiming: StartGestureLauncher took to complete: 0ms行  6089: 05-08 18:46:58.528  1504  1504 I SystemServer: StartLauncherAppsService行  6090: 05-08 18:46:58.528  1504  1504 I SystemServiceManager: Starting com.android.server.pm.LauncherAppsService行  6091: 05-08 18:46:58.529  1504  1504 D SystemServerTiming: StartLauncherAppsService took to complete: 1ms行  6891: 05-08 18:46:59.180  1504  1504 D SystemServiceManager: onStartUser-0 com.android.server.GestureLauncherService took to complete: 0ms行  6912: 05-08 18:46:59.182  1504  1504 D SystemServiceManager: onStartUser-0 com.android.server.pm.LauncherAppsService took to complete: 0ms行  7848: 05-08 18:46:59.809   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: bindService is called for service : com.android.launcher3/com.android.quickstep.TouchInteractionService and for client com.android.systemui行  7850: 05-08 18:46:59.809   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: total connections for service : com.android.launcher3/com.android.quickstep.TouchInteractionServiceare :17879: 05-08 18:46:59.844  1504  1649 I am_proc_start: [0,2435,10112,com.android.launcher3,service,{com.android.launcher3/com.android.quickstep.TouchInteractionService}]7880: 05-08 18:46:59.844  1504  1649 I ActivityManager: Start proc 2435:com.android.launcher3/u0a112 for service {com.android.launcher3/com.android.quickstep.TouchInteractionService}7931: 05-08 18:46:59.905  1504  2231 I am_proc_bound: [0,2435,com.android.launcher3]7947: 05-08 18:46:59.912   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: startService() is called for servicecom.android.launcher3/com.android.quickstep.TouchInteractionService行  8237: 05-08 18:47:00.075  2435  2435 W ContextImpl: Failed to ensure /data/user/0/com.android.launcher3/cache: mkdir failed: ENOENT (No such file or directory)8368: 05-08 18:47:00.140  1504  1504 V NotificationListeners: enabling notification listener for 0: ComponentInfo{com.android.launcher3/com.android.launcher3.notification.NotificationListener}8370: 05-08 18:47:00.141  1504  1504 V NotificationListeners: binding: Intent { act=android.service.notification.NotificationListenerService cmp=com.android.launcher3/.notification.NotificationListener (has extras) }8371: 05-08 18:47:00.141  1504  1504 W ActivityManager: Unable to start service Intent { act=android.service.notification.NotificationListenerService cmp=com.android.launcher3/.notification.NotificationListener } U=0: not found行  8372: 05-08 18:47:00.141  1504  1504 W NotificationListeners: Unable to bind notification listener service: Intent { act=android.service.notification.NotificationListenerService cmp=com.android.launcher3/.notification.NotificationListener (has extras) } in user 013479: 05-08 18:47:03.025  1504  1648 D SystemServiceManager: onUnlockUser-0 com.android.server.GestureLauncherService took to complete: 0ms行 13491: 05-08 18:47:03.026  1504  1648 D SystemServiceManager: onUnlockUser-0 com.android.server.pm.LauncherAppsService took to complete: 0ms行 13593: 05-08 18:47:03.068  1504  1648 V ActivityManager: Installing ContentProviderInfo{name=com.android.launcher3 className=com.android.quickstep.LauncherSearchIndexablesProvider}13594: 05-08 18:47:03.068  1504  1648 V ActivityManager: Installing ContentProviderInfo{name=com.android.launcher3.settings className=com.android.launcher3.LauncherProvider}13734: 05-08 18:47:03.171  1504  3109 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.Launcher} from uid 013771: 05-08 18:47:03.228  1504  3109 I am_create_activity: [0,91065182,970,com.android.launcher3/.Launcher,android.intent.action.MAIN,NULL,NULL,268435712]13800: 05-08 18:47:03.246  1504  1504 V NotificationListeners: enabling notification listener for 0: ComponentInfo{com.android.launcher3/com.android.launcher3.notification.NotificationListener}13801: 05-08 18:47:03.246  1504  1504 V NotificationListeners: binding: Intent { act=android.service.notification.NotificationListenerService cmp=com.android.launcher3/.notification.NotificationListener (has extras) }13811: 05-08 18:47:03.249   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: unbindService is called for service : com.android.launcher3/com.android.quickstep.TouchInteractionService and for client com.android.systemui行 13812: 05-08 18:47:03.249   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: size of service connections for service: com.android.launcher3/com.android.quickstep.TouchInteractionServiceafter removal is 013814: 05-08 18:47:03.250   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: destroyService is called for service : com.android.launcher3/com.android.quickstep.TouchInteractionService行 13867: 05-08 18:47:03.271   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: bindService is called for service : com.android.launcher3/.notification.NotificationListener and for client system行 13868: 05-08 18:47:03.271   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: total connections for service : com.android.launcher3/.notification.NotificationListenerare :113877: 05-08 18:47:03.277   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: startService() is called for servicecom.android.launcher3/.notification.NotificationListener行 13887: 05-08 18:47:03.283   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: bindService is called for service : com.android.launcher3/com.android.quickstep.TouchInteractionService and for client com.android.systemui行 13888: 05-08 18:47:03.284   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: total connections for service : com.android.launcher3/com.android.quickstep.TouchInteractionServiceare :113890: 05-08 18:47:03.284  1504  1632 I ActivityTaskManager: The Process com.android.launcher3 Already Exists in BG. So sending its PID: 243513892: 05-08 18:47:03.287  1504  1632 I am_restart_activity: [0,91065182,970,com.android.launcher3/.Launcher]13901: 05-08 18:47:03.288  1504  1632 I am_set_resumed_activity: [0,com.android.launcher3/.Launcher,minimalResumeActivityLocked]13903: 05-08 18:47:03.289  1504  1632 I am_add_to_stopping: [0,91065182,com.android.launcher3/.Launcher,makeInvisible]13906: 05-08 18:47:03.290  1504  1632 I am_pause_activity: [0,91065182,com.android.launcher3/.Launcher,userLeaving=false]13930: 05-08 18:47:03.309   722   761 I /vendor/bin/hw/vendor.qti.hardware.servicetracker@1.1-service: startService() is called for servicecom.android.launcher3/com.android.quickstep.TouchInteractionService行 13931: 05-08 18:47:03.309  1504  1632 I sysui_multi_action: [757,1144,758,8,806,com.android.launcher3,871,com.android.launcher3.Launcher]14061: 05-08 18:47:03.504  1504  1504 V NotificationListeners: 0 notification listener service connected: ComponentInfo{com.android.launcher3/com.android.launcher3.notification.NotificationListener}14132: 05-08 18:47:03.596  2435  2435 V Launcher: LauncherAppState initiated行 14803: 05-08 18:47:03.791  1504  1632 W ActivityTaskManager: Activity top resumed state loss timeout for ActivityRecord{56d8b5e u0 com.android.launcher3/.Launcher t970}14804: 05-08 18:47:03.791  1504  1632 W ActivityTaskManager: Activity pause timeout for ActivityRecord{56d8b5e u0 com.android.launcher3/.Launcher t970}14831: 05-08 18:47:03.799  1504  1632 I am_stop_activity: [0,91065182,com.android.launcher3/.Launcher]15393: 05-08 18:47:04.232  2435  2435 I am_on_create_called: [0,com.android.launcher3.Launcher,performCreate]15400: 05-08 18:47:04.242  2435  2435 I am_on_start_called: [0,com.android.launcher3.Launcher,handleStartActivity]15401: 05-08 18:47:04.243  2435  2435 I am_on_resume_called: [0,com.android.launcher3.Launcher,RESUME_ACTIVITY]15408: 05-08 18:47:04.255  2435  2435 I am_on_top_resumed_gained_called: [0,com.android.launcher3.Launcher,topStateChangedWhenResumed]15409: 05-08 18:47:04.255  2435  2435 I am_on_top_resumed_lost_called: [0,com.android.launcher3.Launcher,topStateChangedWhenResumed]15410: 05-08 18:47:04.255  2435  2435 I am_on_paused_called: [0,com.android.launcher3.Launcher,performPause]15411: 05-08 18:47:04.256  1504  3100 I am_failed_to_pause: [0,91065182,com.android.launcher3/.Launcher,(none)]15420: 05-08 18:47:04.268  2435  2435 I am_on_stop_called: [0,com.android.launcher3.Launcher,STOP_ACTIVITY_ITEM]16064: 05-08 18:47:05.573  1504  3106 I am_set_resumed_activity: [0,com.android.launcher3/.Launcher,resumeTopActivityInnerLocked]16065: 05-08 18:47:05.574  1504  3106 I am_resume_activity: [0,91065182,970,com.android.launcher3/.Launcher]16070: 05-08 18:47:05.590  2435  2435 I am_on_restart_called: [0,com.android.launcher3.Launcher,performRestartActivity]16071: 05-08 18:47:05.591  2435  2435 I am_on_start_called: [0,com.android.launcher3.Launcher,handleStartActivity]16072: 05-08 18:47:05.592  2435  2435 I am_on_resume_called: [0,com.android.launcher3.Launcher,RESUME_ACTIVITY]16073: 05-08 18:47:05.592  2435  2435 I am_on_top_resumed_gained_called: [0,com.android.launcher3.Launcher,topWhenResuming]

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

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

相关文章

uni-app解决表格uni-table样式问题

一、如何让表格文字只显示一行&#xff0c;超出部分用省略号表示 步骤 &#xff1a; 给table设置table-layout:fixed; 列宽由表格宽度和列宽度设定。&#xff08;默认是由单元格内容设定&#xff09;让表格元素继承父元素宽度固定table-layout: inherit;overflow: hidden;超过…

list的简单模拟实现

文章目录 目录 文章目录 前言 一、使用list时的注意事项 1.list不支持std库中的sort排序 2.去重操作 3.splice拼接 二、list的接口实现 1.源码中的节点 2.源码中的构造函数 3.哨兵位头节点 4.尾插和头插 5.迭代器* 5.1 迭代器中的operator和-- 5.2其他迭代器中的接口 5.3迭代器…

【气象常用】剖面图

效果图&#xff1a; 主要步骤&#xff1a; 1. 数据准备&#xff1a;我用的era5的散度数据&#xff08;大家替换为自己的就好啦&#xff0c;era5数据下载方法可以看这里【数据下载】ERA5 各高度层月平均数据下载_era5月平均数据-CSDN博客&#xff09; 2. 数据处理&#xff1a…

windows10系统64位安装delphiXE11.2完整教程

windows10系统64位安装delphiXE11.2完整教程 https://altd.embarcadero.com/download/radstudio/11.0/radstudio_11_106491a.iso XE11.1 https://altd.embarcadero.com/download/radstudio/11.0/RADStudio_11_2_10937a.iso XE11.2 关键使用文件在以下内容&#xff1a;windows10…

Java Spring Boot 从必应爬取图片

获取图片主要就是通过必应图片页面控制台的元素&#xff0c;确认图片和标题在哪个类中&#xff08;浏览器 F12&#xff09; 引入依赖 这里需要引入两个依赖 jsoup 和 hutool maven依赖网站地址&#xff1a;Maven Repository: Search/Browse/Explore (mvnrepository.com) 挑选…

极简网络用户手册(1)

极简网络系统处理流程 模块位置&#xff1a;参数平台--专题分析--极简网络分析 步骤&#xff1a; 步骤一&#xff1a;创建精细化场景策略 步骤二&#xff1a;创建任务&#xff0c;主要选择策略&#xff08;包括√配置和距离配置&#xff09;和需要处理的小区清单&#xff08;源…

srcu浅析

本文代码基于linux内核4.19.195 之前写了rcu、rcu nocb的文章&#xff0c;感觉还差个srcu就完整了&#xff0c;现在补齐一下。 SRCU&#xff08;Sleepable RCU&#xff09;是rcu的一个变体&#xff0c;顾名思义&#xff0c;就是在rcu的读临界区中允许睡眠&#xff0c;而rcu在读…

CCF-GESP 等级考试 2023年6月认证C++四级真题解析

2023年09月真题 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 第 1 题 高级语言编写的程序需要经过以下&#xff08; &#xff09;操作&#xff0c;可以生成在计算机上运行的可执行代码。 A. 编辑B. 保存C. 调试D. 编译 答案&#xff1a;D 解析&#xff1a;…

FS212E 系列PD协议

PD快充协议芯片FS212EL、FS212EH可以智能的识别插入的手机类型&#xff0c;选择最为合适的协议应对手机快充需要。兼容多类USB Type-C协议&#xff0c;包括TypeC协议、TypeC PD2.0、TypeC PD3.0、TypeC PD3.2等协议。集成OPTO输出&#xff0c;通过电阻直驱反馈光耦。FS212E 的调…

C++函数模版和C#的泛型函数的区别

函数模板和C#的泛型函数在概念上是非常相似的&#xff0c;都是用于创建能够处理多种数据类型的通用函数。它们的主要区别点在于语法和实现细节上&#xff1a; 语法&#xff1a; C 中的函数模板使用 template <typename T> 或者 template <class T> 来声明模板&am…

新手学习STM32还是ESP32

对于新手来说&#xff0c;选择学习STM32还是ESP32取决于个人的学习目标和背景。以下是针对这两种微控制器的详细分析&#xff0c;以便您做出更明智的选择&#xff1a; STM32 1. 处理器架构与性能 STM32采用单核或多核处理器架构&#xff0c;基于ARM Cortex-M0&#xff0c;M0…

PlantUML-使用文本来画时序图

介绍 PlantUML 是一个开源工具&#xff0c;用户可以使用纯文本描述来创建 UML (统一建模语言) 图形。由于它使用文本来描述图形&#xff0c;因此可以很容易地将这些描述与源代码一起存储在版本控制系统中。然后&#xff0c;PlantUML 负责将这些描述转换为图形。 资料 官方文…

k8s部署1.27.3版本,使用containerd模式

环境 操作系统&#xff1a;centos7.9 机器&#xff1a;1个master 和 1个node 节点&#xff08;云ECS&#xff09; 系统设置 # 所有机器设置hostname hostnamectl set-hostname master1 hostnamectl set-hostname node1# 所有机器增加内网ip和 master1 对应关系 vi /etc/ho…

杂牌记录仪TS视频流恢复方法

大多数的记录仪都采用了MP4/MOV文件方案&#xff0c;极少数的可能在用AVI文件&#xff0c;极极少数的在用TS文件方案。很多人可能不太解TS文件&#xff0c;这是一种古老的视频文件结构&#xff0c;下边这个案例我们来看下TS视频文件的恢复方法。 故障存储:8G存储卡/fat32文件系…

2-1RT-Thread线程管理-笔记

2-1RT-Thread线程管理-笔记 其中系统线程由内核创建&#xff0c;如main函数和空闲线程都属于系统线程&#xff0c;而用户线程是由应用程序所创建的。 对于资源较大的MCU可以适当设计较大的线程栈&#xff0c;也可以在初始化时设置一个具体的数值&#xff0c;如1K或2K字节。…

索引 ---- mysql

目录 1. 索引 1.1 概念 1.2 作用 1.3 使用场景 1.4 使用 1.4.1查看索引 1.4.2 创建索引 1.4.3 删除索引 1.5 注意事项 1.6 索引底层的数据结构 (面试经典问题) 1. 索引 1.1 概念 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的…

2006NOIP普及组真题 2. 开心的金明

线上OJ&#xff1a; 【06NOIP普及组】开心的金明 本题只要把 1、限定金额看成背包总容量 2、每件物品的价格 v 看成占用背包的体积 3、每件物品的价格v乘以权重w作为该物品的价值 则本题可套用标准的01背包问题模板&#xff1a; f [ j ] m a x ( f [ j ] , f [ j − v ] w …

Redis单线程

Redis是基于Reactor模式开发的网络事件处理器,这个处理器是单线程的,所 以redis是单线程的。 为什么它是单线程还那么快呢? 主要有以下几个原因: 一、纯内存操作 由于Redis是 纯内存操作,相比于磁盘来说,内存就快得多,这个是Redis快的主要 原因。 二、多路复用I/O机制(…

优先队列优化哈夫曼编码

前言 个人小记 一、代码 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define MAX_NODE 80 #define MAX_STR 50 #define MAX_HEAP 100 #define father(i) ((i)/2) #define left(i) ((i)*2) #define right(i) …

11.3 指针和函数

11.3 指针和函数 本节必须掌握的知识点&#xff1a; 指针作为函数的参数 数组作为函数的参数 指针作为函数的返回值 在C语言中&#xff0c;指针的一个重要作用就是作为函数参数使用&#xff0c;本节将介绍这一重要作用。 11.3.1 指针作为函数的参数 实验一百一十三&#xff…