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 确保了日志数据的完整性和可访问性,同时也提供了一种机制来控制日志文件的存储和生命周期。

假设你是一个应用开发者,想要记录一些自定义的日志信息到Dropbox中。请注意,下面的示例代码需要在具有相应权限的系统应用或者具有READ_LOGS权限的应用中运行。DropboxLogger的封装类参考代码如下所示:

import android.content.Context;
import android.os.DropBoxManager;public class DropboxLogger {private Context mContext;private DropBoxManager mDropBoxManager;public DropboxLogger(Context context) {this.mContext = context;this.mDropBoxManager = (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);}public void logError(String tag, String message) {if (mDropBoxManager != null) {DropBoxManager.Entry entry = new DropBoxManager.Entry(tag, message.getBytes());mDropBoxManager.addData(tag, message.getBytes(), 0);System.out.println("Logged to Dropbox: " + message);} else {System.out.println("DropBoxManager is not available.");}}
}

在这个示例中,我们创建了一个DropboxLogger类,它提供了一个logError方法,用于将错误信息记录到Dropbox中。这个方法接受一个标签(tag)和消息(message),然后使用DropBoxManager将消息添加到Dropbox中。在系统应用开发中,参考使用方式如下:

public class MyApp extends Application {@Overridepublic void onCreate() {super.onCreate();DropboxLogger logger = new DropboxLogger(this);logger.logError("MY_APP_ERROR", "This is a test error message.");}
}

通过这个示例,可以看到Dropbox机制如何使用,类似于EventLog.writeEvent的方式。这些日志信息对于系统问题诊断非常有用。

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

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

相关文章

【Vue】Vue(八)Vue3.0 使用ref 和 reactive创建响应式数据

ref 创建&#xff1a;基本类型的响应式数据 **作用&#xff1a;**定义响应式变量。语法&#xff1a;let xxx ref(初始值)。**返回值&#xff1a;**一个RefImpl的实例对象&#xff0c;简称ref对象或ref&#xff0c;ref对象的value属性是响应式的。注意点&#xff1a; JS中操作…

《拿下奇怪的前端报错》:1比特丢失导致的音视频播放时长无限增长-浅析http分片传输核心和一个坑点

问题背景 在一个使用MongoDB GridFS实现文件存储和分片读取的项目中&#xff0c;同事遇到了一个令人困惑的问题&#xff1a;音频文件总是丢失最后几秒&#xff0c;视频文件也出现类似情况。更奇怪的是&#xff0c;播放器显示的总时长为无限大。这个问题困扰了团队成员几天&…

Java项目-基于Springboot的应急救援物资管理系统项目(源码+说明).zip

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

工业自动化为什么依赖光耦隔离器 --- 腾恩科技

光耦合器隔离器在工业自动化中必不可少&#xff0c;可确保信号传输&#xff0c;同时保护敏感电子设备和人员免受高压影响。选择合适的光耦合器隔离器取决于对操作环境和隔离要求的了解。本文将重点介绍在为工业应用选择光耦合器隔离器时需要考虑的关键因素。 光耦合器隔离器在工…

上传图片到github上,生成链接在Typora中使用(解决Typora的md文件在分享时的丢失问题)

上传图片到github上,生成链接在Typora中使用(解决Typora的md文件在分享时的丢失问题) 在GitHub上从操作 创建一个 GitHub 仓库: 登录 GitHub,创建一个新的仓库来存储图片。 生成 GitHub 令牌: 在 GitHub 中,前往“Settings” > “Developer settings” > “Pers…

AUTOSAR_EXP_ARAComAPI的5章笔记(12)

☞返回总目录 5.4.6 方法 骨架侧的服务方法是抽象方法&#xff0c;必须由继承骨架的服务实现子类进行重写。让我们来看一下我们服务示例中的 Adjust 方法&#xff1a; /*** 对于所有输出和非空返回参数* 生成一个包含非空返回值和/或输出参数的封装结构。*/ struct AdjustOu…

UE4 材质学习笔记08(雨滴流淌着色器/雨水涟漪着色器)

一.雨滴流淌着色器 法线贴图在红色通道和绿色通道上&#xff0c;那是法线的X轴和Y轴&#xff0c;在蓝色通道中 我有个用于雨滴流淌的蒙版&#xff0c;在Alpha通道中&#xff0c;有个时间偏移蒙版。这些贴图都是可以在PS上制作做来的&#xff0c;雨滴流淌图可以直接用笔刷画出来…

永恒之蓝漏洞

MS17-010是微软于2017年3月发布的一个安全补丁&#xff0c;旨在修复Windows操作系统中的一个严重漏洞&#xff0c;该漏洞被称为“永恒之蓝”&#xff08;EternalBlue&#xff09;。这个漏洞影响了Windows的Server Message Block&#xff08;SMB&#xff09;协议&#xff0c;允许…

Java集合剖析3】ArrayList

目录 拓展 1. 在面试时如何讲解一个集合的底层&#xff1f; 2. IDEA如何查看底层源码&#xff1f; 一、ArrayList底层数据结构 二、插入方法的具体实现 三、ArrayList底层原理总结 拓展 1. 在面试时如何讲解一个集合的底层&#xff1f; 底层的数据结构。插入方法的具体实现。…

vue综合指南(六)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Vuet篇专栏内容:vue综合指南 目录 101、Vue 框架怎么实现对象和数组的监听&#xff1f; 102、Proxy 与 Object.d…

10 分钟使用豆包 MarsCode 帮我搭建一套后台管理系统

以下是「 豆包MarsCode 体验官」优秀文章&#xff0c;作者把梦想揉碎。 十分钟使用豆包 MarsCode 搭建后台管理项目 在这个快节奏的时代&#xff0c;开发者们总是希望能够快速、高效地完成项目的搭建与开发工作。无论是初创企业还是大型公司&#xff0c;后台管理系统都是必不可…

SpringBoot1~~~

目录 快速入门 依赖管理和自动配置 修改自动仲裁/默认版本号 starter场景启动器 自动配置 修改默认扫描包结构 修改默认配置 读取application.properties文件 按需加载原则 容器功能 Configuration Import ​编辑 Conditional ImportResource 配置绑定Configur…

要在 Git Bash 中使用 `tree` 命令,下载并手动安装 `tree`。

0、git bash 安装 git(安装,常用命令,分支操作,gitee,IDEA集成git,IDEA集成gitee,IDEA集成github,远程仓库操作) 1、下载并手动安装 tree 下载 tree.exe 从 tree for Windows 官方站点 下载 tree 的 Windows 可执行文件。tree for Window&#xff1a;https://gnuwin32.source…

鸿蒙应用开发:全面认识鸿蒙系统

前言 随着智能设备的普及和物联网的发展&#xff0c;对操作系统的需求也越来越多样化。鸿蒙操作系统作为一款面向全场景的分布式操作系统&#xff0c;其适用范围非常广泛&#xff0c;从智能手机到家用电器&#xff0c;再到工业设备&#xff0c;都能找到应用场景。特别是在智能…

Nginx如何配置Gzip

Nginx 配置 Gzip 压缩可以显著减小传输的文件大小&#xff0c;提高网页加载速度。以下是在 Nginx 中配置 Gzip 的详细步骤&#xff1a; 一、找到 Nginx 配置文件 Nginx 的配置文件通常位于 /etc/nginx/nginx.conf 或 /usr/local/nginx/conf/nginx.conf&#xff08;取决于 Ngin…

鸿蒙网络编程系列22-Web组件文件上传示例

1. web组件文件上传功能简介 鸿蒙的web组件可以加载网页&#xff0c;如果网页本身具备文件上传功能的话就比较尴尬了&#xff0c;因为html上传文件时&#xff0c;允许用户选择本地文件&#xff0c;但是鸿蒙因为安全性的考虑&#xff0c;只允许操作沙箱中的文件&#xff0c;所以…

物联网的应用以及优势

物联网智能项目涵盖了多个行业领域&#xff0c;随着技术的不断进步和普及&#xff0c;越来越多的应用案例成为主流趋势。此篇文章将概述一些主要的物联网智能项目类别及其优势和日常使用场景&#xff1a; 主流物联网智能项目 1. 智能家居: •优势: 提升居住体验&#xff0c;…

双十一母婴有什么好物推荐?双十一这五款母婴好物不容错过!

随着双十一购物狂欢节的来临&#xff0c;母婴用品市场再次迎来了消费者的热切关注。作为家长们为孩子和自身挑选必需品的重要时刻&#xff0c;母婴用品的质量和安全性无疑成为了关注的焦点。在众多品牌和商品中&#xff0c;我们精心筛选了本年度最受欢迎的母婴用品&#xff0c;…

24/10/14 算法笔记 循环神经网络RNN

RNN: 一种专门用于处理序列数据的神经网络&#xff0c;它能够捕捉时间序列中的动态特征。RNN的核心特点是其循环连接&#xff0c;这允许网络在不同时间步之间传递信息&#xff0c;从而实现对序列数据的记忆和处理能力。 应用的场景&#xff1a; 自然语言处理&#xff08;NLP&…

关于Python AI 编程助手Fitten Code的应用体验以及Python 修改删除 sys.path 路径以实现两个项目代码的合并

一、关于Python AI 编程助手Fitten Code的应用体验 AI现在无孔不入&#xff0c;现在都开始进入到编程中了&#xff0c;有一个能适用多种编译器环境的AI编程插件 Fitten Code。其适配了 Viusal Studio&#xff0c;VS Code(本文使用)&#xff0c;JetBrains 系列(本文使用)以及Vim…