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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…