【Android 14源码分析】Activity启动流程-3
忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
– 服装学院的IT男
本篇已收录于Activity短暂的一生系列
欢迎一起学习讨论Android应用开发或者WMS
V:WJB6995
Q:707409815
正文
由于篇幅原因,整个启动流程分为以下3篇进行分析:
Activity启动流程-1
Activity启动流程-2
Activity启动流程-3
本篇介绍阶段三的逻辑,这部分的分析上篇阶段二的触发点是同级的,也就是在阶段一中 TaskFragment::resumeTopActivity 触发的。
进程是怎么创建的不是当前分析的重点,所以快速过一遍流程。
1 阶段三–触发进程创建
执行pause后会执行 ActivityTaskManagerService::startProcessAsync,最后也是通过 ActivityManagerService来触发启动进程的。
看看AMS这块执行进程创建的调用流程
# ActivityTaskManagerServicevoid startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,String hostingType) {try {if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"+ activity.processName);}// 发送消息,启动进程,调用 ActivityManagerInternal::startProcessfinal Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,isTop, hostingType, activity.intent.getComponent());mH.sendMessage(m);} finally {Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}}
这里通过 Handler 来完成,ActivityManagerInternal::startProcess 的实现在 ActivityManagerService 的内部类 LocalService 中。
# ActivityManagerService$LocalServicepublic void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,boolean isTop, String hostingType, ComponentName hostingName) {try {if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"+ processName);}synchronized (ActivityManagerService.this) {// 继续调用startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,new HostingRecord(hostingType, hostingName, isTop),ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,false /* isolated */);}} finally {Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);}}@GuardedBy("this")final ProcessRecord startProcessLocked(String processName,ApplicationInfo info, boolean knownToBeDead, int intentFlags,HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,boolean isolated) {return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,false /* isSdkSandbox */, 0 /* sdkSandboxClientAppUid */,null /* sdkSandboxClientAppPackage */,null /* ABI override */, null /* entryPoint */,null /* entryPointArgs */, null /* crashHandler */);}
流程走到 ProcessList::startProcessLocked 。
# ProcessListboolean startProcessLocked(......) {......mService.mProcStartHandler.post(() -> handleProcessStart(app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,requiredAbi, instructionSet, invokeWith, startSeq)); ...... }private void handleProcessStart(......) {创建一个用于启动进程的 Runnable 对象final Runnable startRunnable = () -> {try { // 调用 startProcess 方法启动进程,并获取启动结果 ProcessStartResultfinal Process.ProcessStartResult startResult = startProcess(app.getHostingRecord(),entryPoint, app, app.getStartUid(), gids, runtimeFlags, zygotePolicyFlags,mountExternal, app.getSeInfo(), requiredAbi, instructionSet, invokeWith,app.getStartTime());// 在锁定 ActivityManagerService 后,处理进程启动结果synchronized (mService) {// 更新应用的状态,如设置PID,更新生命周期状态等handleProcessStartedLocked(app, startResult, startSeq);}} catch (RuntimeException e) {......异常处理}};......}
-
- 通过 ProcessList::startProcess 来启动应用进程
-
- 启动玩之后 ProcessList::handleProcessStartedLocked 会更新应用的状态,如设置PID,更新生命周期状态等
ProcessList::handleProcessStartedLocked 经过几次重载会调用下面的方法
# ProcessListActivityManagerService mService = null;boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,long expectedStartSeq, boolean procAttached) {......StringBuilder buf = mStringBuilder;buf.append("Start proc ");buf.append(pid);buf.append(':');buf.append(app.processName);buf.append('/');UserHandle.formatUid(buf, app.getStartUid());if (app.getIsolatedEntryPoint() != null) {buf.append(" [");buf.append(app.getIsolatedEntryPoint());buf.append("]");}buf.append(" for ");buf.append(app.getHostingRecord().getType());if (app.getHostingRecord().getName() != null) {buf.append(" ");buf.append(app.getHostingRecord().getName());}// 输出日志mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.getStartUid());......}
1.1创建进程小结
创建进行逻辑我没深入了解过,所以简单看了下调用逻辑,以 AMS::reportUidInfoMessageLocked 结束的原因是因为其会打印下面的这个创建进程的关键日志:
07-26 19:19:05.477 8737 8782 I ActivityManager: Start proc 19643:com.example.myapplication/u0a198 for next-top-activity {com.example.myapplication/com.example.myapplication.MainActivity}
经常看日志看启动了哪个进程搜的日志就是在这里打印的。
这部分的调用链如下:
ActivityTaskManagerService::startProcessAsyncActivityManagerService$LocalService::startProcessActivityManagerService::startProcessLockedProcessList::startProcessLockedProcessList::handleProcessStartProcessList::startProcess -- 启动进程ProcessList::handleProcessStartedLockedActivityManagerService::reportUidInfoMessageLocked -- 打印日志
2. 阶段三–应用进程创建
进程创建完成后会执行 ActivityThread::main 方法,所以应用端进程创建结束的逻辑从这个方法开始分析。
2.1 应用端处理
# ActivityThread// ApplicationThread 是 AMS 作为 C 端时,与应用进程通信的方式final ApplicationThread mAppThread = new ApplicationThread();public static void main(String[] args) {......// 主线程LooperLooper.prepareMainLooper();......ActivityThread thread = new ActivityThread();// 下一步thread.attach(false, startSeq);// 主线程LooperLooper.loop();}private void attach(boolean system, long startSeq) {......final IActivityManager mgr = ActivityManager.getService();try {//重点 *2. 将mAppThread告知AMS,用于AMS与应用进程通信mgr.attachApplication(mAppThread, startSeq);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}......}
应用端的调用链比较简单:
ActivityThread::mainLooper::prepareMainLooperActivityThread::initActivityThread::attachActivityManagerService::attachApplication -- 跨进程Looper::loop
应用进程(电话)创建完毕后,在 main 方法里会执行 attach 就是要将自己的信息告知AMS,毕竟 AMS 是管理模块。
2.2 system_service端处理
system_service 端 ActivityManagerService 知道有进程启动了,这个行为也可能会触发系统组显示逻辑的改变,所以比如也会做响应处理
2.1 调用链
ActivityManagerService::attachApplicationActivityManagerService::attachApplicationLockedActivityThread::bindApplicationActivityTaskManagerService.LocalService::attachApplication WindowContainer::forAllRootTasks --- 省略forAllRootTasks等固定堆栈Task::forAllRootTasksWindowContainer::forAllActivitiesActivityRecord::forAllActivitiesRootWindowContainer.AttachApplicationHelper::testRootWindowContainer.AttachApplicationHelper::testActivityTaskSupervisor::realStartActivityLocked -- 构建LaunchActivityItem
接上一篇知道如果进程启动了 ActivityTaskSupervisor::startSpecificActivity 就会走进去ActivityTaskSupervisor::realStartActivityLocked 。
但是可能会好奇怎么就知道要执行应用 MainActivity 到 onCreate 就一定是在这个方法里呢? 调试方法有很多,比如加 log ,打堆栈,但是对应这个逻辑比较简单的是,需要执行 Activity 启动到 onCreate 的控制在 LaunchActivityItem 中,而 LaunchActivityItem在 framework 的引用除了本身,就只有在 ActivityTaskSupervisor。
2.2 主流程
# ActivityManagerService// 当应用进程调用attachApplication 执行public final void attachApplication(IApplicationThread thread, long startSeq) {if (thread == null) {throw new SecurityException("Invalid application interface");}synchronized (this) {// 获取 应用进程的信息后执行attachApplicationLockedint callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();// 执行attachApplicationLocked(thread, callingPid, callingUid, startSeq);Binder.restoreCallingIdentity(origId);}}private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {// 需要启动应用的进程数据ProcessRecord app;......if (pid != MY_PID && pid >= 0) {synchronized (mPidsSelfLocked) {// 通过mPidsSelfLocked获取app = mPidsSelfLocked.get(pid);}......} ...... // 触发ActivityThread::bindApplication 逻辑if (app.getIsolatedEntryPoint() != null) {......} else if (instr2 != null) {// bindApplication...... } else {// 重点* 1. bindApplicationthread.bindApplication(processName, appInfo,app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,providerList, null, profilerInfo, null, null, null, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.isPersistent(),new Configuration(app.getWindowProcessController().getConfiguration()),app.getCompat(), getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, autofillOptions, contentCaptureOptions,app.getDisabledCompatChanges(), serializedSystemFontMap,app.getStartElapsedTime(), app.getStartUptime());}......if (!mConstants.mEnableWaitForFinishAttachApplication) {// 重点* 2. finishAttachApplicationInner(startSeq, callingUid, pid);} else {app.setPendingFinishAttach(true);}......}
ActivityManagerService::finishAttachApplicationInner 是 U 把原来的逻辑提取新增的方法。
# ActivityManagerServicepublic ActivityTaskManagerInternal mAtmInternal;private void finishAttachApplicationInner(long startSeq, int uid, int pid) {......if (normalMode) {try {// 重点 触发构建 LaunchActivityItem 流程didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());} catch (Exception e) {Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);badApp = true;}}......}
这里是触发 LaunchActivityItem 的流程主线, mAtmInternal是 ATMS 的内部类 LocalService 。
# ActivityTaskManagerService$LocalService@Overridepublic boolean attachApplication(WindowProcessController wpc) throws RemoteException {......return mRootWindowContainer.attachApplication(wpc);......}
流程来到 RootWindowContainer 预感要开始处理窗口显示逻辑。
# RootWindowContainerprivate final AttachApplicationHelper mAttachApplicationHelper = new AttachApplicationHelper();boolean attachApplication(WindowProcessController app) throws RemoteException {try {return mAttachApplicationHelper.process(app);} finally {mAttachApplicationHelper.reset();}}# RootWindowContainer// 实现 Consumer 接口private class AttachApplicationHelper implements Consumer<Task>, Predicate<ActivityRecord> {......boolean process(WindowProcessController app) throws RemoteException {mApp = app;for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {// 重点* 调用每个容器的 forAllRootTasksgetChildAt(displayNdx).forAllRootTasks(this);......}......}......}
这里看到传递了“this”,所以 AttachApplicationHelper 必然实现了 Consumer 接口, 直接看其 accept 实现即可。
# RootWindowContainer$AttachApplicationHelperprivate class AttachApplicationHelper implements Consumer<Task>, Predicate<ActivityRecord> {......boolean process(WindowProcessController app) throws RemoteException {mApp = app;for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {// 重点*1. 调用每个容器的 forAllRootTasksgetChildAt(displayNdx).forAllRootTasks(this);......}......}@Overridepublic void accept(Task rootTask) {......if (rootTask.getVisibility(null /* starting */)== TASK_FRAGMENT_VISIBILITY_INVISIBLE) {// 如果Task 不可见则不需要处理return;}// 执行 topRunningActivitymTop = rootTask.topRunningActivity();// 重点*2. 执行accept 让容器下的每个 ActivityRecord 执行 testrootTask.forAllActivities(this);}@Overridepublic boolean test(ActivityRecord r) {// 判断 ActivityRecord 是否满足需要启动条件if (r.finishing || !r.showToCurrentUser() || !r.visibleIgnoringKeyguard|| r.app != null || mApp.mUid != r.info.applicationInfo.uid|| !mApp.mName.equals(r.processName)) {return false;}try {// 重点*3. 执行 realStartActivityLocked 尝试实际启动 Activityif (mTaskSupervisor.realStartActivityLocked(r, mApp,mTop == r && r.getTask().canBeResumed(r) /* andResume */,true /* checkConfig */)) {mHasActivityStarted = true;}} catch (RemoteException e) {......}return false;}}
这部分的逻辑也就是一路执行,比较疑惑的点也许是进入 AttachApplicationHelper::proces 方法后的几个 Lambda 表达式容易绕晕。
简单梳理一下,首先是从 RootWindowContainer 开始执行的,目的就是想要执行到所有 ActivityRecord 然后判断一下它的情况是不是需要启动对应的 Activity 。
-
- AttachApplicationHelper::proces 里的 “getChildAt” 对应的是每个屏幕,也就是 DisplayContent ,然后再遍历其下的每个 RootTask ,让其执行 accept 函数
-
- AttachApplicationHelper::accept 目前是对象是 Task ,准确的说是 RootTask 。如果这个 Task 是可见的,则又开始遍历其下的所有 ActivityRecord ,让其执行 test 函数
-
- AttachApplicationHelper::test 方法是真正干活的地方,所有一堆条件判断这个 ActivityRecord 是否满足
- r.finishing:活动是否正在结束
- !r.showToCurrentUser() :活动是否对当前用户可见
- !r.visibleIgnoringKeyguard :活动是否可见,即使有屏幕保护器
- r.app != null :活动的应用程序是否已经存在
- mApp.mUid != r.info.applicationInfo.uid :应用程序的UID是否与预期的不同
- !mApp.mName.equals(r.processName):应用程序的名称是否与活动的进程名称不同
感觉比较重要的就是前面3个条件,这个 ActivityRecord 是不是需要显示给用户,如果需要则执行 ActivityTaskSupervisor::realStartActivityLocked 试图启动 Activity 。
3. 阶段三总结
阶段三的流程相对来逻辑简单一些,知道个调用链就好,流程目的就是执行 ActivityTaskSupervisor::realStartActivityLocked 。
这部分的堆栈如下图:
加上应用端的调用链,完成调用链如下:
ActivityThread::mainLooper::prepareMainLooperActivityThread::initActivityThread::attachActivityManagerService::attachApplication -- 跨进程ActivityManagerService::attachApplicationLockedActivityThread::bindApplicationActivityManagerService::finishAttachApplicationInnerActivityTaskManagerService$LocalService::attachApplicationRootWindowContainer::attachApplicationRootWindowContainer$AttachApplicationHelper::process -- 开始遍历WindowContainer::forAllRootTasks --- 省略forAllRootTasks等固定堆栈Task::forAllRootTasks -- 1. 遍历所有root TaskRootWindowContainer$AttachApplicationHelper::accept -- 1.1 root Task执行acceptWindowContainer::forAllActivities -- 2. 遍历下面的所有ActivityRecordActivityRecord::forAllActivities RootWindowContainer$AttachApplicationHelper::test -- 2.1 ActivityRecord执行testActivityTaskSupervisor::realStartActivityLocked -- 试图启动ActivityLooper::loop
对应时序图:
大概流程图如下:
这一阶段主要就是应用进程启动后,试图拉起对应的 Activity ,能不能启动的条件就是没有正在 pause 的 Activity 了。
主要逻辑就是触发 ActivityTaskSupervisor::realStartActivityLocked
4. 阶段四–真正启动Activity
阶段四其实就是触发应用端创建 Activity 。
4.1 realStartActivityLocked
这个方法是要真去触发 Activity 启动的,根据前面的流程图,阶段二,三的最终就是想执行到这个方法里面,来触发 Activity 启动。
这个方法的最终目的就是通过事务执行 LaunchActivityItem 和 PauseActivityItem ,也就是会触发应用端 TargetActivity 启动,并执行生命周期到 onCreate 和 onResume 。
# ActivityTaskSupervisor boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,boolean andResume, boolean checkConfig) throws RemoteException {// 重点* 1. 判断是否执行完了pause if (!mRootWindowContainer.allPausedActivitiesComplete()) {// While there are activities pausing we skipping starting any new activities until// pauses are complete. NOTE: that we also do this for activities that are starting in// the paused state because they will first be resumed then paused on the client side.// 如果还有Activity没完成pause,则打印日志并returnProtoLog.v(WM_DEBUG_STATES,"realStartActivityLocked: Skipping start of r=%s some activities pausing...",r);return false;}// 重点* 2. 表示ActivityRecord已连接到相应的进程r.setProcess(proc);......// event日志: wm_restart_activity EventLogTags.writeWmRestartActivity(r.mUserId, System.identityHashCode(r),task.mTaskId, r.shortComponentName);......// 重点* 3.1 创建事务,用于Activity启动// Create activity launch transaction.final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.token);final boolean isTransitionForward = r.isTransitionForward();// 获取Activity所在 TaskFragment Tokenfinal IBinder fragmentToken = r.getTaskFragment().getFragmentToken();// 重点* 3.2 将构建的 LaunchActivityItem 添加到 clientTransaction 中clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),System.identityHashCode(r), r.info,// TODO: Have this take the merged configuration instead of separate global// and override configs.mergedConfiguration.getGlobalConfiguration(),mergedConfiguration.getOverrideConfiguration(), r.compat,r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),results, newIntents, r.takeOptions(), isTransitionForward,proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken));// 重点* 3.3 设置预期的最终状态为 ResumeActivityItemfinal ActivityLifecycleItem lifecycleItem;if (andResume) {// Resume逻辑,启动走的这。 表示需要执行到onCreatelifecycleItem = ResumeActivityItem.obtain(isTransitionForward);} else {// Pause 逻辑lifecycleItem = PauseActivityItem.obtain();}clientTransaction.setLifecycleStateRequest(lifecycleItem);// 重点* 3.4 执行事务mService.getLifecycleManager().scheduleTransaction(clientTransaction);......}
根据代码的标注解释:
-
- 根据注释,如果有 Activity 正在 pause 则不允许任何 Activity 启动。换句话说就是想要启动一个 Activity 必须其他的 Activity 需要 pause 的都完成了 pause 流程
-
- 对应上一篇提到的 ActivityRecord::attachedToProcess 方法如果需要返回 true ,则必须在这里执行 setProcess 方法,否则 ActivityRecord 下的 app 变量就是 null
-
- 这里就是开始真正执行启动 Activity 的地方了,是通过事务执行的,分为以下几步
- 3.1 构建一个事务
- 3.2 设置 LaunchActivityItem ,这一步会将 Activity 的生命周期执行到 onCreate
- 3.3 设置 ResumeActivityItem ,这一步会将 Activity 的生命周期执行到 onResume
- 3.4 执行事务
后续逻辑就是在应用端执行 Activity 的创建,以及生命周期处理了,这部分本篇大概看一遍流程,以分析到 Activity 的创建为止。
4.2 创建Activity
这部分的调用链如下:
LaunchActivityItem::executeActivityThread::handleLaunchActivityActivityThread::performLaunchActivityInstrumentation::newActivity -- 创建ActivityActivity::attach -- 处理Window相关Window::initWindow::setWindowManagerInstrumentation::callActivityOnCreate -- onCreate流程Activity::performCreateActivity::onCreate --over
时序图图下:
接下来看 LaunchActivityItem::execute
# LaunchActivityItem public void execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,mTaskFragmentToken);client.handleLaunchActivity(r, pendingActions, null /* customIntent */);Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}
这里的 client 是 ClientTransactionHandler 类型, 而 ActivityThread 是 ClientTransactionHandler 子类。
重点是这边创建了 ActivityClientRecord ,第一个参数就是我们要找的token
逻辑来到应用进程 ActivityThread
# ActivityThreadpublic Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {......final Activity a = performLaunchActivity(r, customIntent);......}private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {......Activity activity = null;try {// 重点* 1. 通过Instrumentation 反射创建Activityjava.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);......}try {......// 重点* 2. 执行 attach 流程activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.activityConfigCallback,r.assistToken, r.shareableActivityToken);......if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {重点* 3. onCreate流程mInstrumentation.callActivityOnCreate(activity, r.state);}}......}
这里有3个重点:
-
- Activity 是通过反射创建的。 到这里其实 Activity 启动流程基本分析完了
-
- 执行 Activity::attach 。这里会触发创建 Window
-
- 触发 Activity::onCreate
Activity 已经创建好了,那就剩下 onCreate 了。
# Instrumentationpublic void callActivityOnCreate(Activity activity, Bundle icicle) {prePerformCreate(activity);// onCreate流程activity.performCreate(icicle);postPerformCreate(activity);}# Activityfinal void performCreate(Bundle icicle) {performCreate(icicle, null);}final void performCreate(Bundle icicle, PersistableBundle persistentState) {......if (persistentState != null) {onCreate(icicle, persistentState);} else {// 执行onCreateonCreate(icicle);}......}
写过应用的都知道,默认都是一个参数的onCreate。
至此,Activity 启动流程分析完毕。
在 ActivityTaskSupervisor::realStartActivityLocked 方法看到给事务加了个 ResumeActivityItem , 因为 LaunchActivityItem 只是创建,但是创建完成后,需要执行到对应的生命周期。
正常情况都是希望执行到onResume,所以会设置 ResumeActivityItem 。
4.3 阶段四小结
无论是阶段二还是阶段三触发了 ActivityTaskSupervisor::realStartActivityLocked 方法,并且还满足启动 Activity 条件,则会触发应用端进程 Activity 的创建和生命周期的执行。
相关文章:

【Android 14源码分析】Activity启动流程-3
忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。 – 服装…...
Javascript客户端时间与服务器时间
在Java代码中使用new Date(),获取的是本机时间; 但是在Javascript 中使用new Date(),获取的却是访问该页面的客户端时间。 这样,就可能会出现一个问题:我的电脑时间比正常时间要快,我访问一个页面&#x…...
系统架构设计师教程 第11章 11.4 边缘计算概述 笔记
11.4 边缘计算概述 ★★☆☆☆ 11.4.1 边缘计算概念 边缘计算将数据的处理、应用程序的运行甚至一些功能服务的实现,由 网络中心下放到网络边缘的节点上。在网络边缘侧的智能网关上就近采集并且处理数据,不需要上传原生数据。 11.4.2 边缘计算的定义 1…...
CSS全解析
文章目录 CSS全解析一、CSS是什么二、基本语法规范三、引入方式(一)内部样式表(二)行内样式表(三)外部样式 四、代码风格(一)样式格式(二)样式大小写…...

一款基于 Java 的可视化 HTTP API 接口快速开发框架,干掉 CRUD,效率爆炸(带私活源码)
平常我们经常需要编写 API,但其实常常只是一些简单的增删改查,写这些代码非常枯燥无趣。 今天给大家带来的是一款基于 Java 的可视化 HTTP API 接口快速开发框架,通过 UI 界面编写接口,无需定义 Controller、Service、Dao 等 Jav…...

CSS3渐变
一、线性渐变 通过background-image: linear-gradient(...)设置线性渐变 语法: linear-gradient(direction,color1,color2, . . ) direction:渐变方向,默认从上到下,可选值: 简单选取: ① to right&…...

Emissive CEO Fabien Barati谈《消失的法老》背后的故事:XR大空间体验的创新与未来
在最近的一次播客访谈中,虚拟现实之声(Voices of VR)的主持人Kent Bye与Emissive公司的联合创始人兼CEO Fabien Barati进行了深入交流。Emissive是全球顶级的VR大空间体验制作商之一,以其沉浸式探险项目如《永恒的巴黎圣母院》和《胡夫地平线》而闻名。以下是这次访谈的核心…...
mysql设置表的某一个字段每天定时清零
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...

实例分割、语义分割和 SAM(Segment Anything Model)
实例分割、语义分割和 SAM(Segment Anything Model) 都是图像处理中的重要技术,它们的目标是通过分割图像中的不同对象或区域来帮助识别和分析图像,但它们的工作方式和适用场景各有不同。 1. 语义分割(Semantic Segme…...

深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)
前言 前几天在看论文,打算复现,论文用到了LSTM,故这一篇文章是小编学LSTM模型的学习笔记;LSTM感觉很复杂,但是结合代码构建神经网络,又感觉还行;本次学习的案例数据来源于GitHub,在…...

《精通开关电源设计》笔记一
重点 效率 纹波 环路响应 尺寸,从静态到动态的研究方法,假设开关电源稳态运行,以电感为中心,根据半导体器件(mos管或二极管)分段分析电路的状态,工具有电路原理和能量守恒 影响效率的主要是开关损耗,所以…...
QLoRA代码实战
QLoRA原理参考: BiliBili:4bit量化与QLoRA模型训练 zhihu:QLoRA(Quantized LoRA)详解 下载llama3-8b模型 from modelscope import snapshot_download model_dir snapshot_download(LLM-Research/Meta-Llama-3-8B-In…...
pyqt QGraphicsView 以鼠标为中心进行缩放
注意几个关键点: 1. 初始化 class CustomGraphicsView(QGraphicsView):def __init__(self, parentNone):super(CustomGraphicsView, self).__init__(parent)self.scene QGraphicsScene()self.setScene(self.scene)self.setGeometry(0, 0, 1024, 600)# 以下初始化…...

FPGA-Vivado-IP核-逻辑分析仪(ILA)
ILA IP核 背景介绍 在用FPGA做工程项目时,当Verilog代码写好,我们需要对代码里面的一些关键信号进行上板验证查看。首先,我们可以把需要查看的这些关键信号引出来,接好线通过示波器进行实时监测,但这会用到大量的线材…...

基于webComponents的纯原生前端框架
我本人的个人开发web前端前框架xui,正在开发中,业已完成50%的核心开发工作,并且在开发过程中逐渐完善. 目前框架未采用任何和市面上框架模式,没有打包过程,实现真实的开箱即用。 当然在开发过程中也会发现没有打包工…...
OpenCV-背景建模
文章目录 一、背景建模的目的二、背景建模的方法及原理三、背景建模实现四、总结 OpenCV中的背景建模是一种在计算机视觉中从视频序列中提取出静态背景的技术。以下是对OpenCV背景建模的详细解释: 一、背景建模的目的 背景建模的主要目标是将动态的前景对象与静态的…...
一个简单的摄像头应用程序6
主要改进点: 使用 ThreadPoolExecutor 管理多线程: 使用 concurrent.futures.ThreadPoolExecutor 来管理多线程,这样可以更高效地处理图像。 在 main 函数中创建一个 ThreadPoolExecutor,并在每个循环中提交图像处理任务。 减少…...

Pikachu-目录遍历
目录遍历,跟不安全文件上传下载有差不多; 访问 jarheads.php 、truman.php 都是通过 get 请求,往title 参数传参; 在后台,可以看到 jarheads.php 、truman.php所在目录: /var/www/html/vul/dir/soup 图片…...
用Python实现基于Flask的简单Web应用:从零开始构建个人博客
解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 前言 在现代Web开发中,Python因其简洁、易用以及丰富的库生态系统,成为了许多开发者的首选编程语言。Flask作为一个轻量级的Python Web框架,以其简洁和灵活性深受开…...

IDEA的lombok插件不生效了?!!
记录一下,防止找不到解决方案,已经遇到好几次了 前面啰嗦的多,可以直接跳到末尾的解决方法,点击一下 问题现场情况 排查过程 确认引入的依赖正常 —》🆗 idea 是否安装了lombok插件 --》🆗 貌似没有问题…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...