Android Framework AMS(05)startActivity分析-2(ActivityThread启动到Activity拉起)

该系列文章总纲链接:专题总纲目录 Android Framework 总纲


本章关键点总结 & 说明:

说明:本章节主要解读AMS通过startActivity启动Activity的整个流程的整个流程的第二阶段:从ActivityThread启动到Activity拉起。

第一阶段文章链接为:

Android Framework AMS(04)startActivity分析-1(am启动到ActivityThread启动)

上一节中我们从adb shell am start 命令开始分析 Activity 的启动流程,直到通过调用Process.start方法通过socket向Zygote发送一个消息,传递相关信息,让Zygote创建一个对应的应用进程。当zygote进程 fork一个应用进程启动后,应用进程的入口是ActivityThread的main函数。

这一节我们以ActivityThread的main函数作为起点开始继续分析startActivity启动Activity的流程。

1 ActivityThread启动分析

ActivityThread的main函数,代码实现如下:

//ActivityThreadpublic static void main(String[] args) {// 启动 SamplingProfilerIntegration,这是 Android 中用于性能监控和分析的组件SamplingProfilerIntegration.start();// 关闭 CloseGuard 错误检查。CloseGuard 用于跟踪资源(如流和数据库)是否被正确关闭// 在发布版本中禁用,但在调试版本中可以使用 StrictMode 并通过 DropBox 而不是日志来报告CloseGuard.setEnabled(false);// 初始化 Environment,设置当前用户的配置环境Environment.initForCurrentUser();// 设置 libcore 事件日志的报告器EventLogger.setReporter(new EventLoggingReporter());// 添加 AndroidKeyStoreProvider,用于提供 Android 密钥存储的加密服务Security.addProvider(new AndroidKeyStoreProvider());// 设置 TrustedCertificateStore,确保在查找 CA 证书时使用正确的用户目录final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);// 设置进程的主参数(ArgV0),这里用一个字符串 "<pre-initialized>" 作为临时值Process.setArgV0("<pre-initialized>");// 准备主循环器 Looper,这是 Android 消息循环机制的关键部分Looper.prepareMainLooper();// 创建 ActivityThread 实例并调用 attach 方法,进行一些初始化工作ActivityThread thread = new ActivityThread();thread.attach(false);// 如果 sMainThreadHandler 为空,则设置为当前线程的 Handlerif (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}// 调试代码,如果启用,将打印出 Looper 中的所有消息if (false) {Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));}// 进入 Looper 消息循环,等待和分发消息,直到 Looper 退出Looper.loop();// 如果 Looper 意外退出,抛出异常throw new RuntimeException("Main thread loop unexpectedly exited");}

这里我们主要关注attach方法,代码实现如下:

//ActivityThreadprivate void attach(boolean system) {sCurrentActivityThread = this;mSystemThread = system;// 当前线程不是系统线程时执行的操作if (!system) {//优化应用性能,首次绘制时启用 JIT 编译器,确保应用在执行时能够获得更好的性能ViewRootImpl.addFirstDrawHandler(new Runnable() {@Overridepublic void run() {ensureJitEnabled();}});// 设置调试时的应用程序名称,DDMS中看到的调试程序名android.ddm.DdmHandleAppName.setAppName("<pre-initialized>", UserHandle.myUserId());// 设置RuntimeInit应用程序对象,供调试使用RuntimeInit.setApplicationObject(mAppThread.asBinder());// 获取和AMS交互的代理final IActivityManager mgr = ActivityManagerNative.getDefault();// 关键方法:调用AMS的 attachApplication 方法,注册应用程序线程mgr.attachApplication(mAppThread);// 添加 GC 监视器,用于处理活动管理BinderInternal.addGcWatcher(new Runnable() {@Override public void run() {if (!mSomeActivitiesChanged) {return;}// 计算 Dalvik 内存使用情况,并在内存使用达到一定阈值时释放一些活动Runtime runtime = Runtime.getRuntime();long dalvikMax = runtime.maxMemory();long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();if (dalvikUsed > ((3*dalvikMax)/4)) {mSomeActivitiesChanged = false;try {mgr.releaseSomeActivities(mAppThread);} catch (RemoteException e) {}}}});} else {//... 系统进程的初始化操作}// 设置 DropBox 报告器,用于收集系统崩溃和错误信息DropBox.setReporter(new DropBoxReporter());// 添加配置更改回调,用于处理屏幕配置更改ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {//...});}

接下来我们主要关注关键方法即AMS的attachApplication方法。这里attachApplication的实现代码如下:

//ActivityManagerService@Overridepublic final void attachApplication(IApplicationThread thread) {synchronized (this) {int callingPid = Binder.getCallingPid();final long origId = Binder.clearCallingIdentity();attachApplicationLocked(thread, callingPid);Binder.restoreCallingIdentity(origId);}}

这里主要是调用attachApplicationLocked方法。接下来专注分析attachApplicationLocked方法。

1.1 AMS.attachApplicationLocked第一阶段代码分析

AMS.attachApplicationLocked第一阶段代码解读如下:

//ActivityManagerServiceprivate final boolean attachApplicationLocked(IApplicationThread thread, int pid) {// 获取正在附加的进程记录ProcessRecord app;if (pid != MY_PID && pid >= 0) {synchronized (mPidsSelfLocked) {//通过pid获取appapp = mPidsSelfLocked.get(pid);}} else {app = null;}// 如果没有找到对应的进程记录,且进程ID有效,则杀死该进程if (app == null) {if (pid > 0 && pid != MY_PID) {Process.killProcessQuiet(pid);} else {try {thread.scheduleExit();} catch (Exception e) {// 忽略异常}}return false; // 返回false,表示附加失败}// 如果该进程已经有线程关联,则处理应用已死的情况if (app.thread != null) {handleAppDiedLocked(app, true, true);}// 创建linkToDeath并关联到应用线程final String processName = app.processName;try {AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);thread.asBinder().linkToDeath(adr, 0);app.deathRecipient = adr;} catch (RemoteException e) {app.resetPackageList(mProcessStats);startProcessLocked(app, "link fail", processName);return false; // 如果链接失败,重置进程列表并重新启动进程}// 使进程记录处于活动状态app.makeActive(thread, mProcessStats);// 设置进程的调度参数app.curAdj = app.setAdj = -100; // 设置当前和设定的调度调整值app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT; // 设置调度组// 重置进程的UI和调试状态app.forcingToForeground = null;updateProcessForegroundLocked(app, false, false);app.hasShownUi = false;app.debugging = false;app.cached = false;app.killedByAm = false;// 移除之前设置的启动超时消息mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);  

这段代码的主要目的是处理应用程序进程的attach操作的第一阶段。它主要做了:获取进程记录、处理应用死亡、创建应用死亡接收器、激活进程、设置调度参数、重置UI和调试状态以及移除启动超时消息等。

1.2 AMS.attachApplicationLocked第二阶段代码分析

AMS.attachApplicationLocked第二阶段代码解读如下:

//ActivityManagerService    private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {//...// 第二阶段// 检查是否处于正常模式或是否允许在启动过程中附加boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);// 如果是正常模式,生成应用程序的内容提供者List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;try {//...// 设置调试模式相关处理// 设置性能分析文件和文件描述符相关处理// 设置 OpenGL 追踪//...// 如果应用被启动用于恢复或完整备份,特殊设置boolean isRestrictedBackupMode = false;if (mBackupTarget != null && mBackupAppName.equals(processName)) {isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)|| (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);}// 确保包的 Dex 优化ensurePackageDexOpt(app.instrumentationInfo != null? app.instrumentationInfo.packageName: app.info.packageName);if (app.instrumentationClass != null) {ensurePackageDexOpt(app.instrumentationClass.getPackageName());}// 获取应用程序信息ApplicationInfo appInfo = app.instrumentationInfo != null? app.instrumentationInfo : app.info;app.compat = compatibilityInfoForPackageLocked(appInfo);// 设置性能分析器信息相关处理//...// 关键方法:绑定应用程序thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(mConfiguration), app.compat,getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked());updateLruProcessLocked(app, false, null);app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();} catch (Exception e) {app.resetPackageList(mProcessStats);app.unlinkDeathRecipient();startProcessLocked(app, "bind fail", processName);return false;}// 从持久启动进程和保持进程列表中移除该应用mPersistentStartingProcesses.remove(app);mProcessesOnHold.remove(app);//...

这段代码的主要目的是处理应用程序的attach操作的第二阶段,包括设置调试模式、性能分析、备份/恢复模式等。它通过设置各种参数和调用 bindApplication 方法(最关键)来实现。如果在绑定过程中出现异常,它会进行适当的清理和重试操作。

1.3 AMS.attachApplicationLocked第三阶段代码分析

AMS.attachApplicationLocked第三阶段代码解读如下:

//ActivityManagerService    private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {//...// 第三阶段boolean badApp = false;boolean didSomething = false;// 如果处于正常模式,尝试将应用程序与顶层可见activity关联if (normalMode) {try {//关键方法:为了确保用户界面能够正确响应用户的交互。if (mStackSupervisor.attachApplicationLocked(app)) {didSomething = true;}} catch (Exception e) {//...}}// 尝试处理service组件(这里的service实际上是AndroidManifest.xml 中声明的 Service // 或者在代码中通过继承 Service 类并实例化的自定义服务)if (!badApp) {try {//为了确保应用的服务能够正确启动和运行didSomething |= mServices.attachApplicationLocked(app, processName);} catch (Exception e) {badApp = true;}}// 如果有待处理的广播,尝试发送(实际上就是处理broadcast组件)if (!badApp && isPendingBroadcastProcessLocked(pid)) {try {didSomething |= sendPendingBroadcastsLocked(app);} catch (Exception e) {badApp = true;}}// 如果应用是备份目标,安排创建备份代理if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) {ensurePackageDexOpt(mBackupTarget.appInfo.packageName);try {thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,compatibilityInfoForPackageLocked(mBackupTarget.appInfo),mBackupTarget.backupMode);} catch (Exception e) {badApp = true;}}// 如果应用处理过程中出现严重错误,杀死应用并标记为死亡if (badApp) {app.kill("error during init", true);handleAppDiedLocked(app, false, true);return false;}// 如果没有执行任何操作,更新OOM调整if (!didSomething) {updateOomAdjLocked();}return true; // 返回成功}

这段代码的主要目的是处理应用程序的attach操作的第三阶段,即在应用程序进程成功attach后,执行一系列的后续操作。包括处理可见activity、服务、广播和备份代理创建。如果在处理过程中出现错误,会进行适当的错误处理,包括杀死应用和更新系统状态。如果没有执行任何操作,会更新应用的 OOM 调整,以确保系统资源的合理分配。这个过程确保了应用程序能够正确地响应系统的各种管理操作,并且在出现错误时能够及时进行恢复。

总之,AMS.attachApplicationLocked 方法是 Android 应用程序启动流程中的核心环节,它确保了应用程序能够正确地绑定到系统服务并开始执行。这个过程涉及到多个关键的函数调用,包括绑定应用程序、处理可见活动、创建应用程序上下文和实例,以及调用应用程序的 onCreate 方法。如果在启动过程中遇到错误,attachApplicationLocked 方法还会负责错误处理,确保系统的稳定性。

接下来我们主要关注2个关键方法:

  • 第二阶段的bindApplication方法:这是 IApplicationThread 接口的方法,用于在应用程序进程中创建应用程序的实例。这里最终会调用到handleBindApplication方法,它负责处理 BIND_APPLICATION 消息,执行应用程序的绑定逻辑。
  • 第三阶段的realStartActivityLocked方法:在 ActivityStackSupervisor 类中,这个方法负责启动一个新的 Activity 实例。该方法是在第三阶段中ActivityStackSupervisor类中的attachApplicationLocked中会调用到次此方法。因此分析时会从attachApplicationLocked开始分析。

接下来我们针对这2个方法在2.1(bindApplication) 2.2(realStartActivityLocked) 中进行详细的分析。

2 AMS.attachApplicationLocked中关键方法继续分析

2.1 bindApplication代码分析

ActivityThread中的子类ApplicationThread的bindApplication代码实现如下所示:

//ActivityThread//ApplicationThreadpublic final void bindApplication(String processName, ApplicationInfo appInfo,List<ProviderInfo> providers, ComponentName instrumentationName,ProfilerInfo profilerInfo, Bundle instrumentationArgs,IInstrumentationWatcher instrumentationWatcher,IUiAutomationConnection instrumentationUiConnection, int debugMode,boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,Bundle coreSettings) {if (services != null) {// 初始化服务管理器的缓存ServiceManager.initServiceCache(services);}setCoreSettings(coreSettings);// 设置核心设置IPackageManager pm = getPackageManager();android.content.pm.PackageInfo pi = null;try {pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());} catch (RemoteException e) {}if (pi != null) {// 检查是否设置了共享用户ID或进程名称不是默认的boolean sharedUserIdSet = (pi.sharedUserId != null);boolean processNameNotDefault =(pi.applicationInfo != null &&!appInfo.packageName.equals(pi.applicationInfo.processName));boolean sharable = (sharedUserIdSet || processNameNotDefault);if (!sharable) {// 注册应用程序信息VMRuntime.registerAppInfo(appInfo.packageName, appInfo.dataDir,appInfo.processName);}}// 创建 AppBindData 对象AppBindData data = new AppBindData();data.processName = processName;// ... data 数据初始化,各种赋值// 发送绑定应用程序的消息sendMessage(H.BIND_APPLICATION, data);}//...//Handler消息处理private class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {//...case BIND_APPLICATION:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");AppBindData data = (AppBindData)msg.obj;//关键方法调用handleBindApplication(data);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;//...}//...}//...}

这里最后调用的sendMessage将数据通过handler消息机制,通过handleBindApplication方法来处理。接下来我们主要分析该方法,代码实现如下:

//ActivityThreadprivate void handleBindApplication(AppBindData data) {// 设置当前绑定的应用程序数据mBoundApplication = data;// 更新当前的配置和兼容性配置mConfiguration = new Configuration(data.config);mCompatConfiguration = new Configuration(data.config);//...// 设置进程的主名称Process.setArgV0(data.processName);// 设置调试时的应用程序名称android.ddm.DdmHandleAppName.setAppName(data.processName, UserHandle.myUserId());// 如果应用是持久化的,且设备不是高端图形设备,则禁用硬件加速if (data.persistent) {if (!ActivityManager.isHighEndGfx()) {HardwareRenderer.disable(false);}}//...// 如果应用的目标 SDK 版本较低,则设置默认的 AsyncTask 执行器if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) {AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);}// 更新消息循环中的消息检查策略Message.updateCheckRecycle(data.appInfo.targetSdkVersion);// 设置默认时区和语言环境TimeZone.setDefault(null);Locale.setDefault(data.config.locale);// 应用配置到资源管理器mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);// 更新当前显示的 DPImCurDefaultDisplayDpi = data.config.densityDpi;applyCompatConfiguration(mCurDefaultDisplayDpi);// 获取应用程序信息data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);// 如果应用不支持屏幕密度,则启用兼容性模式if ((data.appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) == 0) {mDensityCompatMode = true;Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);}updateDefaultDensity();// 创建应用程序上下文final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);// 设置临时文件夹的系统属性if (!Process.isIsolated()) {final File cacheDir = appContext.getCacheDir();if (cacheDir != null) {System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());setupGraphicsSupport(data.info, cacheDir);}}// 设置日期和时间格式final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24));DateFormat.set24HourTimePref(is24Hr);// 设置调试时的视图属性View.mDebugViewAttributes = mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0;// 如果应用是系统应用或更新的系统应用,启用 StrictMode 的调试日志if ((data.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) {StrictMode.conditionallyEnableDebugLogging();}// 如果应用的目标 SDK 版本大于 9,则UI主线程不允许使用网络耗时操作if (data.appInfo.targetSdkVersion > 9) {StrictMode.enableDeathOnNetwork();}// 获取连接管理服务,并设置系统属性IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);if (b != null) {IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);try {final ProxyInfo proxyInfo = service.getDefaultProxy();Proxy.setHttpProxySystemProperty(proxyInfo);} catch (RemoteException e) {}}// 如果有 Instrumentation 信息,则进行初始化if (data.instrumentationName != null) {InstrumentationInfo ii = null;try {ii = appContext.getPackageManager().getInstrumentationInfo(data.instrumentationName, 0);} catch (PackageManager.NameNotFoundException e) {}// ...// 初始化 Instrumentationif (ii != null) {// ...mInstrumentation = (Instrumentation) ii.getClass().newInstance();mInstrumentation.init(this, instrContext, appContext, new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher, data.instrumentationUiAutomationConnection);}}// 如果应用需要大堆,则清除 Dalvik 增长限制if ((data.appInfo.flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0) {dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();}// 允许线程进行磁盘写操作final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();try {// 创建应用程序实例Application app = data.info.makeApplication(data.restrictedBackupMode, null);mInitialApplication = app;if (!data.restrictedBackupMode) {// 安装内容提供者ContentProvidwerList<ProviderInfo> providers = data.providers;if (providers != null) {installContentProviders(app, providers);mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);}}// 调用 Instrumentation 的 onCreatemInstrumentation.onCreate(data.instrumentationArgs);//...// 调用应用程序的 onCreate//...mInstrumentation.callApplicationOnCreate(app);} finally {// 恢复 StrictMode 策略StrictMode.setThreadPolicy(savedPolicy);}}

handleBindApplication 方法是 Android 应用程序启动过程中的关键步骤,它负责设置应用程序运行时的关键配置和环境。这个过程包括配置应用程序上下文、设置日期时间格式、配置 StrictMode、设置 HTTP 代理、初始化 Instrumentation 以及创建应用程序实例等。这些步骤确保了应用程序能够在正确的环境中运行,并且能够及时响应系统的各种管理操作。环境构建好了,接下来就是启动Activity了。

我们继续分析attachApplicationLocked中realStartActivityLocked的实现。

2.2 realStartActivityLocked分析

realStartActivityLocked是在ActivityThread中的attachApplicationLocked中调用的,该方法代码实现如下所示:

//ActivityStackSupervisorboolean attachApplicationLocked(ProcessRecord app) throws RemoteException {final String processName = app.processName;  // 获取应用程序进程的名称boolean didSomething = false;  // 标记是否执行了启动 Activity 的操作// 遍历所有显示(屏幕)的 ActivityStackfor (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;// 遍历每个显示中的 ActivityStackfor (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {final ActivityStack stack = stacks.get(stackNdx);// 只处理前台的 ActivityStackif (!isFrontStack(stack)) {continue;}// 获取顶层正在运行的 ActivityActivityRecord hr = stack.topRunningActivityLocked(null);if (hr != null) {// 如果顶层 Activity 的应用记录为空,并且它的 UID 和进程名称与当前应用记录匹配if (hr.app == null && app.uid == hr.info.applicationInfo.uid&& processName.equals(hr.processName)) {try {// 尝试启动该 Activityif (realStartActivityLocked(hr, app, true, true)) {didSomething = true;  // 如果成功启动,标记为已执行操作}} catch (RemoteException e) {// 异常处理// ...}}}}}// 如果没有执行任何 Activity 启动操作,确保所有 Activity 可见if (!didSomething) {ensureActivitiesVisibleLocked(null, 0);}return didSomething;  // 返回是否执行了启动 Activity 的操作}

attachApplicationLocked 方法的主要目的是在应用程序进程启动后,检查并启动该进程中的顶层 Activity。这个过程包括遍历 ActivityStack、检查顶层 Activity 的状态、尝试启动 Activity 以及异常处理。如果没有启动任何 Activity,它会确保所有 Activity 都是可见的。这里的关键方法是尝试启动Activity的realStartActivityLocked方法,接下来我们继续分析该方法。代码实现如下:

//ActivityStackSupervisorfinal boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig)throws RemoteException {// 冻结屏幕以准备启动 Activityr.startFreezingScreenLocked(app, 0);// 设置应用可见mWindowManager.setAppVisibility(r.appToken, true);// 开始启动计时r.startLaunchTickingLocked();// 检查并更新配置if (checkConfig) {Configuration config = mWindowManager.updateOrientationFromAppTokens(mService.mConfiguration,r.mayFreezeScreenLocked(app) ? r.appToken : null);mService.updateConfigurationLocked(config, r, false, false);}r.app = app;app.waitingToKill = null;r.launchCount++;r.lastLaunchTime = SystemClock.uptimeMillis();// 将 Activity 添加到应用的 Activity 列表int idx = app.activities.indexOf(r);if (idx < 0) {app.activities.add(r);}// 更新 LRU 列表和内存调整mService.updateLruProcessLocked(app, true, null);mService.updateOomAdjLocked();// 获取目标 ActivityStackfinal ActivityStack stack = r.task.stack;try {// 确保应用线程不为空if (app.thread == null) {throw new RemoteException();}List<ResultInfo> results = null;List<ReferrerIntent> newIntents = null;if (andResume) {results = r.results;newIntents = r.newIntents;}// 如果是启动主屏幕 Activityif (r.isHomeActivity() && r.isNotResolverActivity()) {mService.mHomeProcess = r.task.mActivities.get(0).app;}// 确保包的 Dex 优化mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());// 设置 Activity 不再睡眠r.sleeping = false;r.forceNewConfig = false;// 显示兼容性对话框mService.showAskCompatModeDialogLocked(r);// 设置兼容性信息r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);// 性能分析相关//...app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);// 关键方法:调度启动 Activityapp.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState,r.icicle, r.persistentState, results, newIntents, !andResume,mService.isNextTransitionForward(), profilerInfo);// 处理重量级应用if ((app.info.flags & ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {if (app.processName.equals(app.info.packageName)) {mService.mHeavyWeightProcess = app;Message msg = mService.mHandler.obtainMessage(ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);msg.obj = r;mService.mHandler.sendMessage(msg);}}} catch (RemoteException e) {//...throw e;}// 标记 Activity 未启动失败r.launchFailed = false;// 更新 LRU 列表stack.updateLRUListLocked(r);// 如果需要resumeif (andResume) {//关键方法:最小化resume Activitystack.minimalResumeActivityLocked(r);} else {r.state = ActivityState.STOPPED;r.stopped = true;}// 如果是前台栈,则启动设置 Activityif (isFrontStack(stack)) {mService.startSetupActivityLocked();}// 更新服务连接活动mService.mServices.updateServiceConnectionActivitiesLocked(r.app);return true; // 返回成功}

realStartActivityLocked 方法是 Android 系统中启动 Activity 的核心方法。它负责处理从 Activity 的启动到恢复的完整流程,包括冻结屏幕、设置应用可见、更新配置、添加到应用的 Activity 列表、内存调整、启动 Activity、处理重量级应用、异常处理、更新 LRU 列表、恢复 Activity 以及启动设置 Activity。这个过程确保了 Activity 能够正确地启动并响应用户的交互。

接下来我们主要关注scheduleLaunchActivity方法:app.thread.scheduleLaunchActivity(...) 是一个在 ActivityThread 类中调用的方法,用于调度新的 Activity 的启动。这个方法通过应用程序线程 (app.thread) 向应用程序进程发送启动 Activity 的请求。接下来开始scheduleLaunchActivity代码的分析。ActivityThread中的子类ApplicationThread的scheduleLaunchActivity代码实现如下所示:

//ActivityThread//ApplicationThreadpublic final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,PersistableBundle persistentState, List<ResultInfo> pendingResults,List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,ProfilerInfo profilerInfo) {// 更新进程状态updateProcessState(procState, false);// 创建一个新的 ActivityClientRecord 来存储 Activity 的启动信息ActivityClientRecord r = new ActivityClientRecord();// 设置 Activity 的令牌、标识符、Intent 等信息r.token = token;r.ident = ident;r.intent = intent;// ...// 更新待处理的配置信息updatePendingConfiguration(curConfig);// 通过消息队列发送启动 Activity 的消息sendMessage(H.LAUNCH_ACTIVITY, r);}private class H extends Handler {//...public void handleMessage(Message msg) {switch (msg.what) {case LAUNCH_ACTIVITY: {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");final ActivityClientRecord r = (ActivityClientRecord) msg.obj;r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);handleLaunchActivity(r, null);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);} break;//...}//...}//...}//...

这里最后处理消息的方法是handleLaunchActivity,代码实现如下:

//ActivityThreadprivate void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {// 取消调度 GC(垃圾回收)Idler,准备启动 ActivityunscheduleGcIdler();// 标记有 Activity 状态改变mSomeActivitiesChanged = true;// 处理配置变更handleConfigurationChanged(null, null);// 初始化 WindowManagerGlobalWindowManagerGlobal.initialize();// 关键方法:执行 Activity 的启动Activity a = performLaunchActivity(r, customIntent);// 如果成功创建了 Activityif (a != null) {// 记录创建时的配置r.createdConfig = new Configuration(mConfiguration);// 保存之前的状态Bundle oldState = r.state;//关键方法: resume ActivityhandleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed);// 如果 Activity 没有结束并且没有在启动时恢复if (!r.activity.mFinished && r.startsNotResumed) {try {// 标记为已调用 onPauser.activity.mCalled = false;// 调用 Activity 的 onPause 方法mInstrumentation.callActivityOnPause(r.activity);// ...} catch (Exception e) {// ...throw e;}r.paused = true;}} else {// 如果启动 Activity 失败try {// 通知 ActivityManager 结束 ActivityActivityManagerNative.getDefault().finishActivity(r.token, Activity.RESULT_CANCELED, null, false);} catch (RemoteException ex) {// Ignore}}}

handleLaunchActivity 方法是 Android ActivityThread 类中的关键方法,负责处理 Activity 的启动逻辑。它包括取消垃圾回收的调度、处理配置变更、初始化 WindowManagerGlobal、执行 Activity 的启动相关操作,以及处理 Activity 启动失败的情况。这个过程确保了 Activity 能够正确地启动并响应用户的交互。其中最重要的是执行 Activity 的启动相关操作

接下来对关键方法performLaunchActivity和handleResumeActivity分别进行解读。

2.2.1 performLaunchActivity方法解读

ActivityThread.performLaunchActivity 方法代码实现如下:

//ActivityThreadprivate Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {ActivityInfo aInfo = r.activityInfo;  // 获取Activity的信息if (r.packageInfo == null) {r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,Context.CONTEXT_INCLUDE_CODE);}ComponentName component = r.intent.getComponent();  // 获取Intent中指定的组件if (component == null) {component = r.intent.resolveActivity(  // 如果组件未指定,解析组件mInitialApplication.getPackageManager());r.intent.setComponent(component);  // 设置解析后的组件到Intent}if (r.activityInfo.targetActivity != null) {component = new ComponentName(r.activityInfo.packageName,r.activityInfo.targetActivity);}Activity activity = null;try {java.lang.ClassLoader cl = r.packageInfo.getClassLoader();  // 获取ClassLoaderactivity = mInstrumentation.newActivity(  // 使用Instrumentation创建Activity实例cl, component.getClassName(), r.intent);StrictMode.incrementExpectedActivityCount(activity.getClass());r.intent.setExtrasClassLoader(cl);  // 为Intent设置ClassLoaderr.intent.prepareToEnterProcess();  // 准备进入进程if (r.state != null) {r.state.setClassLoader(cl);}} catch (Exception e) {//...}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);  // 创建Application实例if (activity != null) {Context appContext = createBaseContextForActivity(r, activity);  // 创建Activity上下文CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());  // 加载标题Configuration config = new Configuration(mCompatConfiguration);  // 创建Configurationactivity.attach(appContext, this, getInstrumentation(), r.token,  // 绑定Activityr.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor);if (customIntent != null) {activity.mIntent = customIntent;  // 如果有自定义Intent,设置之}r.lastNonConfigurationInstances = null;activity.mStartedActivity = false;  // 标记Activity尚未启动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);  // 调用onCreate} else {mInstrumentation.callActivityOnCreate(activity, r.state);  // 调用onCreate}r.activity = activity;  // 设置ActivityClientRecord中的Activityr.stopped = true;  // 标记Activity已停止if (!r.activity.mFinished) {activity.performStart();  // 执行start操作,调用onStart系列方法r.stopped = false;  // 标记Activity未停止}if (!r.activity.mFinished) {if (r.isPersistable()) {if (r.state != null || r.persistentState != null) {mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,  // 恢复状态r.persistentState);}} else if (r.state != null) {mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);  // 恢复状态}}if (!r.activity.mFinished) {activity.mCalled = false;  // 标记onPostCreate尚未调用if (r.isPersistable()) {mInstrumentation.callActivityOnPostCreate(activity, r.state,  // 调用onPostCreater.persistentState);} else {mInstrumentation.callActivityOnPostCreate(activity, r.state);  // 调用onPostCreate}}}r.paused = true;  // 标记Activity已暂停mActivities.put(r.token, r);  // 将ActivityClientRecord添加到映射中} catch (Exception e) {// Handle exceptions during activity resuming}return activity;  // 返回Activity实例}

performLaunchActivity 方法负责实际的 Activity 创建和初始化工作。它首先解析 Activity 的组件名称,然后使用类加载器创建 Activity 实例。接着,它为 Activity 设置上下文、加载标签和配置,绑定 Activity 并调用其生命周期方法,如 onCreate、onStart 和 onPostCreate。如果在任何时候 Activity 被标记为已完成,它将停止进一步的操作。这个过程确保了 Activity 能够正确地响应用户的启动请求,并在出现问题时能够进行适当的异常处理。最终,该方法返回新创建的 Activity 实例。

2.2.2 handleResumeActivity方法解读

ActivityThread.handleResumeActivity 方法代码实现如下:

//ActivityThread//关键流程:step1final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {unscheduleGcIdler();  // 取消垃圾回收的定时器,准备恢复 ActivitymSomeActivitiesChanged = true;  // 标记有 Activity 状态改变// 关键方法:resume Activity 的操作ActivityClientRecord r = performResumeActivity(token, clearHide);  if (r != null) {final Activity a = r.activity;final int forwardBit = isForward ?WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;  // 设置前进导航的标志boolean willBeVisible = !a.mStartedActivity;  // 检查 Activity 是否将要变为可见if (!willBeVisible) {try {willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(a.getActivityToken());  // 查询 Activity 是否将要变为可见} catch (RemoteException e) {}}if (r.window == null && !a.mFinished && willBeVisible) {// 如果 Activity 的窗口为空,且 Activity 未结束,且将要变为可见,则创建窗口r.window = r.activity.getWindow();View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE);  // 设置窗口的装饰视图为不可见ViewManager wm = a.getWindowManager();  // 获取窗口管理器WindowManager.LayoutParams l = r.window.getAttributes();  // 获取窗口参数a.mDecor = decor;  // 设置 Activity 的装饰l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;  // 设置窗口类型l.softInputMode |= forwardBit;  // 设置软键盘模式if (a.mVisibleFromClient) {a.mWindowAdded = true;  // 标记窗口已添加wm.addView(decor, l);  // 将装饰视图添加到窗口管理器}} else if (!willBeVisible) {r.hideForNow = true;  // 如果 Activity 不将要变为可见,则暂时隐藏}cleanUpPendingRemoveWindows(r);  // 清理待处理的移除窗口if (!r.activity.mFinished && willBeVisible&& r.activity.mDecor != null && !r.hideForNow) {// 如果 Activity 未结束,将要变为可见,且mDecor不为空,则进行可见性处理if (r.newConfig != null) {performConfigurationChanged(r.activity, r.newConfig);  // 执行配置更改freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.newConfig));  // 释放文本布局缓存r.newConfig = null;  // 清除新配置}WindowManager.LayoutParams l = r.window.getAttributes();  // 获取窗口参数if ((l.softInputMode& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)!= forwardBit) {// 如果软键盘模式的标志不匹配,则更新标志l.softInputMode = (l.softInputMode& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))| forwardBit;if (r.activity.mVisibleFromClient) {ViewManager wm = a.getWindowManager();  // 获取窗口管理器View decor = r.window.getDecorView();  // 获取装饰视图wm.updateViewLayout(decor, l);  // 更新视图布局}}r.activity.mVisibleFromServer = true;  // 标记 Activity 在服务器端可见mNumVisibleActivities++;  // 增加可见 Activity 计数if (r.activity.mVisibleFromClient) {r.activity.makeVisible();  // 使 Activity 可见}}if (!r.onlyLocalRequest) {r.nextIdle = mNewActivities;  // 设置下一个空闲的 ActivitymNewActivities = r;  // 设置新的 ActivityLooper.myQueue().addIdleHandler(new Idler());  // 添加空闲处理器}r.onlyLocalRequest = false;  // 清除本地请求标志if (reallyResume) {try {ActivityManagerNative.getDefault().activityResumed(token);  // 通知 AMS Activity 已resume} catch (RemoteException ex) {}}} else {try {ActivityManagerNative.getDefault().finishActivity(token, Activity.RESULT_CANCELED, null, false);  // 如果 Activity 无法resume,通知 AMS 结束 Activity} catch (RemoteException ex) {}}}//关键流程:step2public final ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide) {// 根据 IBinder token 获取对应的 ActivityClientRecord 对象ActivityClientRecord r = mActivities.get(token);if (r != null && !r.activity.mFinished) {// 如果需要清除隐藏状态if (clearHide) {r.hideForNow = false; // 清除隐藏标记r.activity.mStartedActivity = false; // 重置开始活动标记}try {// 通知 Fragments 状态不会保存,因为 Activity 即将恢复r.activity.mFragments.noteStateNotSaved();// 如果有待处理的 Intent,分发它们if (r.pendingIntents != null) {deliverNewIntents(r, r.pendingIntents);r.pendingIntents = null; // 清除待处理的 Intent 列表}// 如果有待处理的结果,分发它们if (r.pendingResults != null) {deliverResults(r, r.pendingResults);r.pendingResults = null; // 清除待处理的结果列表}// resume Activityr.activity.performResume();// 更新 ActivityClientRecord 的状态r.paused = false; // 标记 Activity 未暂停r.stopped = false; // 标记 Activity 未停止r.state = null; // 清除保存的状态r.persistentState = null; // 清除持久化的状态} catch (Exception e) {//...}}return r;}

handleResumeActivity 方法负责处理 Activity 的恢复逻辑。它首先取消垃圾回收的定时器,然后执行恢复操作,更新 Activity 的可见性和窗口状态,并与 ActivityManager 通信以确保 Activity 的状态一致。如果 Activity 无法恢复,它会通知 ActivityManager 结束该 Activity。这个过程确保了 Activity 在用户交互和系统配置更改时能够正确地恢复到前台,同时保持了应用程序的响应性和稳定性。

handleResumeActivity方法中最关键的调用是performResumeActivity 方法。该方法是 Android 系统中resume Activity 状态的关键方法。它负责准备 Activity 从停止或暂停状态转换到运行状态,包括清除隐藏状态、分发待处理的 Intent 和结果、调用 Activity 的 performResume 方法来执行实际的恢复操作,并更新 ActivityClientRecord 的状态。这里最终会调用到Atcivity的onResume方法。

从activity角度来看,通过performLaunchActivity调用activity中的onCreate、onStart方法,通过handleResumeActivity调用activity中的onResume方法。

AMS.attachApplicationLocked流程分析额外知识补充

3.1 isPersistable方法详细解读

在 Android 系统的源代码中,isPersistable 方法是 ActivityClientRecord 类的一个实例方法,用于确定与之关联的 Activity 是否能够保存其状态。

当 Activity 因为配置更改(例如屏幕旋转)而需要被重新创建时,Android 系统提供了一种机制来保存和恢复 Activity 的状态。这个状态包括 Activity 的成员变量、保存的对话框、输入状态等。如果 Activity 标记为 persistable,这意味着:

  • 状态保存:Activity 可以保存其状态,以便在 Activity 被销毁并重新创建时能够恢复这些状态。
  • 跨配置更改保持状态:当发生配置更改(如屏幕旋转、语言更改等)时,Activity 的状态可以被保存,并且可以在新的 Activity 实例中恢复。
  • 兼容性:对于需要跨不同的系统配置或设备保持状态的 Activity,isPersistable 返回 true 表示该 Activity 需要使用这种状态保存和恢复机制。

在 performLaunchActivity 方法中,isPersistable 方法的返回值用于决定是否需要调用 Instrumentation 的 callActivityOnCreate 方法来传递保存的状态,以及是否需要在 Activity 重新创建后调用 callActivityOnRestoreInstanceState 方法来恢复状态。代码部分如下:

if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {mInstrumentation.callActivityOnCreate(activity, r.state);

简而言之,isPersistable 表示 Activity 是否需要参与状态保存和恢复的过程,这对于确保用户体验的连续性至关重要。如果 Activity 不标记为 persistable,则在配置更改时不会保存和恢复状态,可能会导致用户体验中断。

3.2 Eventlog机制解读

Android 的 EventLog 机制是系统层面的日志记录系统,用于跟踪和记录系统关键事件,如系统启动、应用生命周期事件、系统异常等。这些日志对于系统调试和问题诊断非常有用。

EventLog 机制的工作原理解读如下:

  • 事件标记(Event Tag):每个事件都有一个唯一的标记,通常存储在 /system/etc/event-log-tags 文件中。这些标记定义了事件的类型和它们可以携带的数据类型。
  • 事件写入(Event Write):系统或应用在特定事件发生时,通过调用 EventLog.writeEvent 方法将事件写入日志。这个方法接受一个事件标记和一系列参数,这些参数可以是整数、长整数、字符串等。
  • 日志查看:开发者可以使用 adb logcat 命令查看事件日志。例如,使用 adb logcat -b events 可以查看事件日志。
  • 日志分析:事件日志可以被分析以诊断问题,如性能问题、系统崩溃、应用行为等。

EventLog.writeEvent 方法的逻辑

EventLog.writeEvent 方法接受一个事件标记和一个可变参数列表,这些参数是与事件相关的数据。方法将事件标记和参数封装成一条日志记录,并将其写入系统的事件日志系统。这些日志记录可以被系统或第三方工具读取和分析。

示例代码注释:

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {// ... 省略其他代码 ...if (reallyResume) {try {// 当 Activity 真正恢复时,记录事件日志EventLog.writeEvent(EventLogTags.AM_ACTIVITY_RESUMED, token);} catch (RemoteException ex) {// 忽略远程异常}}// ... 省略其他代码 ...
}

在这个示例中,当一个 Activity 真正恢复时,使用 EventLog.writeEvent 方法记录一个 AM_ACTIVITY_RESUMED 事件,并将 Activity 的 token 作为参数传递。

总之,EventLog 机制是 Android 系统用于记录关键系统事件的工具。通过 EventLog.writeEvent 方法,系统可以在事件发生时写入事件日志,这些日志可以被用来分析和诊断系统问题。

3.3 Dropbox机制解读

Android 的 Dropbox 机制是系统级别的日志记录服务,用于收集和存储系统运行时产生的各种异常信息,如应用程序崩溃(App Crash)、原生崩溃(Native Crash)、应用程序无响应(ANR)、内核恐慌(Kernel Panic)等。这些日志文件通常存储在 /data/system/dropbox 目录下,对于开发者和系统管理员来说,是诊断和分析系统问题的重要资源。

Dropbox 基本逻辑解读如下

  • 事件捕获:当系统发生异常事件时,如应用崩溃或ANR,系统服务会捕获这些事件并生成日志条目。
  • 日志存储:这些日志条目会被写入到 Dropbox 的指定目录中,每个条目通常包含时间戳、事件类型和详细日志内容。
  • 日志管理:Dropbox 服务会根据配置的策略管理这些日志文件,如限制文件数量和大小,以及清理旧文件。
  • 接口访问:系统提供了 DropBoxManager 接口,允许应用通过 Context.DROPBOX_SERVICE 获取 DropBoxManager 实例,并进行日志的添加和查询操作。
  • 广播通知:当新的日志条目被添加到 Dropbox 时,系统会发送一个 android.intent.action.DROPBOX_ENTRY_ADDED 广播,允许有权限的应用监听这个广播并作出响应。
  • 权限控制:访问 Dropbox 日志通常需要 android.permission.READ_LOGS 权限,这通常只授予系统应用或具有特殊权限的应用。

Dropbox 机制的设计目的是为了提供一个可靠的系统级日志记录服务,帮助开发者和系统管理员在应用或系统出现问题时能够快速定位和解决问题。通过集中管理日志文件,Dropbox 确保了日志数据的完整性和可访问性,同时也提供了一种机制来控制日志文件的存储和生命周期。

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

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

相关文章

【Unity精品源码】打造甜蜜的三消游戏:Candy Match 3 Kit

最近总熬夜&#xff0c;肝不好&#xff0c;大家都叫我小心肝。 &#x1f4c2; Unity 开发资源汇总 | 插件 | 模型 | 源码 &#x1f493; 欢迎访问 Unity 打怪升级大本营 在游戏开发的世界中&#xff0c;三消游戏以其简单易上手、趣味性强的特点&#xff0c;一直深受玩家喜爱。…

【HTTPS】深入解析 https

我的主页&#xff1a;2的n次方_ 1. 背景介绍 在使用 http 协议的时候是不安全的&#xff0c;可能会出现运营商劫持等安全问题&#xff0c;运营商通过劫持 http 流量&#xff0c;篡改返回的网页内容&#xff0c;例如广告业务&#xff0c;可能会通过 Referer 字段 来统计是…

第十一章 缓存之更新/穿透/雪崩/击穿

目录 一、什么是缓存 二、缓存更新策略 2.1. 缓存主动更新策略 2.1.1. Cache Aside模式&#xff08;主流&#xff09;‌ 2.1.2. Read/Write Through模式‌ 2.1‌.3. Write Behind模式‌ 2.1.4. 总结 三、缓存穿透 四、缓存雪崩 五、缓存击穿 5.1. 互斥锁实现 5.1.1…

Elasticsearch学习笔记(五)Elastic stack安全配置二

一、手动配置http层SSL 通过前面的配置&#xff0c;我们为集群传输层手动配置了TLS&#xff0c;集群内部节点之间的通信使用手动配置的证书进行加密&#xff0c;但是集群与外部客户端的http层目前还是使用的自动配置&#xff0c;集群中HTTP的通信目前仍然使用自动生成的证书ht…

SQL Injection | MySQL 数据库概述

关注这个漏洞的其他相关笔记&#xff1a;SQL 注入漏洞 - 学习手册-CSDN博客 0x01&#xff1a;MySQL 数据库简介 MySQL 是一个流行的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它基于 SQL &#xff08;Structured Query Language&#xff09;进行操作。My…

Python库matplotlib之六

Python库matplotlib之六 动画FuncAnimation构造器成员函数应用例子 动画 Matplotlib基于其绘图功能&#xff0c;还提供了一个使用动画模块&#xff0c;生成动画的接口。动画是一系列帧&#xff0c;其中每个帧对应于图形上的一个图。 Matplotlib使用两个类来实现动画&#xff…

Backend - MySQL Server、HeidiSQL

目录 一、MySQL Server &#xff08;一&#xff09;官网下载 &#xff08;二&#xff09;安装与配置 二、HeidiSQL软件 &#xff08;一&#xff09;安装 1. 官网下载 2. 打开 3. 使用 &#xff08;1&#xff09;打开服务 &#xff08;2&#xff09;新增数据库 ​&#xff…

python networkx 计算路径A*

import matplotlib.pyplot as plt # 导入 Matplotlib 工具包 import networkx as nx # 导入 NetworkX 工具包 from typing import List# 初始化空的无向图 graph nx.Graph() # 向图中添加多条赋权边: (node1,node2,weight) graph.add_weighted_edges_from([(1, 2, 50),(1, 3…

集合框架05:List接口使用、List实现类、ArrayList使用

视频链接&#xff1a;13.11 ArrayList使用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1zD4y1Q7Fw?p11&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.List接口使用代码举例 package com.yundait.Demo01;import java.util.ArrayList; import java.util.List;pu…

dowhy中反驳实验怎么做?

首先&#xff0c;我们打开最新的dowhy版本网站。 https://www.pywhy.org/dowhy/v0.11.1/index.html 我们主要看标题栏的User Guide和Examples就可以了&#xff0c;如果在User Guide 里找不到使用方法&#xff0c;就去Examples里找例子&#xff0c;里面的数据读取修改为自己的数…

Copilot Coaching新功能铸就Word更强

Copilot 的意思是副驾驶。 现在&#xff0c;您的副驾驶教练来了&#xff1a;Copilot Coaching Copilot Coaching 是 Word 中的一项新 Copilot 功能&#xff0c;可在您查看内容时为您提供支持&#xff0c;以实现语法和拼写之外的改进 - 帮助您澄清想法&#xff0c;并为您提供有…

前端反馈弹框组件封装

一、需求背景 需要针对某个功能进行用户调查反馈&#xff0c;设计一个弹框&#xff0c;进行后端入表记录&#xff0c;以便后期进行数据分析。 二、实现UI 三、代码留存 以vue为例 <template><div class"advice-container"><van-dialogv-model"…

【父子线程传值TransmittableThreadLocal使用踩坑-及相关知识拓展】

文章目录 一.业务背景二.TransmittableThreadLocal是什么&#xff1f;三.问题复现1.定义注解DigitalAngel2.定义切面3.TransmittableThreadLocal相关4.线程池配置信息5.Controller6.Service7.测试结果8.问题分析9 解决办法及代码改造10.最终测试&#xff1a; 四.与 ThreadLocal…

02复写零

复写零 我们先进行异地复写&#xff1a;代码如下 public class Test {public static void main(String[] args) {int []array {1,0,2,3,0,4};duplicateZeros(array);}public static void duplicateZeros(int[] arr) {int [] elemnew int[arr.length];for(int cur0,dest0;des…

QD1-P17 HTML 下拉框标签(select、option)

本节学习 HTML 常用标签&#xff1a;select和option ‍ 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p17 ‍ 知识点1&#xff1a;select标签用法 演示 ​​ HTML <select name"city"><option>北京</option><option>上海</opti…

新版Win32高级编程教程-学习笔记01:应用程序分类

互联网行业 算法研发工程师 目录 新版Win32高级编程教程-学习笔记01&#xff1a;应用程序分类 控制台程序 强烈注意 窗口程序 启动项 程序入口函数 库程序 静态库 动态库程序 几种应用程序的区别 控制台程序 本身没有窗口&#xff0c;其中的doc窗口&#xff0c;是管…

【通信协议讲解】单片机基础重点通信协议解析与总结(IIC,CAN,MODBUS...)

目录 一.IIC总线 基础特性&#xff1a; 配置特性&#xff1a; 时序特性&#xff1a; 二.SPI总线 基础特性&#xff1a; 配置特性&#xff1a; 时序特性&#xff1a; 三.串口通信 基础特性&#xff1a; 配置特性&#xff1a; 时序特性&#xff1a; 四.CAN总线 基础特性…

三款GIS工具多角度对比:免费的倾斜摄影OSGB/3Dtiles编辑转换发布平台

GIS数据处理工具在现代技术与应用中扮演着至关重要的角色&#xff0c;它们不仅是连接原始地理信息与可分析、可视化数据的桥梁&#xff0c;更是推动地理信息系统&#xff08;GIS&#xff09;在各个行业领域深入发展与应用不可或缺的关键工具。选择一款合适的工具直接关系到数据…

HttpURLConnection学习

介绍 HttpURLConnection类是位于java.net包下继承了URLConnection类的一个抽象类&#xff0c;每个 HttpURLConnection 实例都用于发出单个请求。 URL HttpURLConnection的使用需要依赖URL类&#xff0c;URL类位于java.net包下&#xff0c;有很多种构造方法。 HttpURLConnect…

AI引起用人格局变动,个人如何应对这一趋势

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 人工智能的发展带来的就业结构变革&#xf…