ActivityManagerService Activity的启动流程(2)

ActivityManagerService Activity的启动流程

简述

提到ActivityManagerService,最重要的流程之一就是启动Activity了。
这个流程比较复杂:

  • 启动activity的调用链很长。
  • 业务逻辑很多,activity启动有很多flag,例如FLAG_ACTIVITY_NEW_TASK,FLAG_ACTIVITY_CLEAR_TOP等等。
  • 需要在app进程和systemserver之间来回binder调用。
    同时activity启动还有两种不同的情况,一种是要启动的Activity进程已经存在了,只是新启动一个Activity,例如在app内部页面跳转。而另一种是要启动的Activity进程不存在,需要新启动一个进程,比如从桌面启动一个进程。

app启动Activity

在这里插入图片描述

1.1 Activity.startActivityForResult
Activity.startActivity最终也会走到Activity.startActivityForResult,只不过requestCode会传-1,我们就从这里开始.
这里的mParent是历史版本的遗留产物,应该是被Fragment替代了,这里可以不用管。
调用了Instrumentation.execStartActivity

  public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) { options = transferSpringboardActivityOptions(options);// 调用Instrumentation.execStartActivity,详见1.2Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);if (ar != null) {mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(),ar.getResultData());}if (requestCode >= 0) {mStartedActivity = true;}cancelInputsAndStartExitTransition(options);} else {// ...}
}

1.2 Instrumentation.execStartActivity
mActivityMonitors一般用于CTS测试,除了mActivityMonitors插桩的逻辑,主要逻辑是调用ATMS服务,启动Activity

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {IApplicationThread whoThread = (IApplicationThread) contextThread;Uri referrer = target != null ? target.onProvideReferrer() : null;if (referrer != null) {intent.putExtra(Intent.EXTRA_REFERRER, referrer);}// ...mActivityMonitors,一般用于CTS测试try {intent.migrateExtraStreamToClipData(who);intent.prepareToLeaveProcess(who);// 调用ATMS启动activity,详见1.3int result = ActivityTaskManager.getService().startActivity(whoThread,who.getOpPackageName(), who.getAttributionTag(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()), token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);// 通知mActivityMonitors返回结果notifyStartActivityResult(result, options);// 处理返回值,如果没有启动成功抛出对应异常checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;
}

1.3 ActivityTaskManagerService.startActivity
直接调用了startActivityAsUser

public final int startActivity(IApplicationThread caller, String callingPackage,String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,Bundle bOptions) {// 详见1.4return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,UserHandle.getCallingUserId());
}

1.4 ActivityTaskManagerService.startActivityAsUser
调用了重载函数startActivityAsUser

public int startActivityAsUser(IApplicationThread caller, String callingPackage,String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,Bundle bOptions, int userId) {return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,true /*validateIncomingUser*/);
}

1.5 ActivityTaskManagerService.startActivityAsUser
使用所有参数构造一个ActivityStarter,然后调用ActivityStarter.execute

private int startActivityAsUser(IApplicationThread caller, String callingPackage,@Nullable String callingFeatureId, Intent intent, String resolvedType,IBinder resultTo, String resultWho, int requestCode, int startFlags,ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);assertPackageMatchesCallingUid(callingPackage);enforceNotIsolatedCaller("startActivityAsUser");// ...userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");// 详见1.6return getActivityStartController().obtainStarter(intent, "startActivityAsUser").setCaller(caller) // 调用者对应的binder token.setCallingPackage(callingPackage) // 调用者包名.setCallingFeatureId(callingFeatureId).setResolvedType(resolvedType) // MIME类型.setResultTo(resultTo) // startActivityForResult里接受返回结果的Activity.setResultWho(resultWho).setRequestCode(requestCode) // startActivityForResult里面的requestCode.setStartFlags(startFlags) // 启动的flag.setProfilerInfo(profilerInfo) .setActivityOptions(opts) // 启动Activity的option.setUserId(userId) // 设置userId.execute(); // 详见1.6}

1.6 ActivityStarter.execute
主要是判断activityInfo是否为空来判断之前Intent是否解析,如果没有解析则需要在这里解析出需要启动的Activity信息,然后调用executeRequest进行实际的启动。
除此之外还记录了一些信息以便重启后可以恢复一些数据。判断是否改变了Configuration,如果需要则通知Configuraion改变,进行Configuraion计算以及通知。

int execute() {try {// ...// 如果activityInfo为空,则说明之前没有解析Intent,就在这里解析,将action等解析为需要启动的Activity信息。if (mRequest.activityInfo == null) {mRequest.resolveActivity(mSupervisor);}if (mRequest.intent != null) {String intentAction = mRequest.intent.getAction();String callingPackage = mRequest.callingPackage;if (intentAction != null && callingPackage != null&& (Intent.ACTION_REQUEST_SHUTDOWN.equals(intentAction)|| Intent.ACTION_SHUTDOWN.equals(intentAction)|| Intent.ACTION_REBOOT.equals(intentAction))) {// 记录关机检测点,以便重启后可以恢复一些状态。ShutdownCheckPoints.recordCheckPoint(intentAction, callingPackage, null);}}int res;synchronized (mService.mGlobalLock) {// 检测Configuration是否需要变化final boolean globalConfigWillChange = mRequest.globalConfig != null&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();if (rootTask != null) {rootTask.mConfigWillChange = globalConfigWillChange;}// ...try {// 具体启动的逻辑,详见1.7res = executeRequest(mRequest);} finally {mRequest.logMessage.append(" result code=").append(res);Slog.i(TAG, mRequest.logMessage.toString());mRequest.logMessage.setLength(0);}Binder.restoreCallingIdentity(origId);if (globalConfigWillChange) {// ...// 通知Configuration变化mService.updateConfigurationLocked(mRequest.globalConfig, null, false);}// ... 记录启动消耗时间等信息// 等待启动结果if (mRequest.waitResult != null) {mRequest.waitResult.result = res;res = waitResultIfNeeded(mRequest.waitResult, mLastStartActivityRecord,launchingState);}return getExternalResult(res);}} finally {onExecutionComplete();}
}  

1.7 executeRequest
这里主要是一些预处理,resultRecord的判定,然后是对Intent中的信息进行解析,解析结果主要是ResolveInfo,里面有一个ActivityInfo。
ResolveInfo里面有用于Intent筛选的信息,如IntentFilter.还有一些图标,labels等信息。
ResolveInfo持有一个ActivityInfo,里面是Activity的一些信息,如launchMode,taskAffinity(acitivity启动的Task),启动activity所需的permission等。
这些信息几乎都是来源于AndroidManifest.xml,所以这里解析其实是通过PackageManagerService,我们这里就不细看了。
此外还有一些特殊场景的处理,如语音会话的启动,启动需要先启动用户确认授权Activity等。
然后就是构建ActivityRecord,这个在WMS章节提过,Activity在system_server侧就是一个ActivityRecord,会在WMS等窗口树上,就是这里创建的。
最后调用了startActivityUnchecked,进一步做启动Activity的操作。

private int executeRequest(Request request) {// ... 一些局部变量声明// ...ActivityRecord sourceRecord = null;ActivityRecord resultRecord = null;// 如果requestCode大于0,且SourceRecord没有finish,则说明这次启动activity结束后是需要给resultRecord返回结果的。if (resultTo != null) {sourceRecord = ActivityRecord.isInAnyTask(resultTo);if (sourceRecord != null) {if (requestCode >= 0 && !sourceRecord.finishing) {resultRecord = sourceRecord;}}}final int launchFlags = intent.getFlags();// FLAG_ACTIVITY_FORWARD_RESULT作用:假如A启动B,B使用FLAG_ACTIVITY_FORWARD_RESULT启动C,则C返回的结果给A。if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {if (requestCode >= 0) {SafeActivityOptions.abort(options);return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;}resultRecord = sourceRecord.resultTo;if (resultRecord != null && !resultRecord.isInRootTaskLocked()) {resultRecord = null;}resultWho = sourceRecord.resultWho;requestCode = sourceRecord.requestCode;sourceRecord.resultTo = null;if (resultRecord != null) {resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);}if (sourceRecord.launchedFromUid == callingUid) {callingPackage = sourceRecord.launchedFromPackage;callingFeatureId = sourceRecord.launchedFromFeatureId;}}// 找不到处理Intent的类if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {err = ActivityManager.START_INTENT_NOT_RESOLVED;}// 找不到Intent对应的Activity类if (err == ActivityManager.START_SUCCESS && aInfo == null) {err = ActivityManager.START_CLASS_NOT_FOUND;}// ... 语音会话启动,需要鉴权,处理兼容性问题// ... Intent权限检查if (request.allowPendingRemoteAnimationRegistryLookup) {checkedOptions = mService.getActivityStartController().getPendingRemoteAnimationRegistry().overrideOptionsIfNeeded(callingPackage, checkedOptions);}if (mService.mController != null) {try {Intent watchIntent = intent.cloneFilter();abort |= !mService.mController.activityStarting(watchIntent,aInfo.applicationInfo.packageName);} catch (RemoteException e) {mService.mController = null;}}mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,callingFeatureId);// ...// 这个ACTION_REVIEW_PERMISSIONS是给用户确认是否授于XXX权限if (aInfo != null) {if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(aInfo.packageName, userId)) {final IIntentSender target = mService.getIntentSenderLocked(ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId,callingUid, userId, null, null, 0, new Intent[]{intent},new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT| PendingIntent.FLAG_ONE_SHOT, null);Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);// ...}}// 处理临时应用程序,安装成功或者安装失败提示。if (rInfo != null && rInfo.auxiliaryInfo != null) {intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent,callingPackage, callingFeatureId, verificationBundle, resolvedType, userId);resolvedType = null;callingUid = realCallingUid;callingPid = realCallingPid;// The ephemeral installer shouldn't get any permission grants// intended for the original destinationintentGrants = null;aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);}if (callerApp == null && realCallingPid > 0) {final WindowProcessController wpc = mService.mProcessMap.getProcess(realCallingPid);if (wpc != null) {callerApp = wpc;}}// 构建ActivityRecordfinal ActivityRecord r = new ActivityRecord.Builder(mService).setCaller(callerApp).setLaunchedFromPid(callingPid).setLaunchedFromUid(callingUid).setLaunchedFromPackage(callingPackage).setLaunchedFromFeature(callingFeatureId).setIntent(intent).setResolvedType(resolvedType).setActivityInfo(aInfo).setConfiguration(mService.getGlobalConfiguration()).setResultTo(resultRecord).setResultWho(resultWho).setRequestCode(requestCode).setComponentSpecified(request.componentSpecified).setRootVoiceInteraction(voiceSession != null).setActivityOptions(checkedOptions).setSourceRecord(sourceRecord).build();mLastStartActivityRecord = r;WindowProcessController homeProcess = mService.mHomeProcess;boolean isHomeProcess = homeProcess != null&& aInfo.applicationInfo.uid == homeProcess.mUid;if (balCode != BAL_BLOCK && !isHomeProcess) {mService.resumeAppSwitches();}// 调用startActivityUnchecked,详见1.8mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,request.voiceInteractor, startFlags, checkedOptions,inTask, inTaskFragment, balCode, intentGrants, realCallingUid);if (request.outActivity != null) {request.outActivity[0] = mLastStartActivityRecord;}return mLastStartActivityResult;
}

1.8 startActivityUnchecked
处理启动动画相关的逻辑。调用startActivityInner

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, ActivityOptions options, Task inTask,TaskFragment inTaskFragment, @BalCode int balCode,NeededUriGrants intentGrants, int realCallingUid) {int result = START_CANCELED;final Task startedActivityRootTask;// 创建Activity启动动画的Transactionfinal TransitionController transitionController = r.mTransitionController;Transition newTransition = transitionController.isShellTransitionsEnabled()? transitionController.createAndStartCollecting(TRANSIT_OPEN) : null;RemoteTransition remoteTransition = r.takeRemoteTransition();try {mService.deferWindowLayout();transitionController.collect(r);try {Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");// 详见1.9result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,startFlags, options, inTask, inTaskFragment, balCode,intentGrants, realCallingUid);} finally {Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);// 处理result,同时会触发启动动画。startedActivityRootTask = handleStartResult(r, options, result, newTransition,remoteTransition);}} finally {mService.continueWindowLayout();}postStartActivityProcessing(r, result, startedActivityRootTask);return result;
}

1.9 startActivityInner
这里会处理Activity启动的flag等参数,处理新启动的Activity和WMS相关的逻辑(即调整WMS树),具体有以下的操作:

  • 选择启动的Activity的DisplayContent

  • 判断是否有可以复用的Task,如果没有则新增Task

  • 调整Task位置,比如移动到最上层。

  • 如果需要,则将ActivityRecord添加到对应的Task上
    最后调用resumeFocusedTasksTopActivities,尝试唤醒Focus的Activity。(后面就是Activity声明周期相关的逻辑了)

    int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
    int startFlags, ActivityOptions options, Task inTask,
    TaskFragment inTaskFragment, @BalCode int balCode,
    NeededUriGrants intentGrants, int realCallingUid) {
    // 设置初始化状态。主要是计算activity需要启动在哪个屏幕上,如果启动的时候没有指定display,就是这里计算的结果一般是sourceActivity所在的屏幕或者主屏
    // 这里计算的diplay不一定是最终的display,如果后续发现有复用Task之类的情况,可能会更新这个display
    // 除此之外还有一些option里面的参数处理(如指定了taskId),以及根据Activity launch mode更新launch flag等等。
    setInitialState(r, options, inTask, inTaskFragment, startFlags, sourceRecord,
    voiceSession, voiceInteractor, balCode, realCallingUid);
    // 计算launch flag,会影响到后面是否需要new task或者敷用task等等。
    computeLaunchingTaskFlags();
    mIntent.setFlags(mLaunchFlags);

      // ...// 根据Activity的LaunchMode以及mLaunchFlags等信息,计算是否有可以复用的Taskfinal Task reusedTask = getReusableTask();// ...// computeTargetTask根据mLaunchFlags和sourceRecord等信息,计算是否已经有存在的Task用来启动目标的Activity// (比如mLaunchFlags有FLAG_ACTIVITY_NEW_TASK,表示要新增Task,computeTargetTask一般就会返回null)final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();final boolean newTask = targetTask == null;mTargetTask = targetTask;// 会根据计算出来的targetTask重新计算需要将Activity启动到哪个屏幕computeLaunchParams(r, sourceRecord, targetTask);// 合法性检查int startResult = isAllowedToStart(r, newTask, targetTask);if (startResult != START_SUCCESS) {// ...return startResult;}if (targetTask != null) {// 数量检查,由于每个Activity都有Surface,是系统资源,所以要约束app,每个Task有数量限制if (targetTask.getTreeWeight() > MAX_TASK_WEIGHT_FOR_ADDING_ACTIVITY) {// ...targetTask.removeImmediately("bulky-task");return START_ABORTED;}// ...}final ActivityRecord targetTaskTop = newTask? null : targetTask.getTopNonFinishingActivity();if (targetTaskTop != null) {// 如果Activity的LaunchMode是SingleInstance,检查一下其他的Task有没有这个activity,有就需要销毁if (LAUNCH_SINGLE_INSTANCE == mLaunchMode && mSourceRecord != null&& targetTask == mSourceRecord.getTask()) {final ActivityRecord activity = mRootWindowContainer.findActivity(mIntent,mStartActivity.info, false);if (activity != null && activity.getTask() != targetTask) {activity.destroyIfPossible("Removes redundant singleInstance");}}// 判断是否需要加入ActivityRecord(可能是复用原来存在的),还会把targetTask放到目标屏幕最上面。startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants);if (startResult != START_SUCCESS) {return startResult;}} else {mAddingToTask = true;}if (mTargetRootTask == null) {mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,mOptions);}// 如果需要newTask,根据Affiliate新建一个Taskif (newTask) {final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)? mSourceRecord.getTask() : null;setNewTask(taskToAffiliate);} else if (mAddingToTask) {// 否则直接将启动activity添加addOrReparentStartingActivity(targetTask, "adding to task");}// ...final Task startedTask = mStartActivity.getTask();// ...mRootWindowContainer.startPowerModeLaunchIfNeeded(false /* forceSend */, mStartActivity);final boolean isTaskSwitch = startedTask != prevTopTask;mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,mOptions, sourceRecord);if (mDoResume) {final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked();if (!mTargetRootTask.isTopActivityFocusable()|| (topTaskActivity != null && topTaskActivity.isTaskOverlay()&& mStartActivity != topTaskActivity)) {// 如果activity不是focusable,不能设置为focus,但是仍然要设置可见性。mTargetRootTask.ensureActivitiesVisible(null /* starting */,0 /* configChanges */, !PRESERVE_WINDOWS);mTargetRootTask.mDisplayContent.executeAppTransition();} else {// 需要将Task移动到最上面if (!mAvoidMoveToFront && mTargetRootTask.isTopActivityFocusable()&& !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {mTargetRootTask.moveToFront("startActivityInner");}// 这里resume,详见1.10mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);}}mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask);// Update the recent tasks list immediately when the activity startsmSupervisor.mRecentTasks.add(startedTask);mSupervisor.handleNonResizableTaskIfNeeded(startedTask,mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask);// ...画中画相关逻辑return START_SUCCESS;
    

    }

1.10 RootWindowContainer.resumeFocusedTasksTopActivities
这里需要resume Activity。最终都会调用需要resume的Activity所在的rootTask的resumeTopActivityUncheckedLocked。

boolean resumeFocusedTasksTopActivities(Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,boolean deferPause) {// 如果有其他activity还在resume,直接returnif (!mTaskSupervisor.readyToResume()) {return false;}boolean result = false;// 如果focus的task就是targetRootTask,就调用resumeTopActivityUncheckedLockedif (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea()|| getTopDisplayFocusedRootTask() == targetRootTask)) {// 详见1.11result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions,deferPause);}// 遍历每个DisplayContentfor (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {final DisplayContent display = getChildAt(displayNdx);final boolean curResult = result;boolean[] resumedOnDisplay = new boolean[1];// 遍历所有Taskdisplay.forAllRootTasks(rootTask -> {final ActivityRecord topRunningActivity = rootTask.topRunningActivity();if (!rootTask.isFocusableAndVisible() || topRunningActivity == null) {return;}if (rootTask == targetRootTask) {// 如果是targetRootTaskresumedOnDisplay[0] |= curResult;return;}if (topRunningActivity.isState(RESUMED)&& topRunningActivity == rootTask.getDisplayArea().topRunningActivity()) {rootTask.executeAppTransition(targetOptions);} else {// 这里会对每个Activity进行判断,如果是target则代表需要resume,会调用Activity对应Task的resumeTopActivityUncheckedLocked,详见1.11// 如果activity需要pause,调用scheduleTransaction通知端侧resumedOnDisplay[0] |= topRunningActivity.makeActiveIfNeeded(target);}});result |= resumedOnDisplay[0];// 前面都没有resume成功if (!resumedOnDisplay[0]) {final Task focusedRoot = display.getFocusedRootTask();// 调用focus task的resumeTopActivityUncheckedLockedif (focusedRoot != null) {// 详见1.11result |= focusedRoot.resumeTopActivityUncheckedLocked(target, targetOptions);} else if (targetRootTask == null) {// 如果没有focus task,就resume home activityresult |= resumeHomeActivity(null /* prev */, "no-focusable-task",display.getDefaultTaskDisplayArea());}}}return result;
}

1.11 Task.resumeTopActivityUncheckedLocked
如果是leafTask,直接调用resumeTopActivityInnerLocked,否则递归调用resumeTopActivityUncheckedLocked,遍历子节点,最终会调用可以focus并且有RunningActivity的leafTask的resumeTopActivityInnerLocked。

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options,boolean deferPause) {// 防止重入if (mInResumeTopActivity) {return false;}boolean someActivityResumed = false;try {mInResumeTopActivity = true;// 如果是leafTask,直接调用Task的resumeTopActivityInnerLockedif (isLeafTask()) {if (isFocusableAndVisible()) {// 详见1.12someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause);}} else {// 否则递归调用resumeTopActivityUncheckedLockedint idx = mChildren.size() - 1;while (idx >= 0) {final Task child = (Task) getChildAt(idx--);if (!child.isTopActivityFocusable()) {continue;}if (child.getVisibility(null /* starting */)!= TASK_FRAGMENT_VISIBILITY_VISIBLE) {if (child.topRunningActivity() == null) {continue;}break;}someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options,deferPause);if (idx >= mChildren.size()) {idx = mChildren.size() - 1;}}}final ActivityRecord next = topRunningActivity(true /* focusableOnly */);if (next == null || !next.canTurnScreenOn()) {checkReadyForSleep();}} finally {mInResumeTopActivity = false;}return someActivityResumed;
}

1.12 Task.resumeTopActivityInnerLocked
遍历当前Task的所有的TaskFragment,调用Top且可resume的TaskFragment的resumeTopActivity

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options,boolean deferPause) {if (!mAtmService.isBooting() && !mAtmService.isBooted()) {return false;}final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */);if (topActivity == null) {return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options);}final boolean[] resumed = new boolean[1];final TaskFragment topFragment = topActivity.getTaskFragment();// 调用TaskFragment的resumeTopActivity,详见1.13resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);forAllLeafTaskFragments(f -> {if (topFragment == f) {return;}if (!f.canBeResumed(null /* starting */)) {return;}resumed[0] |= f.resumeTopActivity(prev, options, deferPause);}, true);return resumed[0];
}

1.13 TaskFragment.resumeTopActivity
首先会检查是否有activity正在pause,如果有就直接返回,等待pause完成再继续操作。
然后需要检查是否需要pause activity,我们知道如果activity A跳转activity B,会先执行A的pause,再resume B,所以这里如果发现之前的activity没有pause,就先执行pause操作。(如果发生了pause操作,就会顺带检查一下ativity B所在的进程是否启动,如果没有启动就会在这里触发启动,并行进行节约时间,关于启动进程的逻辑我们下一个章节再介绍)
如果发现顶部的activity已经pause了(往往是第二次进这个方法,app pause完成后通知systemserver,systemserver重新执行resumeTopActivity),这个时候就可以进行resume操作。
如果要启动的Activity是复用之前已经创建过的,next.attachedToProcess()为true,这时候主要做的事是修改ActivityRecord的状态到RESUMED,更新Activity可见效以及config,然后通知app侧执行对应的声明周期。
而如果没有复用的Activity,这里是刚刚新建的Activity,则会调用startSpecificActivity来启动Activity。
关于通知App更新生命周期LifecycleManager的逻辑我们会单独有一篇文章来介绍。

final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,boolean deferPause) {ActivityRecord next = topRunningActivity(true /* focusableOnly */);if (next == null || !next.canResumeByCompat()) {return false;}next.delayedResume = false;// ...如果没有正在pause的activty,继续流程final TaskDisplayArea taskDisplayArea = getDisplayArea();// 如果top activity已经resume,不做什么操作if (mResumedActivity == next && next.isState(RESUMED)&& taskDisplayArea.allResumedActivitiesComplete()) {// 确保topactivity visibletaskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,false /* preserveWindows */, true /* notifyClients */);// 确保切换已经执行executeAppTransition(options);// ...return false;}// 如果topactivity是paused,并且在sleep,那什么都不用做if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {executeAppTransition(options);return false;}// ...处理多用户情况// 从mStoppingActivities remove现在要换新的ActivityRecordmTaskSupervisor.mStoppingActivities.remove(next);mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);ActivityRecord lastResumed = null;final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTaskFragment().asTask()) {lastResumed = lastFocusedRootTask.getTopResumedActivity();}boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);if (mResumedActivity != null) {// 如果之前的ResumeActivity还没有resume,现在触发pause,详见1.13.1pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,next, "resumeTopActivity");}// 如果已经触发pause,进这个分支直接return了,要等pausing完了activity,app回调回来才会进行resumeif (pausing) {if (next.attachedToProcess()) {// 更新进程信息,例如adj,启动组件可能需要更新adjnext.app.updateProcessInfo(false /* updateServiceConnectionActivities */,true /* activityChange */, false /* updateOomAdj */,false /* addPendingTopUid */);} 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);}if (lastResumed != null) {lastResumed.setWillCloseOrEnterPip(true);}return true;} else if (mResumedActivity == next && next.isState(RESUMED)&& taskDisplayArea.allResumedActivitiesComplete()) {// 确保AppTrasnsition。executeAppTransition(options);return true;}// ...// ... 处理动画mTaskSupervisor.mNoAnimActivities.clear();// 如果需要启动ActivityRecord的app已经绑定进程,这里需要做resume操作了// 如果activity没有复用,是新启动的这里一般是false。if (next.attachedToProcess()) {// mLastPausedActivityfinal boolean lastActivityTranslucent = inMultiWindowMode()|| mLastPausedActivity != null && !mLastPausedActivity.occludesParent();// 设置Activity可见性if (!next.isVisibleRequested() || next.mAppStopped || lastActivityTranslucent) {next.app.addToPendingTop();next.setVisibility(true);}// 收集app启动速度next.startLaunchTickingLocked();ActivityRecord lastResumedActivity =lastFocusedRootTask == null ? null: lastFocusedRootTask.getTopResumedActivity();final ActivityRecord.State lastState = next.getState();mAtmService.updateCpuStats();// 设置activity状态resumenext.setState(RESUMED, "resumeTopActivity");boolean notUpdated = true;if (shouldBeVisible(next)) {// 更新activity visible以及配置notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),true /* markFrozenIfConfigChanged */, false /* deferResume */);}if (notUpdated) {ActivityRecord nextNext = topRunningActivity();if (nextNext != next) {mTaskSupervisor.scheduleResumeTopActivities();}if (!next.isVisibleRequested() || next.mAppStopped) {next.setVisibility(true);}next.completeResumeLocked();return true;}try {// 回调app侧,通知app activity需要到resume状态。final ClientTransaction transaction =ClientTransaction.obtain(next.app.getThread(), next.token);// ...transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(next.app.getReportedProcState(),dc.isNextTransitionForward(), next.shouldSendCompatFakeFocus()));mAtmService.getLifecycleManager().scheduleTransaction(transaction);} catch (Exception e) {// Whoops, need to restart this activity!ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "+ "%s", lastState, next);next.setState(lastState, "resumeTopActivityInnerLocked");// lastResumedActivity being non-null implies there is a lastStack present.if (lastResumedActivity != null) {lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");}Slog.i(TAG, "Restarting because process died: " + next);if (!next.hasBeenLaunched) {next.hasBeenLaunched = true;} else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null&& lastFocusedRootTask.isTopRootTaskInDisplayArea()) {next.showStartingWindow(false /* taskSwitch */);}mTaskSupervisor.startSpecificActivity(next, true, false);return true;}try {next.completeResumeLocked();} catch (Exception e) {// If any exception gets thrown, toss away this// activity and try the next one.Slog.w(TAG, "Exception thrown during resume of " + next, e);next.finishIfPossible("resume-exception", true /* oomAdj */);return true;}} else {if (!next.hasBeenLaunched) {next.hasBeenLaunched = true;} else {if (SHOW_APP_STARTING_PREVIEW) {next.showStartingWindow(false /* taskSwich */);}}// 正常新启动Activity而不是复用会走这里,详见1.14mTaskSupervisor.startSpecificActivity(next, true, true);}return true;
}

1.13.1 TaskFragment.startPausing
更新需要pause的ActivityRecord状态到PAUSING,通知app侧进行对应的生命周期操作。
如果pause到Activity对应的进程已经不在了,就直接调用resumeFocusedTasksTopActivities继续唤醒topActivity的流程。
否则等待app pause流程完成后,app会回调system_server,最终也会走到completePause,里面会将ActivityRecord状态改为PAUSED,并且重新调用resumeFocusedTasksTopActivities。(关于通知app的生命周期以及app回调流程,我们会单独有一篇文章来介绍)

boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,String reason) {// ...// ... 合理性检查,如正在暂停的activity不能是我们正在唤醒的activity resumingProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);mPausingActivity = prev;mLastPausedActivity = prev;if (!prev.finishing && prev.isNoHistory()&& !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {mTaskSupervisor.mNoHistoryActivities.add(prev);}// 修改状态为PAUSINGprev.setState(PAUSING, "startPausingLocked");prev.getTask().touchActiveTime();mAtmService.updateCpuStats();boolean pauseImmediately = false;boolean shouldAutoPip = false;if (resuming != null) {// ...画中画相关逻辑 }if (prev.attachedToProcess()) {if (shouldAutoPip) {prev.mPauseSchedulePendingForPip = true;boolean didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs, false /* fromClient */);} else {// 通过getLifecycleManager().scheduleTransaction通知app进行pause到生命周期schedulePauseActivity(prev, userLeaving, pauseImmediately,false /* autoEnteringPip */, reason);}} else {mPausingActivity = null;mLastPausedActivity = null;mTaskSupervisor.mNoHistoryActivities.remove(prev);}// ...// If already entered PIP mode, no need to keep pausing.if (mPausingActivity != null) {// ... if (pauseImmediately) {// 如果需要立刻完成pause,不等待app侧完成pause再进行回调,这里直接执行completePause继续唤醒流程// completePause会将ActivityRecord状态改为PAUSED,并且重新调用resumeFocusedTasksTopActivities,completePause(false, resuming);return false;} else {prev.schedulePauseTimeout();if (!uiSleeping) {mTransitionController.setReady(this, false /* ready */);}return true;}} else {// ... 直接调用resumeFocusedTasksTopActivities继续唤醒流程if (resuming == null) {mRootWindowContainer.resumeFocusedTasksTopActivities();}return false;}
}

1.14 ActivityTaskSupervisor.startSpecificActivity
如果app进程已经启动,则会调用realStartActivityLocked进行Activity的启动,否则会调用startProcessAsync启动对应进程,启动进程的流程我们下一篇文章再介绍。

void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {final WindowProcessController wpc =mService.getProcessController(r.processName, r.info.applicationInfo.uid);boolean knownToBeDead = false;// 如果app进程已经启动,则会调用realStartActivityLocked来启动activity,详见1.15if (wpc != null && wpc.hasThread()) {// ...realStartActivityLocked(r, wpc, andResume, checkConfig);return;// ...}// ...final boolean isTop = andResume && r.isTopRunningActivity();// 否则会触发启动进程的流程。mService.startProcessAsync(r, knownToBeDead, isTop,isTop ? HostingRecord.HOSTING_TYPE_TOP_ACTIVITY: HostingRecord.HOSTING_TYPE_ACTIVITY);
}

1.15 ActivityTaskSupervisor.realStartActivityLocked
主要通过getLifecycleManager通知app启动activity。最后还会调用rootTask.minimalResumeActivityLocked来设置Activity的可见性。

boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,boolean andResume, boolean checkConfig) throws RemoteException {// ...检测所有Activity pause完成final Task task = r.getTask();final Task rootTask = task.getRootTask();try {r.startFreezingScreenLocked(proc, 0);// schedule launch ticks to collect information about slow apps.r.startLaunchTickingLocked();r.lastLaunchTime = SystemClock.uptimeMillis();r.setProcess(proc);if (andResume && !r.canResumeByCompat()) {andResume = false;}r.notifyUnknownVisibilityLaunchedForKeyguardTransition();// ...if (checkConfig) {// 更新Activity visible以及configurationmRootWindowContainer.ensureVisibilityAndConfig(r, r.getDisplayId(),false /* markFrozenIfConfigChanged */, true /* deferResume */);}// ...try {// ...// 通知app侧创建启动activity。  final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.token);final boolean isTransitionForward = r.isTransitionForward();final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();final int deviceId = getDeviceIdForDisplayId(r.getDisplayId());clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),System.identityHashCode(r), r.info, mergedConfiguration.getGlobalConfiguration(),mergedConfiguration.getOverrideConfiguration(), deviceId,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));final ActivityLifecycleItem lifecycleItem;if (andResume) {lifecycleItem = ResumeActivityItem.obtain(isTransitionForward,r.shouldSendCompatFakeFocus());} else {lifecycleItem = PauseActivityItem.obtain();}clientTransaction.setLifecycleStateRequest(lifecycleItem);mService.getLifecycleManager().scheduleTransaction(clientTransaction);// ... }// ...if (andResume && readyToResume()) {// 详见1.16rootTask.minimalResumeActivityLocked(r);}
}

1.16 Task.minimalResumeActivityLocked

void minimalResumeActivityLocked(ActivityRecord r) {ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (starting new instance) "+ "callers=%s", r, Debug.getCallers(5));r.setState(RESUMED, "minimalResumeActivityLocked");// 详见1.17r.completeResumeLocked();
}

1.17 ActivityRecord.completeResumeLocked
这个方法其实只要resume了Activity的路径都会走到。
我们的主要关注更新可见性相关逻辑,其余的逻辑就忽略了。我们知道Activity可见状态是resume的时候,所以在这里会更新可见性。
通过setVisibility设置ActivityRecord可见,然后调用reportResumedActivityLocked。

void completeResumeLocked() {final boolean wasVisible = mVisibleRequested;setVisibility(true);// ...// 详见1.18mTaskSupervisor.reportResumedActivityLocked(this);// ...
}

1.18 ActivityTaskSupervisor.reportResumedActivityLocked

boolean reportResumedActivityLocked(ActivityRecord r) {// resumed的Activity不能在StoppingActivities集合里mStoppingActivities.remove(r);final Task rootTask = r.getRootTask();if (rootTask.getDisplayArea().allResumedActivitiesComplete()) {// 这个方法用来更新Activity的可见性,详见1.19mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);mRootWindowContainer.executeAppTransitionForAllDisplay();return true;}return false;
}

1.19 Task.ensureActivitiesVisible
RootWindowContainer.ensureActivitiesVisible会递归遍历DisplayContent,调用DisplayContent.ensureActivitiesVisible
同样DisplayContent会调用RootTask的ensureActivitiesVisible
我们直接看Task.ensureActivitiesVisible
遍历LeafTask,调用updateActivityVisibilities方法。

void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,boolean preserveWindows, boolean notifyClients) {mTaskSupervisor.beginActivityVisibilityUpdate();try {forAllLeafTasks(task -> {// 详见1.20task.updateActivityVisibilities(starting, configChanges, preserveWindows,notifyClients);}, true /* traverseTopToBottom */);if (mTranslucentActivityWaiting != null &&mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {notifyActivityDrawnLocked(null);}} finally {mTaskSupervisor.endActivityVisibilityUpdate();}
}

1.20 TaskFragment.updateActivityVisibilities
只是调用了mEnsureActivitiesVisibleHelper.process

final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges,boolean preserveWindows, boolean notifyClients) {mTaskSupervisor.beginActivityVisibilityUpdate();try {// 详见1.21mEnsureActivitiesVisibleHelper.process(starting, configChanges, preserveWindows, notifyClients);} finally {mTaskSupervisor.endActivityVisibilityUpdate();}
}

1.21 EnsureActivitiesVisibleHelper.process
递归遍历所有TaskFragment.updateActivityVisibilities,如果TaskFragment有activity,则更新activity可见性状态。

void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows,boolean notifyClients) {reset(starting, configChanges, preserveWindows, notifyClients);if (mTopRunningActivity != null && mTaskFragment.asTask() != null) {// TODO(14709632): Check if this needed to be implemented in TaskFragment.mTaskFragment.asTask().checkTranslucentActivityWaiting(mTopRunningActivity);}// 判断一些不应resumeActivity的情况。  // 我们不应该恢复在后面启动的Activity,因为这些活动实际上在其他全屏的Activity后面,但仍然需要可见(例如执行Recents动画)。final boolean resumeTopActivity = mTopRunningActivity != null&& !mTopRunningActivity.mLaunchTaskBehind&& mTaskFragment.canBeResumed(starting)&& (starting == null || !starting.isDescendantOf(mTaskFragment));ArrayList<TaskFragment> adjacentTaskFragments = null;for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) {final WindowContainer child = mTaskFragment.mChildren.get(i);final TaskFragment childTaskFragment = child.asTaskFragment();if (childTaskFragment != null&& childTaskFragment.getTopNonFinishingActivity() != null) {// 递归遍历调用子TaskFragment.updateActivityVisibilitieschildTaskFragment.updateActivityVisibilities(starting, configChanges,preserveWindows, notifyClients);// 判断当前Task边界是否和父Task边界相同,如果相同,且不透明,则他完全遮盖了下面的兄弟TaskmBehindFullyOccludedContainer |=(childTaskFragment.getBounds().equals(mTaskFragment.getBounds())&& !childTaskFragment.isTranslucent(starting));if (mAboveTop && mTopRunningActivity.getTaskFragment() == childTaskFragment) {mAboveTop = false;}// 如果之前的Task已经全部遮盖了,就直接continueif (mBehindFullyOccludedContainer) {continue;}if (adjacentTaskFragments != null && adjacentTaskFragments.contains(childTaskFragment)) {if (!childTaskFragment.isTranslucent(starting)&& !childTaskFragment.getAdjacentTaskFragment().isTranslucent(starting)) {// Everything behind two adjacent TaskFragments are occluded.mBehindFullyOccludedContainer = true;}continue;}final TaskFragment adjacentTaskFrag = childTaskFragment.getAdjacentTaskFragment();if (adjacentTaskFrag != null) {if (adjacentTaskFragments == null) {adjacentTaskFragments = new ArrayList<>();}adjacentTaskFragments.add(adjacentTaskFrag);}} else if (child.asActivityRecord() != null) {// 设置activity visible state,详见1.22setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);}}
}

1.22 EnsureActivitiesVisibleHelper.setActivityVisibilityState
判断activity是否可见,如果是可见的,则需要设置他的可见状态。
针对不同情况有不同的处理,如果可见的activity还没有attachedToProcess,则调用makeVisibleAndRestartIfNeeded,会设置Activity可见,且如果正在starting的Activity不是当前正在设置可见的Activity,还会重新调用startSpecificActivity来启动Activity。
而其他情况则调用makeActiveIfNeeded或者makeVisibleIfNeeded设置Activity可见,makeVisibleIfNeeded最终也会调用到makeActiveIfNeeded,而makeActiveIfNeeded我们前面已经介绍过了。1.10里也调用这个方法。会重新走上面的流程,而在1.13里如果Activity已经创建过,则会走另一个分支,直接设置Activity可见,通知app侧activity resume的生命周期。

private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,final boolean resumeTopActivity) {final boolean isTop = r == mTopRunningActivity;if (mAboveTop && !isTop) {// 如果不是最上层的activity,设置不可见r.makeInvisible();return;}mAboveTop = false;r.updateVisibilityIgnoringKeyguard(mBehindFullyOccludedContainer);// 计算Activity是否可见final boolean reallyVisible = r.shouldBeVisibleUnchecked();// Check whether activity should be visible without Keyguard influenceif (r.visibleIgnoringKeyguard) {if (r.occludesParent()) {mBehindFullyOccludedContainer = true;} else {mBehindFullyOccludedContainer = false;}} else if (r.isState(INITIALIZING)) {r.cancelInitializing();}if (reallyVisible) {if (r.finishing) {return;}if (r != mStarting && mNotifyClients) {r.ensureActivityConfiguration(0 /* globalChanges */, mPreserveWindows,true /* ignoreVisibility */);}// 如果ActivityRecord还没有attch到进程上if (!r.attachedToProcess()) {// 设置activity visible// 且如果当前需要设置可见的activity不是正在starting的activity,就会调用startSpecificActivity去启动当前设置可见的activitymakeVisibleAndRestartIfNeeded(mStarting, mConfigChanges,resumeTopActivity && isTop, r);} else if (r.isVisibleRequested()) {if (r.mClientVisibilityDeferred && mNotifyClients) {// 这个方法之前提过,在1.10的方法中也调用了该方法。// 如果需要走会resumeTopActivity的流程,相当于重新走了上面的流程,只是如果activity已经构建,在1.13中,会走另一条分支,直接通知app侧resume activity,同时会通知client。而不是走startSpecificActivity流程了。r.makeActiveIfNeeded(r.mClientVisibilityDeferred ? null : starting);r.mClientVisibilityDeferred = false;}r.handleAlreadyVisible();if (mNotifyClients) {r.makeActiveIfNeeded(mStarting);}} else {// mNotifyClients为true,则也会调用到makeActiveIfNeeded,否则设置mClientVisibilityDeferred为true,推迟操作。r.makeVisibleIfNeeded(mStarting, mNotifyClients);}// Aggregate current change flags.mConfigChanges |= r.configChangeFlags;} else {// 否者设置不可见r.makeInvisible();}if (!mBehindFullyOccludedContainer && mTaskFragment.isActivityTypeHome()&& r.isRootOfTask()) {mBehindFullyOccludedContainer = true;}
}

小结

Activity的启动流程非常长,核心思想主要是两个,一个是处理WMS的窗口层次结构,在WMS树上加上新增的ActivityRecord(如需要可能还有Task),或者是找到复用的ActivityRecord;另一个则是需要处理AMS侧activity生命周期以及可见性相关的逻辑。
一个未启动的应用首次创建Activity是会走启动进程流程的,与当前的逻辑略有不同,我们下一篇文章来介绍。
还有app侧对生命周期的控制,我们在下下篇文章中介绍。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

激活Clion2024.2报错We could not validate your license解决

网上激活方法后报错We could not validate your license HLLIQN9GJ8. 只需要设置代理&#xff1a; 主机名&#xff1a;localhost 端口号&#xff1a;80 不为以下设置代理&#xff1a;*.github.com,plugins.jetbrains.com 然后重新激活就不会提示了。

mac安装JetBtains全家桶新版本时报错:Cannot start the IDE

mac安装JetBtains全家桶新版本时报错&#xff1a;Cannot start the IDE 前言报错信息解决方法 前言 作者使用的是Mac电脑&#xff0c;最近想要更新JetBrains相关工具的软件版本&#xff0c;但是在安装时突然报错&#xff0c;导致安装失败&#xff0c;现在将报错信息以及解决方…

FB FC里调用全局变量注意事项

PLC编程基础之数据类型、变量声明、全局变量和I/O映射 PLC编程基础之数据类型、变量声明、全局变量和I/O映射(CODESYS篇 )_codesys全局变量如何映射写入-CSDN博客文章浏览阅读6.3k次,点赞2次,收藏4次。本文介绍了CODESYS编程的基础知识,包括数据类型、变量声明、全局变量、…

口罩检测、未戴口罩识别、未戴口罩检测算法

不戴口罩检测算法主要用于疫情防控、公共安全和企业管理等领域&#xff0c;通过图像识别技术来检测人群中的个体是否佩戴了口罩。这种技术可以帮助管理者实时监控人群的口罩佩戴情况&#xff0c;确保公共卫生安全和防疫措施的落实。以下是关于不戴口罩检测算法的应用场景等详细…

JVM 调优篇8 调优案例5- 逃逸分析

一 逃逸分析 1.1 概念 逃逸分析的基本行为就是分析对象动态作用域&#xff1a;当一个对象在方法中被定义后&#xff0c;对象只在方法内部使用&#xff0c;则认为没有发生逃逸。当一个对象在方法中被定义后&#xff0c;它被外部方法所引用&#xff0c;则认为发生逃逸。例如作为…

聊聊Thread Local Storage

聊聊ThreadLocal 为什么需要Thread Local StorageThread Local Storage的实现PThread库实现操作系统实现GCC __thread关键字实现C11 thread_local实现JAVA ThreadLocal实现 Thread Local Storage 线程局部存储&#xff0c;简称TLS。 为什么需要Thread Local Storage 变量分为全…

ubuntu中Python解释器位置

在Ubuntu系统中&#xff0c;Python解释器通常位于/usr/bin/python或者/usr/bin/python3。 ls /usr/bin/python* 你查python版本可能不是3.10 但是你程序使用如下解释器配置即可运行访问 #!/usr/bin/python3.10 #-*- coding:UTF-8 -*-

前端vue-ref与document.querySelector的对比

ref只在本组件中查找&#xff0c;而document.querySelector是在整个页面查找

一文说清楚ETL与Kafka如何实现集成

ETL与Kafka为何需要集成? 随着企业对实时流数据的处理要求越来越高&#xff0c;很多企业都把实时流数(日志、实时CDC采集数据、设备数据…)先推入到kafka中&#xff0c;再通过ETL对kafka中的数据进行消费通过ETL强大的数据的转换、清洗功能来进行数据的集成与分发。 实时数据…

Qt日志输出及QsLog日志库

目录 Qt日志输出及QsLog日志库日志输出格式化日志普通格式化条件格式化环境变量设置格式化日志输出位置日志输出对象信息禁用输出 QsLog日志库使用方法1. 将QsLog目录添加到项目中2. 配置CMakeLists.txt文件3. 配置.pro文件4. 日志记录器的配置5. 运行程序6. 启用行号和文件名C…

新通话,新突破!菊风荣获第七届“绽放杯”5G消息与新通话专题赛二等奖!

2024年9月9日&#xff0c;由中国信息通信研究院、中国通信企业协会主办的第七届“绽放杯”5G应用征集大赛5G消息及新通话专题赛决赛及颁奖仪式在西安富力希尔顿酒店成功举办。 PART 1 菊风荣获「绽放杯」二等奖 实力见证 荣耀加冕 经过初赛、复赛、决赛的层层选拔&#xff0c…

Prometheus - nVisual插件让运维更轻松

Prometheus 是一个开源的服务监控系统和时间序列数据库&#xff0c;常用于对基础设施的监控&#xff0c;监控范围涵盖了硬件层、操作系统层、中间件层、应用层等运维所需的所有监控指标类型&#xff0c;同时可利用第三方可视化工具Grafana实现时序数据的展示。然而&#xff0c;…

深度学习笔记17_TensorFlow实现咖啡豆识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 一、我的环境 1.语言环境&#xff1a;Python 3.9 2.编译器&#xff1a;Pycharm 3.深度学习环境&#xff1a;TensorFlow 2.10.0 二、GPU设置…

96 kHz、24bit 立体声音频ADC芯片GC5358描述

概述&#xff1a; GC5358 是一款高性能、宽采样率、立体声音频模数转换器。其采样率范围是8KHz~96KHz&#xff0c;非常适合从消费级到专业级的音频应用系统。单端模拟输入不需要外围器件。GC5358 音频有两种数据格式&#xff1a;MSB对齐和 I2S 格式&#xff0c;和各种如 DTV、D…

移动技术开发:简单文本编辑器

1 实验名称 简单文本编辑器 2 实验目的 掌握基本布局管理器的使用方法和基本控件的使用方法&#xff0c;以及事件监听处理的使用方法 3 实验源代码 布局文件代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:an…

Elasticsearch:检索增强生成背后的重要思想

作者&#xff1a;来自 Elastic Jessica L. Moszkowicz 星期天晚上 10 点&#xff0c;我九年级的女儿哭着冲进我的房间。她说她对代数一无所知&#xff0c;注定要失败。我进入超级妈妈模式&#xff0c;却发现我一点高中数学知识都不记得了。于是&#xff0c;我做了任何一位超级妈…

Java servlet《网吧机房管理系统浅析》

网吧机房管理系统在网吧运营中起着至关重要的作用。 对于用户而言&#xff0c;该系统提供了便捷的登录方式&#xff0c;通过用户名和密码可准确显示所在网吧机房号&#xff0c;便于快速定位。同时&#xff0c;合理的机房分配功能确保用户获得良好上网体验。遇到问题时&#xff…

两栏布局和三栏布局的实现方法

两栏布局 右侧不设置宽&#xff0c;实现一栏自适应。 1. float margin-left 左侧设置float&#xff0c;且设置宽度&#xff0c;右侧margin-left为左侧的宽度 <head><style>.left{width: 300px;height: 500px;background-color: palegreen;float: left;}.right…

AI 基础设施:构建AI时代全栈云计算体系

生成式AI 新时代下催生新的基础设施需求 随着企业在数字化转型之路上越走越远&#xff0c;期间一场新的技术革命正在发生&#xff0c;近几年涌现的生成式AI技术正在迅速改变科技、商业和整个社会的格局。这种强大的技术能够从数据中学习并生成预测性输出&#xff0c;生成式 AI …

使用chatgpt降低论文重复率的方法和需要注意的一些细节

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 要降低论文的重复率&#xff0c;可以借助ChatGPT进行多种方式的优化。以下是几种策略&#xff1a; 1. 重写段落或句子&#xff1a; 输入你认为可能重复率较高的段落或句子&#xff0c;…