FallbackHome的啟動流程(android11)

首次開機開機動畫播完進入Launcher桌面時黑屏進入Launcher,有黑屏不太美觀,在重啟以后會在進入桌面后會顯示android正在啟動等一會進入Launcher,這就是系統FallBackHome機制

接下來我們跟著代碼看下首次啟動系統如何進入FallbackHome的

在SystemServer的startOtherServices這個方法里他會調mActivityManagerService.systemReady這個方法,主要是執行了這里

第一次執行判斷為true,這里會執行,之后調用ActivityTaskManagerInternal.startHomeOnAllDisplays,這是個抽象類,我們找到他的實現類,在ActivityTaskManagerService中有個

我們看下我們所需方法的實現

        @Overridepublic boolean startHomeOnAllDisplays(int userId, String reason) {synchronized (mGlobalLock) {return mRootWindowContainer.startHomeOnAllDisplays(userId, reason);}}

之后調用到RootWindowContainer.startHomeOnAllDisplays

接著調用startHomeOnDisplay和他的重載

接著調用了startHomeOnTaskDisplayArea

    boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,boolean allowInstrumenting, boolean fromHomeKey) {// Fallback to top focused display area if the provided one is invalid.if (taskDisplayArea == null) {final ActivityStack stack = getTopDisplayFocusedStack();taskDisplayArea = stack != null ? stack.getDisplayArea(): getDefaultTaskDisplayArea();}Intent homeIntent = null;ActivityInfo aInfo = null;if (taskDisplayArea == getDefaultTaskDisplayArea()) {//第一次會進入這里,我們下面看具體實現homeIntent = mService.getHomeIntent();aInfo = resolveHomeActivity(userId, homeIntent);} else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea);aInfo = info.first;homeIntent = info.second;}if (aInfo == null || homeIntent == null) {return false;}if (!canStartHomeOnDisplayArea(aInfo, taskDisplayArea, allowInstrumenting)) {return false;}// Updates the home component of the intent.homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);// Updates the extra information of the intent.if (fromHomeKey) {homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "startHomeActivity");}// Update the reason for ANR debugging to verify if the user activity is the one that// actually launched.final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(aInfo.applicationInfo.uid) + ":" + taskDisplayArea.getDisplayId();
最后調用這里來啟動activitymService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,taskDisplayArea);return true;}

之后調用到

    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,TaskDisplayArea taskDisplayArea) {final ActivityOptions options = ActivityOptions.makeBasic();options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);if (!ActivityRecord.isResolverActivity(aInfo.name)) {// The resolver activity shouldn't be put in home stack because when the foreground is// standard type activity, the resolver activity should be put on the top of current// foreground instead of bring home stack to front.options.setLaunchActivityType(ACTIVITY_TYPE_HOME);}final int displayId = taskDisplayArea.getDisplayId();options.setLaunchDisplayId(displayId);options.setLaunchTaskDisplayArea(taskDisplayArea.mRemoteToken.toWindowContainerToken());// The home activity will be started later, defer resuming to avoid unneccerary operations// (e.g. start home recursively) when creating home stack.mSupervisor.beginDeferResume();final ActivityStack homeStack;try {// Make sure home stack exists on display area.homeStack = taskDisplayArea.getOrCreateRootHomeTask(ON_TOP);} finally {mSupervisor.endDeferResume();}mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason).setOutActivity(tmpOutRecord).setCallingUid(0).setActivityInfo(aInfo).setActivityOptions(options.toBundle()).execute();mLastHomeActivityStartRecord = tmpOutRecord[0];if (homeStack.mInResumeTopActivity) {// If we are in resume section already, home activity will be initialized, but not// resumed (to avoid recursive resume) and will stay that way until something pokes it// again. We need to schedule another resume.mSupervisor.scheduleResumeTopActivities();}}

這個是關鍵,我們看下obtainStarter這個方法

    int execute() {try {// Refuse possible leaked file descriptorsif (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) {throw new IllegalArgumentException("File descriptors passed in Intent");}final LaunchingState launchingState;synchronized (mService.mGlobalLock) {final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo);launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mRequest.intent, caller);}// If the caller hasn't already resolved the activity, we're willing// to do so here. If the caller is already holding the WM lock here,// and we need to check dynamic Uri permissions, then we're forced// to assume those permissions are denied to avoid deadlocking.if (mRequest.activityInfo == null) {mRequest.resolveActivity(mSupervisor);}int res;synchronized (mService.mGlobalLock) {final boolean globalConfigWillChange = mRequest.globalConfig != null&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();if (stack != null) {stack.mConfigWillChange = globalConfigWillChange;}if (DEBUG_CONFIGURATION) {Slog.v(TAG_CONFIGURATION, "Starting activity when config will change = "+ globalConfigWillChange);}final long origId = Binder.clearCallingIdentity();res = resolveToHeavyWeightSwitcherIfNeeded();if (res != START_SUCCESS) {return res;}res = executeRequest(mRequest);Binder.restoreCallingIdentity(origId);if (globalConfigWillChange) {// If the caller also wants to switch to a new configuration, do so now.// This allows a clean switch, as we are waiting for the current activity// to pause (so we will not destroy it), and have not yet started the// next activity.mService.mAmInternal.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,"updateConfiguration()");if (stack != null) {stack.mConfigWillChange = false;}if (DEBUG_CONFIGURATION) {Slog.v(TAG_CONFIGURATION,"Updating to new configuration after starting activity.");}mService.updateConfigurationLocked(mRequest.globalConfig, null, false);}// Notify ActivityMetricsLogger that the activity has launched.// ActivityMetricsLogger will then wait for the windows to be drawn and populate// WaitResult.mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,mLastStartActivityRecord);return getExternalResult(mRequest.waitResult == null ? res: waitForResult(res, mLastStartActivityRecord));}} finally {onExecutionComplete();}}

主要方法是

之后調用了

private int executeRequest(Request request) {if (TextUtils.isEmpty(request.reason)) {throw new IllegalArgumentException("Need to specify a reason.");}mLastStartReason = request.reason;mLastStartActivityTimeMs = System.currentTimeMillis();mLastStartActivityRecord = null;final IApplicationThread caller = request.caller;Intent intent = request.intent;NeededUriGrants intentGrants = request.intentGrants;String resolvedType = request.resolvedType;ActivityInfo aInfo = request.activityInfo;ResolveInfo rInfo = request.resolveInfo;final IVoiceInteractionSession voiceSession = request.voiceSession;final IBinder resultTo = request.resultTo;String resultWho = request.resultWho;int requestCode = request.requestCode;int callingPid = request.callingPid;int callingUid = request.callingUid;String callingPackage = request.callingPackage;String callingFeatureId = request.callingFeatureId;final int realCallingPid = request.realCallingPid;final int realCallingUid = request.realCallingUid;final int startFlags = request.startFlags;final SafeActivityOptions options = request.activityOptions;Task inTask = request.inTask;int err = ActivityManager.START_SUCCESS;// Pull the optional Ephemeral Installer-only bundle out of the options early.final Bundle verificationBundle =options != null ? options.popAppVerificationBundle() : null;WindowProcessController callerApp = null;if (caller != null) {callerApp = mService.getProcessController(caller);if (callerApp != null) {callingPid = callerApp.getPid();callingUid = callerApp.mInfo.uid;} else {Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid+ ") when starting: " + intent.toString());err = ActivityManager.START_PERMISSION_DENIED;}}final int userId = aInfo != null && aInfo.applicationInfo != null? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;if (err == ActivityManager.START_SUCCESS) {Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)+ "} from uid " + callingUid);}ActivityRecord sourceRecord = null;ActivityRecord resultRecord = null;if (resultTo != null) {sourceRecord = mRootWindowContainer.isInAnyStack(resultTo);if (DEBUG_RESULTS) {Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord);}if (sourceRecord != null) {if (requestCode >= 0 && !sourceRecord.finishing) {resultRecord = sourceRecord;}}}final int launchFlags = intent.getFlags();if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {// Transfer the result target from the source activity to the new one being started,// including any failures.if (requestCode >= 0) {SafeActivityOptions.abort(options);return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;}resultRecord = sourceRecord.resultTo;if (resultRecord != null && !resultRecord.isInStackLocked()) {resultRecord = null;}resultWho = sourceRecord.resultWho;requestCode = sourceRecord.requestCode;sourceRecord.resultTo = null;if (resultRecord != null) {resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);}if (sourceRecord.launchedFromUid == callingUid) {// The new activity is being launched from the same uid as the previous activity// in the flow, and asking to forward its result back to the previous.  In this// case the activity is serving as a trampoline between the two, so we also want// to update its launchedFromPackage to be the same as the previous activity.// Note that this is safe, since we know these two packages come from the same// uid; the caller could just as well have supplied that same package name itself// . This specifially deals with the case of an intent picker/chooser being// launched in the app flow to redirect to an activity picked by the user, where// we want the final activity to consider it to have been launched by the// previous app activity.callingPackage = sourceRecord.launchedFromPackage;callingFeatureId = sourceRecord.launchedFromFeatureId;}}if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {// We couldn't find a class that can handle the given Intent.// That's the end of that!err = ActivityManager.START_INTENT_NOT_RESOLVED;}if (err == ActivityManager.START_SUCCESS && aInfo == null) {// We couldn't find the specific class specified in the Intent.// Also the end of the line.err = ActivityManager.START_CLASS_NOT_FOUND;}if (err == ActivityManager.START_SUCCESS && sourceRecord != null&& sourceRecord.getTask().voiceSession != null) {// If this activity is being launched as part of a voice session, we need to ensure// that it is safe to do so.  If the upcoming activity will also be part of the voice// session, we can only launch it if it has explicitly said it supports the VOICE// category, or it is a part of the calling app.if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0&& sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {try {intent.addCategory(Intent.CATEGORY_VOICE);if (!mService.getPackageManager().activitySupportsIntent(intent.getComponent(), intent, resolvedType)) {Slog.w(TAG, "Activity being started in current voice task does not support "+ "voice: " + intent);err = ActivityManager.START_NOT_VOICE_COMPATIBLE;}} catch (RemoteException e) {Slog.w(TAG, "Failure checking voice capabilities", e);err = ActivityManager.START_NOT_VOICE_COMPATIBLE;}}}if (err == ActivityManager.START_SUCCESS && voiceSession != null) {// If the caller is starting a new voice session, just make sure the target// is actually allowing it to run this way.try {if (!mService.getPackageManager().activitySupportsIntent(intent.getComponent(),intent, resolvedType)) {Slog.w(TAG,"Activity being started in new voice task does not support: " + intent);err = ActivityManager.START_NOT_VOICE_COMPATIBLE;}} catch (RemoteException e) {Slog.w(TAG, "Failure checking voice capabilities", e);err = ActivityManager.START_NOT_VOICE_COMPATIBLE;}}final ActivityStack resultStack = resultRecord == null? null : resultRecord.getRootTask();if (err != START_SUCCESS) {if (resultRecord != null) {resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,null /* data */, null /* dataGrants */);}SafeActivityOptions.abort(options);return err;}boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,requestCode, callingPid, callingUid, callingPackage, callingFeatureId,request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord, resultStack);abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,callingPid, resolvedType, aInfo.applicationInfo);abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,callingPackage);boolean restrictedBgActivity = false;if (!abort) {try {Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,"shouldAbortBackgroundActivityStart");restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,request.originatingPendingIntent, request.allowBackgroundActivityStart,intent);} finally {Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);}}// Merge the two options bundles, while realCallerOptions takes precedence.ActivityOptions checkedOptions = options != null? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;if (request.allowPendingRemoteAnimationRegistryLookup) {checkedOptions = mService.getActivityStartController().getPendingRemoteAnimationRegistry().overrideOptionsIfNeeded(callingPackage, checkedOptions);}if (mService.mController != null) {try {// The Intent we give to the watcher has the extra data stripped off, since it// can contain private information.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);if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,callingUid, checkedOptions)) {// activity start was intercepted, e.g. because the target user is currently in quiet// mode (turn off work) or the target application is suspendedintent = mInterceptor.mIntent;rInfo = mInterceptor.mRInfo;aInfo = mInterceptor.mAInfo;resolvedType = mInterceptor.mResolvedType;inTask = mInterceptor.mInTask;callingPid = mInterceptor.mCallingPid;callingUid = mInterceptor.mCallingUid;checkedOptions = mInterceptor.mActivityOptions;// The interception target shouldn't get any permission grants// intended for the original destinationintentGrants = null;}if (abort) {if (resultRecord != null) {resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,null /* data */, null /* dataGrants */);}// We pretend to the caller that it was really started, but they will just get a// cancel result.ActivityOptions.abort(checkedOptions);return START_ABORTED;}// If permissions need a review before any of the app components can run, we// launch the review activity and pass a pending intent to start the activity// we are to launching now after the review is completed.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);int flags = intent.getFlags();flags |= Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;/** Prevent reuse of review activity: Each app needs their own review activity. By* default activities launched with NEW_TASK or NEW_DOCUMENT try to reuse activities* with the same launch parameters (extras are ignored). Hence to avoid possible* reuse force a new activity via the MULTIPLE_TASK flag.** Activities that are not launched with NEW_TASK or NEW_DOCUMENT are not re-used,* hence no need to add the flag in this case.*/if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0) {flags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK;}newIntent.setFlags(flags);newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));if (resultRecord != null) {newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);}intent = newIntent;// The permissions review target shouldn't get any permission// grants intended for the original destinationintentGrants = null;resolvedType = null;callingUid = realCallingUid;callingPid = realCallingPid;rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0,computeResolveFilterUid(callingUid, realCallingUid, request.filterCallingUid));aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,null /*profilerInfo*/);if (DEBUG_PERMISSIONS_REVIEW) {final ActivityStack focusedStack =mRootWindowContainer.getTopDisplayFocusedStack();Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,true, false) + "} from uid " + callingUid + " on display "+ (focusedStack == null ? DEFAULT_DISPLAY: focusedStack.getDisplayId()));}}}// If we have an ephemeral app, abort the process of launching the resolved intent.// Instead, launch the ephemeral installer. Once the installer is finished, it// starts either the intent we resolved here [on install error] or the ephemeral// app [on install success].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*/);}final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,callingPackage, callingFeatureId, intent, resolvedType, aInfo,mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode,request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions,sourceRecord);mLastStartActivityRecord = r;if (r.appTimeTracker == null && sourceRecord != null) {// If the caller didn't specify an explicit time tracker, we want to continue// tracking under any it has.r.appTimeTracker = sourceRecord.appTimeTracker;}final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();// If we are starting an activity that is not from the same uid as the currently resumed// one, check whether app switches are allowed.if (voiceSession == null && stack != null && (stack.getResumedActivity() == null|| stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,realCallingPid, realCallingUid, "Activity start")) {if (!(restrictedBgActivity && handleBackgroundActivityAbort(r))) {mController.addPendingActivityLaunch(new PendingActivityLaunch(r,sourceRecord, startFlags, stack, callerApp, intentGrants));}ActivityOptions.abort(checkedOptions);return ActivityManager.START_SWITCHES_CANCELED;}}mService.onStartActivitySetDidAppSwitch();mController.doPendingActivityLaunches(false);mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,restrictedBgActivity, intentGrants);if (request.outActivity != null) {request.outActivity[0] = mLastStartActivityRecord;}return mLastStartActivityResult;}

之后調用了startActivityUnchecked

之后調用到startActivityInner里面的

調用resumeFocusedStacksTopActivities,之后調用

    boolean resumeFocusedStacksTopActivities() {return resumeFocusedStacksTopActivities(null, null, null);}boolean resumeFocusedStacksTopActivities(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {if (!mStackSupervisor.readyToResume()) {return false;}boolean result = false;if (targetStack != null && (targetStack.isTopStackInDisplayArea()|| getTopDisplayFocusedStack() == targetStack)) {result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);}for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {final DisplayContent display = getChildAt(displayNdx);boolean resumedOnDisplay = false;for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);final ActivityRecord topRunningActivity = stack.topRunningActivity();if (!stack.isFocusableAndVisible() || topRunningActivity == null) {continue;}if (stack == targetStack) {// Simply update the result for targetStack because the targetStack had// already resumed in above. We don't want to resume it again, especially in// some cases, it would cause a second launch failure if app process was// dead.resumedOnDisplay |= result;continue;}if (taskDisplayArea.isTopStack(stack) && topRunningActivity.isState(RESUMED)) {// Kick off any lingering app transitions form the MoveTaskToFront// operation, but only consider the top task and stack on that display.stack.executeAppTransition(targetOptions);} else {resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);}}}if (!resumedOnDisplay) {// In cases when there are no valid activities (e.g. device just booted or launcher// crashed) it's possible that nothing was resumed on a display. Requesting resume// of top activity in focused stack explicitly will make sure that at least home// activity is started and resumed, and no recursion occurs.final ActivityStack focusedStack = display.getFocusedStack();if (focusedStack != null) {result |= focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);} else if (targetStack == null) {result |= resumeHomeActivity(null /* prev */, "no-focusable-task",display.getDefaultTaskDisplayArea());}}}return result;}

之后調用了resumeTopActivityUncheckedLocked方法

這里調用了resumeTopActivityInnerLocked這個方法

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {// 檢查系統是否已經完成啟動,如果未完成,則不允許恢復 Activityif (!mAtmService.isBooting() && !mAtmService.isBooted()) {// 系統未準備好,直接返回 falsereturn false;}// 查找棧頂可聚焦且未結束的 ActivityActivityRecord next = topRunningActivity(true /* focusableOnly */);// 檢查是否存在正在運行的 Activityfinal boolean hasRunningActivity = next != null;// TODO: 可能需要移除這個條件if (hasRunningActivity && !isAttached()) {return false;}// 取消所有正在初始化的 ActivitymRootWindowContainer.cancelInitializingActivities();// 記錄當前的用戶離開狀態,并重置標志位boolean userLeaving = mStackSupervisor.mUserLeaving;mStackSupervisor.mUserLeaving = false;// 如果棧中沒有 Activity,則嘗試恢復下一個可聚焦的 Activityif (!hasRunningActivity) {return resumeNextFocusableActivityWhenStackIsEmpty(prev, options);}// 重置延遲恢復標志next.delayedResume = false;// 獲取當前任務顯示區域final TaskDisplayArea taskDisplayArea = getDisplayArea();// 如果棧頂 Activity 已經是 resumed 狀態,則無需處理if (mResumedActivity == next && next.isState(RESUMED)&& taskDisplayArea.allResumedActivitiesComplete()) {// 執行未完成的過渡動畫executeAppTransition(options);if (DEBUG_STATES) Slog.d(TAG_STATES,"resumeTopActivityLocked: Top activity resumed " + next);return false;}// 檢查 Activity 是否可以通過兼容性檢查恢復if (!next.canResumeByCompat()) {return false;}// 檢查是否有 Activity 正在暫停,如果有則等待暫停完成final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();if (!allPausedComplete) {if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) {Slog.v(TAG_PAUSE, "resumeTopActivityLocked: Skip resume: some activity pausing.");}return false;}// 如果系統處于睡眠狀態,并且棧頂 Activity 已暫停,則無需恢復if (shouldSleepOrShutDownActivities()&& mLastPausedActivity == next&& mRootWindowContainer.allPausedActivitiesComplete()) {boolean nothingToResume = true;if (!mAtmService.mShuttingDown) {// 檢查 Activity 是否可以顯示在鎖屏上或是否可以解鎖屏幕final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard&& next.canShowWhenLocked();final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next&& next.containsDismissKeyguardWindow();if (canShowWhenLocked || mayDismissKeyguard) {ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,!PRESERVE_WINDOWS);nothingToResume = shouldSleepActivities();} else if (next.currentLaunchCanTurnScreenOn() && next.canTurnScreenOn()) {nothingToResume = false;}}if (nothingToResume) {// 執行未完成的過渡動畫executeAppTransition(options);if (DEBUG_STATES) Slog.d(TAG_STATES,"resumeTopActivityLocked: Going to sleep and all paused");return false;}}// 檢查 Activity 所屬用戶是否已啟動,如果未啟動則不允許恢復if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {Slog.w(TAG, "Skipping resume of top activity " + next+ ": user " + next.mUserId + " is stopped");return false;}// 從停止列表中移除該 Activity,并重置睡眠狀態mStackSupervisor.mStoppingActivities.remove(next);next.setSleeping(false);if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);// 再次檢查是否有 Activity 正在暫停,如果有則等待暫停完成if (!mRootWindowContainer.allPausedActivitiesComplete()) {if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,"resumeTopActivityLocked: Skip resume: some activity pausing.");return false;}// 設置啟動來源mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);// 獲取上一個 resumed 的 ActivityActivityRecord lastResumed = null;final ActivityStack lastFocusedStack = taskDisplayArea.getLastFocusedStack();if (lastFocusedStack != null && lastFocusedStack != this) {lastResumed = lastFocusedStack.mResumedActivity;if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {// 如果處于多窗口模式且上一個棧應該可見,則重置用戶離開標志userLeaving = false;}}// 暫停其他棧的 Activityboolean pausing = taskDisplayArea.pauseBackStacks(userLeaving, next);if (mResumedActivity != null) {// 如果當前有 resumed 的 Activity,則暫停它pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next);}if (pausing) {if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,"resumeTopActivityLocked: Skip resume: need to start pausing");// 如果 Activity 已附加到進程,則更新進程信息if (next.attachedToProcess()) {next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,true /* activityChange */, false /* updateOomAdj */,false /* addPendingTopUid */);} else if (!next.isProcessRunning()) {// 如果進程未運行,則異步啟動進程final boolean isTop = this == taskDisplayArea.getFocusedStack();mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,isTop ? "pre-top-activity" : "pre-activity");}if (lastResumed != null) {lastResumed.setWillCloseOrEnterPip(true);}return true;} else if (mResumedActivity == next && next.isState(RESUMED)&& taskDisplayArea.allResumedActivitiesComplete()) {// 如果 Activity 已經是 resumed 狀態,則執行未完成的過渡動畫executeAppTransition(options);if (DEBUG_STATES) Slog.d(TAG_STATES,"resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);return true;}// 如果上一個 Activity 是 noHistory 且未結束,則結束它if (shouldSleepActivities() && mLastNoHistoryActivity != null&& !mLastNoHistoryActivity.finishing&& mLastNoHistoryActivity != next) {mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);mLastNoHistoryActivity = null;}// 如果上一個 Activity 正在結束且下一個 Activity 已可見,則立即隱藏上一個 Activityif (prev != null && prev != next && next.nowVisible) {if (prev.finishing) {prev.setVisibility(false);}}// 確保應用不再處于停止狀態try {mAtmService.getPackageManager().setPackageStoppedState(next.packageName, false, next.mUserId);} catch (RemoteException e1) {} catch (IllegalArgumentException e) {Slog.w(TAG, "Failed trying to unstop package "+ next.packageName + ": " + e);}// 準備過渡動畫boolean anim = true;final DisplayContent dc = taskDisplayArea.mDisplayContent;if (prev != null) {if (prev.finishing) {if (mStackSupervisor.mNoAnimActivities.contains(prev)) {anim = false;dc.prepareAppTransition(TRANSIT_NONE, false);} else {dc.prepareAppTransition(prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE: TRANSIT_TASK_CLOSE, false);}prev.setVisibility(false);} else {if (mStackSupervisor.mNoAnimActivities.contains(next)) {anim = false;dc.prepareAppTransition(TRANSIT_NONE, false);} else {dc.prepareAppTransition(prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN: next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND: TRANSIT_TASK_OPEN, false);}}} else {if (mStackSupervisor.mNoAnimActivities.contains(next)) {anim = false;dc.prepareAppTransition(TRANSIT_NONE, false);} else {dc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);}}// 應用或清除 Activity 的選項if (anim) {next.applyOptionsLocked();} else {next.clearOptionsLocked();}// 清除無動畫 Activity 列表mStackSupervisor.mNoAnimActivities.clear();// 如果 Activity 已附加到進程,則恢復它if (next.attachedToProcess()) {// 設置 Activity 為可見狀態if (!next.mVisibleRequested || next.stopped) {next.setVisibility(true);}// 啟動延遲統計next.startLaunchTickingLocked();// 更新 CPU 統計信息mAtmService.updateCpuStats();// 將 Activity 狀態設置為 RESUMEDnext.setState(RESUMED, "resumeTopActivityInnerLocked");// 更新進程信息next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,true /* activityChange */, true /* updateOomAdj */,true /* addPendingTopUid */);// 確保 Activity 可見并更新配置boolean notUpdated = true;if (shouldBeVisible(next)) {notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),true /* markFrozenIfConfigChanged */, false /* deferResume */);}// 如果配置更新失敗,則重新調度恢復if (notUpdated) {ActivityRecord nextNext = topRunningActivity();if (nextNext != next) {mStackSupervisor.scheduleResumeTopActivities();}if (!next.mVisibleRequested || next.stopped) {next.setVisibility(true);}next.completeResumeLocked();return true;}// 創建并執行恢復事務try {final ClientTransaction transaction =ClientTransaction.obtain(next.app.getThread(), next.appToken);// 傳遞所有未完成的結果if (next.results != null) {transaction.addCallback(ActivityResultItem.obtain(next.results));}// 傳遞新的 Intentif (next.newIntents != null) {transaction.addCallback(NewIntentItem.obtain(next.newIntents, true /* resume */));}// 通知應用已恢復next.notifyAppResumed(next.stopped);// 記錄日志EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),next.getTask().mTaskId, next.shortComponentName);// 設置生命周期狀態transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(next.app.getReportedProcState(),dc.isNextTransitionForward()));mAtmService.getLifecycleManager().scheduleTransaction(transaction);if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " + next);} catch (Exception e) {// 如果恢復失敗,則重置狀態并重啟 Activitynext.setState(lastState, "resumeTopActivityInnerLocked");if (lastResumed != null) {lastResumed.setState(RESUMED, "resumeTopActivityInnerLocked");}Slog.i(TAG, "Restarting because process died: " + next);if (!next.hasBeenLaunched) {next.hasBeenLaunched = true;} else if (SHOW_APP_STARTING_PREVIEW && lastFocusedStack != null&& lastFocusedStack.isTopStackInDisplayArea()) {next.showStartingWindow(null /* prev */, false /* newTask */,false /* taskSwitch */);}mStackSupervisor.startSpecificActivity(next, true, false);return true;}// 完成恢復try {next.completeResumeLocked();} catch (Exception e) {Slog.w(TAG, "Exception thrown during resume of " + next, e);next.finishIfPossible("resume-exception", true /* oomAdj */);return true;}} else {// 如果 Activity 未附加到進程,則啟動它if (!next.hasBeenLaunched) {next.hasBeenLaunched = true;} else {if (SHOW_APP_STARTING_PREVIEW) {next.showStartingWindow(null /* prev */, false /* newTask */,false /* taskSwitch */);}if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);}if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);mStackSupervisor.startSpecificActivity(next, true, true);}return true;
}

最后調用了?mStackSupervisor.startSpecificActivity(next, true, true);

之后調用了

?mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");

之后調用

在這里啟動了ActivityManagerInternal::startProcess,這和類是抽象類

接著調用到

    boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,boolean mountExtStorageFull, String abiOverride) {if (app.pendingStart) {return true;}long startTime = SystemClock.uptimeMillis();if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {checkSlow(startTime, "startProcess: removing from pids map");mService.removePidLocked(app);app.bindMountPending = false;mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);checkSlow(startTime, "startProcess: done removing from pids map");app.setPid(0);app.startSeq = 0;}if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,"startProcessLocked removing on hold: " + app);mService.mProcessesOnHold.remove(app);checkSlow(startTime, "startProcess: starting to update cpu stats");mService.updateCpuStats();checkSlow(startTime, "startProcess: done updating cpu stats");try {try {final int userId = UserHandle.getUserId(app.uid);AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);} catch (RemoteException e) {throw e.rethrowAsRuntimeException();}int uid = app.uid;int[] gids = null;int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;if (!app.isolated) {int[] permGids = null;try {checkSlow(startTime, "startProcess: getting gids from package manager");final IPackageManager pm = AppGlobals.getPackageManager();permGids = pm.getPackageGids(app.info.packageName,MATCH_DIRECT_BOOT_AUTO, app.userId);if (StorageManager.hasIsolatedStorage() && mountExtStorageFull) {mountExternal = Zygote.MOUNT_EXTERNAL_FULL;} else {StorageManagerInternal storageManagerInternal = LocalServices.getService(StorageManagerInternal.class);mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,app.info.packageName);}} catch (RemoteException e) {throw e.rethrowAsRuntimeException();}// Remove any gids needed if the process has been denied permissions.// NOTE: eventually we should probably have the package manager pre-compute// this for us?if (app.processInfo != null && app.processInfo.deniedPermissions != null) {for (int i = app.processInfo.deniedPermissions.size() - 1; i >= 0; i--) {int[] denyGids = mService.mPackageManagerInt.getPermissionGids(app.processInfo.deniedPermissions.valueAt(i), app.userId);if (denyGids != null) {for (int gid : denyGids) {permGids = ArrayUtils.removeInt(permGids, gid);}}}}gids = computeGidsForProcess(mountExternal, uid, permGids);}app.mountMode = mountExternal;checkSlow(startTime, "startProcess: building args");if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {uid = 0;}int runtimeFlags = 0;if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;// Also turn on CheckJNI for debuggable apps. It's quite// awkward to turn on otherwise.runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;// Check if the developer does not want ART verificationif (android.provider.Settings.Global.getInt(mService.mContext.getContentResolver(),android.provider.Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE, 1) == 0) {runtimeFlags |= Zygote.DISABLE_VERIFIER;Slog.w(TAG_PROCESSES, app + ": ART verification disabled");}}// Run the app in safe mode if its manifest requests so or the// system is booted in safe mode.if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 || mService.mSafeMode) {runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;}if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0) {runtimeFlags |= Zygote.PROFILE_FROM_SHELL;}if ("1".equals(SystemProperties.get("debug.checkjni"))) {runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;}String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;}String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;}if ("1".equals(SystemProperties.get("debug.jni.logging"))) {runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;}if ("1".equals(SystemProperties.get("debug.assert"))) {runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;}if ("1".equals(SystemProperties.get("debug.ignoreappsignalhandler"))) {runtimeFlags |= Zygote.DEBUG_IGNORE_APP_SIGNAL_HANDLER;}if (mService.mNativeDebuggingApp != null&& mService.mNativeDebuggingApp.equals(app.processName)) {// Enable all debug flags required by the native debugger.runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT;          // Don't interpret anythingruntimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug inforuntimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;   // Disbale optimizationsmService.mNativeDebuggingApp = null;}if (app.info.isEmbeddedDexUsed()|| (app.info.isPrivilegedApp()&& DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet()))) {runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;}if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {app.info.maybeUpdateHiddenApiEnforcementPolicy(mService.mHiddenApiBlacklist.getPolicy());@ApplicationInfo.HiddenApiEnforcementPolicy int policy =app.info.getHiddenApiEnforcementPolicy();int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {throw new IllegalStateException("Invalid API policy: " + policy);}runtimeFlags |= policyBits;if (disableTestApiChecks) {runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;}}String useAppImageCache = SystemProperties.get(PROPERTY_USE_APP_IMAGE_STARTUP_CACHE, "");// Property defaults to true currently.if (!TextUtils.isEmpty(useAppImageCache) && !useAppImageCache.equals("false")) {runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;}runtimeFlags |= decideGwpAsanLevel(app);String invokeWith = null;if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {// Debuggable apps may include a wrapper script with their library directory.String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();try {if (new File(wrapperFileName).exists()) {invokeWith = "/system/bin/logwrapper " + wrapperFileName;}} finally {StrictMode.setThreadPolicy(oldPolicy);}}String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;if (requiredAbi == null) {requiredAbi = Build.SUPPORTED_ABIS[0];}String instructionSet = null;if (app.info.primaryCpuAbi != null) {instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);}app.gids = gids;app.setRequiredAbi(requiredAbi);app.instructionSet = instructionSet;// If instructionSet is non-null, this indicates that the system_server is spawning a// process with an ISA that may be different from its own. System (kernel and hardware)// compatililty for these features is checked in the decideTaggingLevel in the// system_server process (not the child process). As TBI is only supported in aarch64,// we can simply ensure that the new process is also aarch64. This prevents the mismatch// where a 64-bit system server spawns a 32-bit child that thinks it should enable some// tagging variant. Theoretically, a 32-bit system server could exist that spawns 64-bit// processes, in which case the new process won't get any tagging. This is fine as we// haven't seen this configuration in practice, and we can reasonable assume that if// tagging is desired, the system server will be 64-bit.if (instructionSet == null || instructionSet.equals("arm64")) {runtimeFlags |= decideTaggingLevel(app);}// the per-user SELinux context must be setif (TextUtils.isEmpty(app.info.seInfoUser)) {Slog.wtf(ActivityManagerService.TAG, "SELinux tag not defined",new IllegalStateException("SELinux tag not defined for "+ app.info.packageName + " (uid " + app.uid + ")"));}final String seInfo = app.info.seInfo+ (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);// Start the process.  It will either succeed and return a result containing// the PID of the new process, or else throw a RuntimeException.final String entryPoint = "android.app.ActivityThread";return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,instructionSet, invokeWith, startTime);} catch (RuntimeException e) {Slog.e(ActivityManagerService.TAG, "Failure starting process " + app.processName, e);// Something went very wrong while trying to start this process; one// common case is when the package is frozen due to an active// upgrade. To recover, clean up any active bookkeeping related to// starting this process. (We already invoked this method once when// the package was initially frozen through KILL_APPLICATION_MSG, so// it doesn't hurt to use it again.)mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),false, false, true, false, false, app.userId, "start failure");return false;}}

之后通過

啟動了進程,

之后調用了

最后調用到

    private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {try {final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;zygoteWriter.write(msgStr);zygoteWriter.flush();// Always read the entire result from the input stream to avoid leaving// bytes in the stream for future process starts to accidentally stumble// upon.Process.ProcessStartResult result = new Process.ProcessStartResult();result.pid = zygoteInputStream.readInt();result.usingWrapper = zygoteInputStream.readBoolean();if (result.pid < 0) {throw new ZygoteStartFailedEx("fork() failed");}return result;} catch (IOException ex) {zygoteState.close();Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "+ ex.toString());throw new ZygoteStartFailedEx(ex);}}

zygoteState是在

之后調用了

其中?public static final String PRIMARY_SOCKET_NAME = "zygote";這是她通信的Sccket名稱

我們找下服務端在哪里

這里在ZygoteInit.java這個類

主要執行了

之后調用了

接著調用

之后就fork出進程

在fork成功后會執行

fork成功會pid==0

執行了?return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);

之后執行

這里對app進行了初始化

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/81368.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/81368.shtml
英文地址,請注明出處:http://en.pswp.cn/web/81368.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【EdgeYOLO】《EdgeYOLO: An Edge-Real-Time Object Detector》

Liu S, Zha J, Sun J, et al. EdgeYOLO: An edge-real-time object detector[C]//2023 42nd Chinese Control Conference (CCC). IEEE, 2023: 7507-7512. CCC-2023 源碼&#xff1a;https://github.com/LSH9832/edgeyolo 論文&#xff1a;https://arxiv.org/pdf/2302.07483 …

宮格導航--純血鴻蒙組件庫AUI

摘要&#xff1a; 宮格導航(A_GirdNav)&#xff1a;可設置導航數據&#xff0c;建議導航項超過16個&#xff0c;可設置“更多”圖標指向的頁面路由。最多顯示兩行&#xff0c;手機每行最多顯示4個圖標&#xff0c;折疊屏每行最多6個圖標&#xff0c;平板每行最多8個圖標。多余圖…

調試的按鈕

在Debug的時候&#xff0c;會有一些按鈕&#xff0c;我們需要知道它們各自的作用。 注&#xff1a;調試器本身并沒有一個直接的、可以撤銷已執行代碼效果的“返回上一步&#xff08;Undo Last Step&#xff09;”或“逆向執行&#xff08;Reverse Debugging&#xff09;”按鈕…

人工智能如何協助老師做課題

第一步&#xff1a;在騰訊元寶對話框中輸入如何協助老師做課題&#xff0c;通過提問&#xff0c;我們了解了老師做課題的步驟和建議。 第二步&#xff1a;開題報告提問&#xff0c;騰訊元寶對話框中&#xff0c;輸入“大單元視域下小學數學教學實踐研究課題開題報告。”......…

OpenGL Chan視頻學習-5 Vertex Attributes and Layouts in OpenGL

bilibili視頻鏈接&#xff1a; 【最好的OpenGL教程之一】https://www.bilibili.com/video/BV1MJ411u7Bc?p5&vd_source44b77bde056381262ee55e448b9b1973 一、知識點整理 1.1.OpenGL管線工作流程 為顯卡提供繪制的所有數據&#xff0c;并將數據存儲在GPU內存使用著色器&…

Linux_編輯器Vim基本使用

?? 歡迎大家來到小傘的大講堂?? &#x1f388;&#x1f388;養成好習慣&#xff0c;先贊后看哦~&#x1f388;&#x1f388; 所屬專欄&#xff1a;LInux_st 小傘的主頁&#xff1a;xiaosan_blog 制作不易&#xff01;點個贊吧&#xff01;&#xff01;謝謝喵&#xff01;&a…

MyBatis 高級映射功能詳解:處理復雜數據庫關系

MyBatis 的高級映射功能是其強大特性之一&#xff0c;它允許開發者輕松處理數據庫中的復雜關系&#xff0c;如一對一、一對多和多對多關系。本文將深入探討這些高級映射功能&#xff0c;包括映射配置方法、嵌套查詢和關聯查詢的使用&#xff0c;并通過示例代碼進行演示。 1.數據…

Halo:一個強大易用的國產開源建站工具

Halo 是一款國產開源的建站工具&#xff0c;適合快速搭建博客、論壇、知識庫、公司官網等多種類型的網站&#xff0c;目前在 GitHub 上已經獲得了 35.6k Star。 功能特性 Halo 核心功能與優勢包括&#xff1a; 插件架構&#xff1a;Halo 采用可插拔架構&#xff0c;功能模塊之…

Java-ArrayList集合的遍歷方式詳解

Java-ArrayList集合的遍歷方式詳解 二、ArrayList概述三、ArrayList的遍歷方式1. 普通for循環遍歷2. 增強for循環遍歷3. 迭代器遍歷4. ListIterator遍歷5. Java 8 Stream API遍歷 四、性能對比與分析性能測試結果分析 五、遍歷方式的選擇建議六、常見遍歷陷阱與注意事項1. 并發…

華為網路設備學習-23(路由器OSPF-LSA及特殊詳解 二)

OSPF動態路由協議要求&#xff1a; 1.必須有一個骨干區域&#xff08;Area 0&#xff09;。有且僅有一個&#xff0c;而且連續不可分割。 2.所有非骨干區域&#xff08;Area 1-n&#xff09;必須和骨干區域&#xff08;Area 0&#xff09;直接相連&#xff0c;且所有區域之間…

基于大模型的急性腐蝕性胃炎風險預測與診療方案研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的 1.3 國內外研究現狀 二、急性腐蝕性胃炎概述 2.1 定義與發病機制 2.2 病因分析 2.3 臨床表現與分型 2.4 診斷方法 三、大模型技術介紹 3.1 大模型原理 3.2 常用大模型及在醫療領域應用案例 3.3 選擇用于急性腐蝕性…

泰迪杯特等獎案例深度解析:基于三維點云與深度學習的復雜零件裝配質量檢測系統設計

一、案例背景與行業痛點 1.1 工業裝配質檢的現狀與挑戰 在精密制造領域(如航空航天發動機、新能源汽車電池模組),復雜零件的裝配質量直接影響產品性能與安全性。傳統人工質檢存在效率低(單件檢測耗時>3分鐘)、漏檢率高(約15%)等問題,而現有自動化方案面臨以下技術…

離散傅里葉變換DFT推導及理解

DTFT到DFT的推導 關于DTFT的相關推導已經做過總結&#xff0c;詳見《DTFT及其反變換的直觀理解》&#xff0c;每一個離散的頻率分量都是由時域中的復指數信號累加得到的&#xff0c;DTFT得到的頻譜時頻率的連續函數 。 離散時間傅里葉變換公式&#xff0c;式1&#xff1a; 將…

欣佰特科技|工業 / 農業 / AR 場景怎么選?Stereolabs ZED 雙目3D相機型號對比與選型建議

Stereolabs ZED 相機系列為視覺感知領域提供了多種創新解決方案&#xff0c;適用于不同應用場景。選擇合適的 ZED 相機型號&#xff0c;需綜合考慮分辨率、深度感知范圍、接口類型等因素。 Stereolabs ZED 相機產品系列概覽 ZED&#xff1a;首款立體視覺相機&#xff0c;專為高…

黑馬點評Reids重點詳解(Reids使用重點)

目錄 一、短信登錄&#xff08;redisseesion&#xff09; 基于Session實現登錄流程 &#x1f504; 圖中關鍵模塊解釋&#xff1a; 利用seesion登錄的問題 設計key的具體細節 整體訪問流程 二、商戶查詢緩存 reids與數據庫主動更新的三種方案 緩存穿透 緩存雪崩問題及…

【Pandas】pandas DataFrame add_suffix

Pandas2.2 DataFrame Reindexing selection label manipulation 方法描述DataFrame.add_prefix(prefix[, axis])用于在 DataFrame 的行標簽或列標簽前添加指定前綴的方法DataFrame.add_suffix(suffix[, axis])用于在 DataFrame 的行標簽或列標簽后添加指定后綴的方法 pandas…

解鎖MCP:AI大模型的萬能工具箱

摘要&#xff1a;MCP&#xff08;Model Context Protocol&#xff0c;模型上下文協議&#xff09;是由Anthropic開源發布的一項技術&#xff0c;旨在作為AI大模型與外部數據和工具之間溝通的“通用語言”。它通過標準化協議&#xff0c;讓大模型能夠自動調用外部工具完成任務&a…

nginx性能調優與深度監控

目錄 nginx性能調優 更改進程數與連接數 進程數 連接數 靜態緩存功能設置 日志切割 配置網頁壓縮 nginx 的深度監控 GoAccess 簡介 GoAccess安裝 ?編輯 配置中文環境 GOAccess生成中文報告 測試訪問 nginx vts 簡介 nginx vts 安裝 nginx配置開啟vts 測試訪問…

【時時三省】Python 語言----牛客網刷題筆記

目錄 1,常用函數 1,input() 2,map() 3,split() 4,range() 5, 切片 6,列表推導式 山不在高,有仙則名。水不在深,有龍則靈。 ----CSDN 時時三省 1,常用函數 1,input() 該函數遇到 換行停止接收,返回類型為字符串 2,map() 該函數出鏡率較高,目的是將一個可迭…

docker compose yml 啟動的容器中,如何使用linux環境變量賦值

在 Docker Compose 中&#xff0c;可以通過環境變量&#xff08;${VAR} 或 $VAR&#xff09;來動態配置容器。以下是幾種常見的使用方式 - 使用 env_file 加載變量文件 可以單獨定義一個環境變量文件&#xff08;如 app.env&#xff09;&#xff0c;然后在 docker-compose.y…