【Android 源码分析】Activity生命周期之onPause
忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
– 服装学院的IT男
本篇已收录于Activity短暂的一生系列
欢迎一起学习讨论Android应用开发或者WMS
V:WJB6995
Q:707409815
正文
Activity 生命周期是学习 Android 必定要掌握的知识点,但是刚入行的同学对于这个概念基本上是靠死记硬背,有一些实际工作经验的同学,在工作中通过实现业务需求或者解决一些BUG基本上是可以知道哪些生命周期对应用户操作的哪一步。
虽然触发生命周期的场景很多,当前只还是以在桌面启动应用来分析,但是完整的跟过这一流程的代码逻辑,就能加深对生命周期本质的理解,其他场景的生命周期切换也不是啥问题。
生命周期系列:
-
Activity生命周期之onPause
-
onCreate,onStart,onResume-1
-
onCreate,onStart,onResume-2
-
Activity生命周期之onStop-1
-
Activity生命周期之onStop-2
-
Activity生命周期之onDestory
本篇会介绍生命周期的执行顺序和 onPause 的执行逻辑
1. 生命周期介绍
回忆一下2个 Activity 的定义:
-
SourceActivity:执行 startActivity 方法的 Activity,也就是发起请求的Activity,当前就是 Launcher 的 Activity
-
TargetActivity:需要被启动的 Activity,当前就是“电话”应用在清单文件配置的 MainActivity
在 Activity.java 中,每个生个周期的执行都有对应的log打印,比如 onPause
# Activity// 默认为false,需要收到打开private static final boolean DEBUG_LIFECYCLE = true;protected void onPause() {if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);......}
打开log开关后,操作一遍就获得到了下面的生命周期日志:
根据这里的日志打印就可以得出以下结论:
-
- 先执行 SourceActivity 的 onPause
-
- 然后依次执行 TargetActivity onCreate,onStart,onResume 三个生命周期
-
- 最后执行 SourceActivity 的 onStop
-
- 冷启动热启动的生命周期执行顺序都是一样的
如果没有条件编译AOSP的同学,搞个Activity在各自的生命周期加上log也能得到这样的一份log
根据之前【Activity启动流程】的知识和这份生命周期的log,可以得到下面这个图:
SystemService 会先触发 SourceActivity 的 onPause,然后才允许启动新的 TargetActivity 。(鲁迅曾说过:俗话说旧的不去新的不来)
SourceActivity 执行了 onPause 的时候,TargetActivity 还没起来,所以说 onPause 的时候,Activity也是可见的,只有知道到 onStop才不可见,因为 TargetActivity 已经显示了。
知道这几个生命周期的执行顺序其实就已经可以开始反向推到调用流程了,但是可以通过 events 日志,把流程具体化,完整流程对应的 events 日志如下:
只有分析流程的时候,按顺序像连线一样找到各个日志打印的地方,形成调用链,那整个生命周期的源码分析就完成了。
这里把 events 日志做了一些简化和加上了注释,如果以后遇到流程异常的,就可以和这份正常的日志对比一下,哪一步开始出问题了,然后去定位问题原因。
// SystemService端创建TargetActivity (writeWmCreateActivity)
03-27 14:41:06.428 27889 28629 I wm_create_activity: [0,253598020,21,com.google.android.dialer/.extensions.GoogleDialtactsActivity,android.intent.action.MAIN,NULL,NULL,270532608]// SystemService端触发SourceActivity的Pause (writeWmPauseActivity)
03-27 14:41:06.431 27889 28629 I wm_pause_activity: [0,51114540,com.android.launcher3/.uioverrides.QuickstepLauncher,userLeaving=true,pauseBackTasks]// SourceActivity应用端将执行onPause (writeWmOnPausedCalled)
03-27 14:41:06.448 28606 28606 I wm_on_paused_called: [51114540,com.android.launcher3.uioverrides.QuickstepLauncher,performPause]// SystemService端把SourceActivity添加进需要stop的集合 (writeWmAddToStopping)
03-27 14:41:06.459 27889 28630 I wm_add_to_stopping: [0,51114540,com.android.launcher3/.uioverrides.QuickstepLauncher,makeInvisible]// SystemService端要真正触发TargetActivity启动 (writeWmRestartActivity)
03-27 14:41:06.487 27889 28630 I wm_restart_activity: [0,253598020,21,com.google.android.dialer/.extensions.GoogleDialtactsActivity]// TargetActivity端将执行onCreate (writeWmOnCreateCalled)
03-27 14:41:06.769 3401 3401 I wm_on_create_called: [253598020,com.google.android.dialer.extensions.GoogleDialtactsActivity,performCreate]// TargetActivity端将执行onStart (writeWmOnStartCalled)
03-27 14:41:06.900 3401 3401 I wm_on_start_called: [253598020,com.google.android.dialer.extensions.GoogleDialtactsActivity,handleStartActivity]// TargetActivity端将执行Resume (writeWmOnResumeCalled)
03-27 14:41:06.911 3401 3401 I wm_on_resume_called: [253598020,com.google.android.dialer.extensions.GoogleDialtactsActivity,RESUME_ACTIVITY]// SystemService端触发SourceActivity的stop (writeWmStopActivity)
03-27 14:41:07.097 27889 30491 I wm_stop_activity: [0,51114540,com.android.launcher3/.uioverrides.QuickstepLauncher]// SourceActivity应用端将执行onStop (writeWmStopActivity)
03-27 14:41:07.119 28606 28606 I wm_on_stop_called: [51114540,com.android.launcher3.uioverrides.QuickstepLauncher,STOP_ACTIVITY_ITEM]
1.1 生命周期事务
Activity各个生命周期的执行被封装成了一个事务,具体的映射关系如下:
LaunchActivityItem onCreate
StartActivityItem Start
ResumeActivityItem onResume
PauseActivityItem Pause
StopActivityItem Stop
ActivityRelaunchItem relaunch
ActivityResultItem result
各个类被定义在 frameworks/base/core/java/android/app/servertransaction/ 路径下:
这些类都实现了 BaseClientRequest 接口,所以都有 preExecute ,execute ,postExecute 3个方法。
其实 execute 表示真正触发应用端执行对应的生命周期,而 preExecute,,postExecute 分别对应执行生命周期前后该做的事,这3个方法的执行循序如下:
preExecute -》execute -》postExecute
这些后续在代码中也能论证。
2. onPause
根据之前的分析知道 SourceActivity 是最先执行的,这个时候 TargetActivity 是还没有启动的。
pause流程对应的事务是 PauseActivityItem ,所以构建这个事务之前的调用逻辑就是 pause 流程在 SystemService 的处理,而 PauseActivityItem::execute 后的调用逻辑就是 pause 流程在应用端的处理。
目标明确,开始分析看代码。
2.1 SystemService 触发 pause 流程
构建 PauseActivityItem 的地方在 TaskFragment::schedulePauseActivity 方法中,在【Activity启动流程1】中知道在流程早起会执行下面这段调用链:
ActivityTaskManagerService::startActivityActivityTaskManagerService::startActivityAsUserActivityTaskManagerService::startActivityAsUserActivityStartController::obtainStarterActivityStarter::executeActivityStarter::executeRequest -- 构建 ActivityRecord --2.1 创建ActivityRecordActivityStarter::startActivityUncheckedActivityStarter::startActivityInner -- 2.2 关键函数startActivityInnerActivityStarter::getOrCreateRootTask -- 2.2.1 创建或者拿到TaskActivityStarter::setNewTask -- 2.2.2 将task与activityRecord 绑定RootWindowContainer::resumeFocusedTasksTopActivities --2.2.3 处理需要显示的Activity
RootWindowContainer::resumeFocusedTasksTopActivities 方法后续的逻辑如下:
RootWindowContainer::resumeFocusedTasksTopActivitiesTask::resumeTopActivityUncheckedLockedTask::resumeTopActivityInnerLockedTaskFragment::resumeTopActivity TaskDisplayArea::pauseBackTasks -- Pause SourceActivity 逻辑 (第一次执行)WindowContainer::forAllLeafTaskTaskFragment::forAllLeafTaskFragmentsTaskFragment::startPausingTaskFragment::startPausingTaskFragment::schedulePauseActivity --构建 PauseActivityItem,这里是触发SourceActivity的pauseActivityTaskManagerService::startProcessAsync -- 创建TargetActivity所在的进程 (第一次执行,且需要启动目标进程)ActivityTaskSupervisor::startSpecificActivity -- 启动TargetActivity(第二次执行,现在有个印象)
需要注意的是,上面的 RootWindowContainer::resumeFocusedTasksTopActivities 触发的逻辑是会执行2次的
第一次调用就是启动流程 startActivity 触发的,执行到 TaskFragment::resumeTopActivity 方法时,触发 SourceActivity 的 pause 流程和创建 TargetActivity 的进程。(应用内启动Activity肯定就不会触发进程创建了)
第二次触发是 SourceActivity 执行完 pause 后,就会触发 completePause 流程,这次执行到 TaskFragment::resumeTopActivity 方法时走的是触发启动 TargetActivity 流程。完整调用链如下:
ActivityClientController::activityPausedActivityRecord::activityPausedTaskFragment::completePauseRootWindowContainer::resumeFocusedTasksTopActivities --分支1 再次执行 resumeFocusedTasksTopActivitiesRootWindowContainer::resumeFocusedTasksTopActivitiesTask::resumeTopActivityUncheckedLockedTask::resumeTopActivityInnerLockedTaskFragment::resumeTopActivityActivityTaskSupervisor::startSpecificActivity -- 启动TargetActivityRootWindowContainer::ensureActivitiesVisible --分支2 确保有Activity显示流程
这里需要注意 completePause 流程再次执行 RootWindowContainer::resumeFocusedTasksTopActivities 方法时内部还会触发 TaskFragment::resumeTopActivity 方法,而这一次触发的是启动 TargetActivity 的流程。
同一个方法因为执行时机不同,走不同的逻辑,这种情况在分析源码的时候非常让人头疼,上面做了一些说明并且提供了调用链。
本篇其实需要注意的只有第一次执行的逻辑处理,因为第二次是 completePause 流程了,触发的是TargetActivity 流程也就是 TargetActivity 的onCreate,onStart,onResume 三个生命周期执行,是下一篇的触发逻辑,当前有个印象即可。
其他的地方在【Activity启动流程】系列都分析过来,目前只关注 pause 逻辑即可,看一下 TaskFragment::resumeTopActivity 方法的代码。
2.1.1 TaskFragment::resumeTopActivity 方法
# TaskFragmentfinal boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,boolean deferPause) {// 这里的next返回下一个需要显示的ActivityActivityRecord next = topRunningActivity(true /* focusableOnly */);...... // 如果跳过的这些逻辑都没执行return,则开始执行resume流程,如果被return了也会打印日志。 // 打印日志,需要显示哪个Activity(也就是SourceActivity)if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);......// 重点* 1. 第一次执行走这,执行SourceActivity的pause流程boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);......if (pausing) {ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to"+ " start pausing");if (next.attachedToProcess()) {......} else if (!next.isProcessRunning()) {// 进程没有运行,则触发异步创建进程。 当前逻辑肯定是执行final boolean isTop = this == taskDisplayArea.getFocusedRootTask();mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,isTop ? HostingRecord.HOSTING_TYPE_NEXT_TOP_ACTIVITY: HostingRecord.HOSTING_TYPE_NEXT_ACTIVITY);}......// 注意,这里会return,所以第一次执行到这就结束了。return true;}......ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next);// 重点*2. 第二次执行的时候走这,SourceActivity已经执行完pause流程了,所以走这启动Activity走这mTaskSupervisor.startSpecificActivity(next, true, true); }
这个方法是极其复杂的, 但是基于前面的铺垫后再看这个方法,我觉得应该目标明确了,由于当前是分析的是 onPause流程,所以只看第一次执行的调用链即可,也就是 pauseBackTasks 方法。
TaskDisplayArea::pauseBackTasks 方法的逻辑从前面的调用链知道会执行 TaskFragment::schedulePauseActivity 方法,这中间的调用逻辑在【Activity启动流程-2】中有详细分析,当前直接看 schedulePauseActivity 方法。
# TaskFragmentvoid schedulePauseActivity(ActivityRecord prev, boolean userLeaving,boolean pauseImmediately, String reason) {// Proto日志ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);try {// 输出events 日志 wm_pause_activityEventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),prev.shortComponentName, "userLeaving=" + userLeaving, reason);// 重点* 构建并执行PauseActivityItemmAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),prev.token, PauseActivityItem.obtain(prev.finishing, userLeaving,prev.configChangeFlags, pauseImmediately));} catch (Exception e) {......}}
-
- 打印第一个 events 日志:wm_pause_activity
-
- 构建执行 PauseActivityItem 事务
SystemService 触发 pause 的逻辑就分析完了,PauseActivityItem 是承上启下的关键线索,看看这个事务做了些什么。
2.2 PauseActivityItem
# PauseActivityItem@Overridepublic void execute(ClientTransactionHandler client, ActivityClientRecord r,PendingTransactionActions pendingActions) {// traceTrace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");// 触发应用进程pauseclient.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, pendingActions,"PAUSE_ACTIVITY_ITEM");Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}@Overridepublic int getTargetState() {return ON_PAUSE;}@Overridepublic void postExecute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {if (mDontReport) {return;}// 自己加的TraceTrace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPaused");// TODO(lifecycler): Use interface callback instead of actual implementation.// 触发 completePause 流程ActivityClient.getInstance().activityPaused(token);Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}
-
- 先执行 execute ,最终触发应用端 Activity 的 onPause
-
- 然后执行 postExecute ,这里的逻辑是由 SystemService 执行 completePause 流程
这2个发生是同步触发,但是具体触发逻辑所在的进程都不一样,所以肯定是异步执行的,没有强绑定关系, 换句话说就是 应用端的 Activity 是不是真的顺利执行完了 onPause,和 completePause 流程没有必然关系。
这点很重要,后面看 onStop 流程的时候还会提到。
activityPaused 触发的后续逻辑就是 2.1 小节提到的第二次执行 TaskFragment::resumeTopActivity 方法来启动 Activity 的触发点。
当然本篇还是看 pause 逻辑,所以直接看 execute 方法触发的应用端是如何处理的。
2.3 应用端执行pause
应用端调用链如下:
ActivityThread::handlePauseActivityActivityThread::performPauseActivityActivityThread::performPauseActivityIfNeededInstrumentationcallActivityOnPauseActivity::performPauseActivity::onPause -- onPauseActivityClientRecord::setState -- 设置状态ON_PAUSE(4)
代码流程也很简单,看一下。
# ActivityThread@Overridepublic void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving,int configChanges, PendingTransactionActions pendingActions, String reason) {......r.activity.mConfigChangeFlags |= configChanges;performPauseActivity(r, finished, reason, pendingActions);......}private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,PendingTransactionActions pendingActions) {......// Pre-Honeycomb apps always save their state before pausingfinal boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();if (shouldSaveState) {// 安卓3之前的版本是在onPause执行执行OnSaveInstanceState的callActivityOnSaveInstanceState(r);}// onPause 逻辑performPauseActivityIfNeeded(r, reason);......}
稍微提到了一下 OnSaveInstanceState 的调用时机,Honeycomb 是安卓3, 现在基本上没有这种手机了,了解一下即可,继续看主流程。
# ActivityThreadInstrumentation mInstrumentation;private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {// 如果已经 paused 就不许要处理了if (r.paused) {// You are already paused silly...return;}try {r.activity.mCalled = false;// 主流程mInstrumentation.callActivityOnPause(r.activity);if (!r.activity.mCalled) {throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent)+ " did not call through to super.onPause()");}} catch ......// 需要留意一下 这里的状态是生命周期,不是ActivityRecord的那个状态 r.setState(ON_PAUSE);}
又看到了 Instrumentation
# Instrumentationpublic void callActivityOnPause(Activity activity) {activity.performPause();}<!----># Activity final void performPause() {// 1. Trace beginif (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performPause:"+ mComponent.getClassName());}dispatchActivityPrePaused();mDoReportFullyDrawn = false;// 2. Fragments处理mFragments.dispatchPause();mCalled = false;// 3. 重点* onPauseonPause();// 4. events日志 wm_on_paused_calledEventLogTags.writeWmOnPausedCalled(mIdent, getComponentName().getClassName(),"performPause");mResumed = false;......// Trace endTrace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);}
到这里 onPause 流程就分析完了, 这里有几个需要注意的点
-
- 有Trace
-
- 对Fragments处理
-
- 最重要的触发了 Activity 的 onPause
-
- 打印日志 wm_on_paused_called ,表示应用端已经pause了
到这里 SourceActivity 的 Pause 流程已经完全结束了。
3 ActivityClientRecord 是什么
在 ActivityThread::performPauseActivityIfNeeded 中看到在触发 onPause 执行后还会调用 ActivityClientRecord::setState 方法传递了一个 ON_PAUSE 进去。
这句代码大概意思猜着就是设置一个 ON_PAUSE 的状态保存起来。
可是 SystemService 端有个 ActivityRecord 来记录 Activity的一些信息,那这个 ActivityClientRecord 又是什么呢?
从命名上来看,好像和 ActivityRecord 类似,是 Client(客户端)这边记录 Activity 的一个类,实际上也确实如此,ActivityClientRecord 内部也保存了很多与 Activity 启动和运行相关的各种参数,如Intent、token、binder引用等。以后遇到再说,不过目前我们关注生命周期相关的,先看看 setState 这个方法。
# ActivityThread$ActivityClientRecord// 是否paused@UnsupportedAppUsageboolean paused;// 是否stoped@UnsupportedAppUsageboolean stopped;// 生命周期状态@LifecycleStateprivate int mLifecycleState = PRE_ON_CREATE;/** Update the current lifecycle state for internal bookkeeping. */// 更新内部保存的生命周期状态public void setState(@LifecycleState int newLifecycleState) {mLifecycleState = newLifecycleState;switch (mLifecycleState) {case ON_CREATE:// 刚刚创建,不可见,不可交互paused = true; stopped = true; break;case ON_START:// 还没完全进入前台,但已经可见,不可交互paused = true; stopped = false; break;case ON_RESUME:// 可见,可交互paused = false;stopped = false; break;case ON_PAUSE:// 未被完全遮挡,可见,不可交互paused = true; stopped = false; break;case ON_STOP:// 被完全遮挡,所以不可见,也不可交互paused = true; stopped = true; break;}}
这段代码有3个变量:
-
- paused: 表示当前Activity 是否处于暂停(pause)状态,也就是能不能和用户交互
-
- stopped : 表示当前Activity是否处于 停止(stop)状态,也就是能不能被用户可见
-
- mLifecycleState :这是一个被LifecycleState注解的变量,说明取值只能是内部定义的几个生命周期
关于pause和stop的区别可能有些小伙伴还很迷糊,这里表达一下我的理解:
onPause和onStop都是不能和用户交互的,但是 onPause 的时候,Activity 还是可见的,比如出现一个dialog主题的Activity,这个时候下面的 Activity 就是pause,但不是stop,因为它还能被用户看见。从log来看,SourceActivity 的onPause是最先执行的,但是onStop却要等 TargetActivity 执行完 onResume 再执行
抛开代码从实际情况来说,SourceActivity 要不可见,就只有等 TargetActivity 完全显示(onResume)的时候才可以算 onStop 了
@LifecycleState 注解是定义在 ActivityLifecycleItem下的
# ActivityLifecycleItem@Retention(RetentionPolicy.SOURCE)public @interface LifecycleState{}public static final int UNDEFINED = -1;public static final int PRE_ON_CREATE = 0;public static final int ON_CREATE = 1;public static final int ON_START = 2;public static final int ON_RESUME = 3;public static final int ON_PAUSE = 4;public static final int ON_STOP = 5;public static final int ON_DESTROY = 6;public static final int ON_RESTART = 7;
可以看到 从 1-7 刚好是和 7个生命周期对应的,这里需要注意对应的几个int值,后面的代码还要用到。
当前 pause 流程传递了一个 ON_PAUSE 进去 那当前 mLifecycleState 的值就是 ON_PAUSE(4)。
4. 总结
本篇首先明确了启动 Activity 生命周期的先后执行顺序,然后介绍了完整流程的 events 日志。
然后知道了 startActivity 流程最开始会执行到 TaskFragment::resumeTopActivity 方法,在这里会触发 SourceActivity 的 pause 流程,同时还会创建 TargetActivity 所在的进程(有必要的话)
我对关键方法加上了一个 Trace 后,当前桌面冷启动应用场景得到的Trace如下:
-
- 可以看到 TaskFragment::resumeTopActivity 执行了2次,上图是第一次
-
- 执行进程在 system_service
-
- 执行了 pauseBackTasks 流程,也就是 SourceActivity 的 pause
-
- 触发了TargetActivity 所在的进程创建
pauseBackTasks 方法后面会执行 TaskFragment::schedulePauseActivity 方法,在这个方法中有2个点:
-
- 打印日志:wm_pause_activity
-
- 构建执行 PauseActivityItem
而 PauseActivityItem 也做了2件事:
-
- 先执行 execute ,最终触发应用端 Activity 的 onPause
-
- 然后执行 postExecute ,这里的逻辑是由 SystemService 执行 completePause 流程。
本篇关心的是 execute 方法除非的逻辑,在应用段执行到 onPause 的逻辑,打印了 wm_on_paused_called 日志,流程也就分析完了。
最后再补充一下 postExecute 方法触
-
- 执行进程还是在 system_service
-
- 这里的 resumeTopActivity 上面是 activityPaused,也就是我在 PauseActivityItem::postExecute 加是 trace
-
- 虽然还会执行 pauseBackTasks 但是下面没有构建 pause 事务的逻辑了
-
- 这一次执行还会触发prepareAppTransition方法
-
- 最终要的是要创建 TargetActivity 了,下一篇是基于这个基础
相关文章:

【Android 源码分析】Activity生命周期之onPause
忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。 – 服装…...

IAR全面支持国科环宇AS32X系列RISC-V车规MCU
全球领先的嵌入式系统开发软件解决方案供应商IAR与北京国科环宇科技股份有限公司(以下简称”国科环宇”)联合宣布,最新版本IAR Embedded Workbench for RISC-V将全面支持国科环宇AS32X系列RISC-V MCU,双方将共同助力中国汽车行业开…...

Java题集(从入门到精通)04
此系列文章收录大量Java经典代码题(也可以算是leetcode刷题指南),希望可以与大家一起努力学好Java。3、2、1,请看! 目录 一、北京地铁计价程序 二、人名币兑换 三、各位数字之和 一、北京地铁计价程序 【问题描述…...

《西北师范大学学报 (自然科学版)》
《西北师范大学学报》(自然科学版) (CN 62-1087/N, ISSN 1001-988X)是甘肃省教育厅主管、西北师范大学主办的综合性自然科学学术期刊,1942年3月创刊,双月刊,逢单月15日出版。主要刊登全国高校和科研院所在数学、计算机与信息科学、物理学、化…...

Oracle SQL语句没有过滤条件,究竟是否会走索引??
答案是:可能走索引也可能不走索引,具体要看列的值可不可为null,Oracle不会为所有列的nullable属性都为Y的sql语句走索引。 例子: create table t as select * from dba_objects; CREATE INDEX ix_t_name ON t(object_id, objec…...

Java中参数传递:按值还是按引用?
目录 1. 按值传递 vs 按引用传递 1.1 基本数据类型:按值传递 1.2 对象引用:按引用传递 2. 拓展知识:理解 Java 的内存模型 2.1 栈内存的作用 2.2 堆内存的作用 2.3 参数传递的底层机制 3. 总结 在软件开发的世界里,Java 是…...

Linux忘记root用户密码怎么重设密码
直接说步骤: 1.重启客户机 2.在选择内核页面快速按e键,进入编辑模式 进入后应该是这个样子 在这里只能按上下键切换行 找到Linux16这里 3.按右方向键切换到行尾,也就是UTF-8处,在后面添加一个空格,然后加上这段话 …...

【Web】复现n00bzCTF2024 web题解(全)
目录 File Sharing Portal 方法一: 方法二: Focus-on-yourSELF Passwordless File Sharing Portal 附件的Dockerfile给了这么一段 # Add the cron job to the crontab RUN mkdir /etc/cron.custom RUN echo "*/5 * * * * root rm -rf /app…...

仿RabbitMQ实现消息队列客户端
文章目录 客⼾端模块实现订阅者模块信道管理模块异步⼯作线程实现连接管理模块生产者客户端消费者客户端 客⼾端模块实现 在RabbitMQ中,提供服务的是信道,因此在客⼾端的实现中,弱化了Client客⼾端的概念,也就是说在RabbitMQ中并…...

CSS | 面试题:你知道几种移动端适配方案?
目录 一、自适应和响应式 二、为什么要做移动端适配? 三、当前流行的几种适配方案 (1) 方案一:百分比设置(不推荐) (2) 方案二:rem 动态设置 font-size px 与 rem 的单位换算 手动换算 less/scss函数 webpac…...

【web安全】——XSS漏洞
1.XSS漏洞基础 1.1.漏洞成因 XSS(Cross-site scripting)被称为跨站脚本攻击,由于与层叠样式表的缩写一样,因此被缩写为XSS.XSS漏洞形成的原因是网站/程序对前端用户的输入过滤不严格,导致攻击者可以将恶意的is/html代码注入到网页中&#x…...

JAVA基础语法 Day11
一、Set集合 Set特点:无序(添加数据的顺序和获取出的数据顺序不一致),不重复,无索引 public class demo1 {public static void main(String[] args) {//1.创建一个集合//HashSet特点:无序,不重…...

知识图谱入门——7:阶段案例:使用 Protégé、Jupyter Notebook 中的 spaCy 和 Neo4j Desktop 搭建知识图谱
在 Windows 环境中结合使用 Protg、Jupyter Notebook 中的 spaCy 和 Neo4j Desktop,可以高效地实现从自然语言处理(NLP)到知识图谱构建的全过程。本案例将详细论述环境配置、步骤实现以及一些扩展和不足之处。 文章目录 1. 环境准备1.1 Neo4j…...

【AIGC】VoiceControl for ChatGPT指南:轻松开启ChatGPT语音对话模式
博客主页: [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 💯前言💯安装VoiceControl for ChatGPT插件💯如何使用VoiceControl for ChatGPT进行语音输入VoiceControl for ChatGPT快捷键注意点 💯VoiceControl for C…...

基于SpringCloud的微服务架构下安全开发运维准则
为什么要进行安全设计 微服务架构进行安全设计的原因主要包括以下几点: 提高数据保护:微服务架构中,服务间通信频繁,涉及到大量敏感数据的交换。安全设计可以确保数据在传输和存储过程中的安全性,防止数据泄露和篡改。…...

vue的图片显示
通过参数 调用方法 进行显示图片 方法一: 方法二:...

深度学习06:线性回归模型
线性回归:从理论到实现 1. 什么是线性回归? 线性回归是一种用于预测因变量(目标值)和自变量(特征值)之间关系的基本模型。它假设目标值(y)是特征值(x)的线性…...

Angular ng-state script 元素的生成机制介绍
ng-state 的生成过程是在 Angular SSR 中非常关键的部分。为了让客户端能够接管服务器渲染的页面状态,Angular 在服务器端需要将应用的当前状态保存下来,并将其嵌入到返回的 HTML 中。这样,客户端在接管时就可以直接使用这些状态,…...

小程序-全局数据共享
目录 1.什么是全局数据共享 2. 小程序中的全局数据共享方案 MboX 1. 安装 MobX 相关的包 2. 创建 MobX 的 Store 实例 3. 将 Store 中的成员绑定到页面中 4. 在页面上使用 Store 中的成员 5. 将 Store 中的成员绑定到组件中 6. 在组件中使用 Store 中的成员 1.什么是全…...

vSAN01:vSAN简介、安装、磁盘组、内部架构与调用关系
目录 传统的共享存储vSAN存储OSA的系统要求vSAN安装vSAN集群vSAN skyline healthvSAN与HA磁盘组混合磁盘架构全闪磁盘架构 vSAN对象vSAN内部架构 传统的共享存储 通过隔离的存储网络使得不同的ESXi主机访问独立的存储设备。需要前期投入较高的资金单独采购存储、网络可以单独规…...

Apache NiFi最全面试题及参考答案
目录 解释什么是Apache NiFi以及它的主要用途。 NiFi 的数据处理流程是怎样的? NiFi 的架构包括哪些组件? 解释 NiFi 的 “FlowFile” 概念及其组成部分。 NiFi 的 “Processor” 是什么?有哪些类型? 如何在 NiFi 中创建一个新的数据流? NiFi 的 “Connection” 有…...

基于Docker部署最新版本SkyWalking【10.1.0版本】
文章目录 前言前置条件一、创建Docker 网络二、部署 SkyWalking OAP 服务器三 部署 SkyWalking UI四 查看日志4.1. 查看 SkyWalking OAP 日志4.2. 查看 SkyWalking UI 日志 五 停止并删除容器结论 前言 由于本地的 JDK 版本与 SkyWalking 对应的 JDK 版本不一致,为…...

如何在 Ubuntu 18.04 上使用 LEMP 安装 WordPress
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 WordPress 是互联网上最流行的 CMS(内容管理系统)。它允许您在 MySQL 后端和 PHP 处理的基础上轻松设置灵…...

shadcn-vue 快速入门(2)
components.json 关于项目配置 components.json 文件保存了项目的配置信息。 我们使用该文件了解项目的基本设定,并生成定制化的组件以适应项目需求。 注意:components.json 文件是可选的,仅在使用 CLI 向项目添加组件时才需要。如果使用复…...

Oracle数据恢复—异常断电导致Oracle数据库报错的数据恢复案例
Oracle数据库故障: 机房异常断电后,Oracle数据库启库报错:“system01.dbf需要更多的恢复来保持一致性,数据库无法打开”。数据库没有备份,归档日志不连续。用户方提供了Oracle数据库的在线文件,需要恢复zxf…...

数据结构-4.1.特殊矩阵的压缩存储
一.一维数组的存储结构: 1.知道一维数组的起始地址,就可以求出任意下标对应的元素所在的地址; 2.注:如果数组下标从1开始,上述公式的i就要改为i-1; 3.数组里的元素类型相同,因此所占空间也相同…...

Hive数仓操作(十四)
一、Hive的DDL语句 在 Hive 中,DDL(数据定义语言)语句用于数据库和表的创建、修改、删除等操作。以下是一些重要的 DDL 语句: 1. 创建数据库和表 创建数据库 CREATE DATABASE IF NOT EXISTS database_name;创建表 CREATE TABLE …...

SpringBoot技术:实现古典舞在线交流平台的秘诀
摘 要 随着互联网技术的发展,各类网站应运而生,网站具有新颖、展现全面的特点。因此,为了满足用户古典舞在线交流的需求,特开发了本古典舞在线交流平台。 本古典舞在线交流平台应用Java技术,MYSQL数据库存储数据&#…...

自动驾驶系列—全面解析自动驾驶线控制动技术:智能驾驶的关键执行器
🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…...

YOLO11改进|卷积篇|引入可变核卷积AKConv
目录 一、AKConv卷积1.1AKConv卷积介绍1.2AKConv核心代码 五、添加MLCA注意力机制5.1STEP15.2STEP25.3STEP35.4STEP4 六、yaml文件与运行6.1yaml文件6.2运行成功截图 一、AKConv卷积 1.1AKConv卷积介绍 AKConv允许卷积参数的数量以线性方式增加或减少,而不是传统的…...