【Android12】Android Framework系列--AMS启动Activity分析
AMS启动Activity分析
通过ActivityManagerService(AMS)提供的方法,可以启动指定的Activity。比如Launcher中点击应用图标后,调用AMS的startActivity函数启动应用。
AMS提供的服务通过IActivityManager.aidl文件定义。
// frameworks/base/core/java/android/app/IActivityManager.aidl
package android.app;
// 省略
/*** System private API for talking with the activity manager service. This* provides calls from the application back to the activity manager.** {@hide}*/
interface IActivityManager {// 省略/** @deprecated Use {@link #startActivityWithFeature} instead */@UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#startActivity(android.content.Intent)} instead")int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,int flags, in ProfilerInfo profilerInfo, in Bundle options);int startActivityWithFeature(in IApplicationThread caller, in String callingPackage,in String callingFeatureId, in Intent intent, in String resolvedType,in IBinder resultTo, in String resultWho, int requestCode, int flags,in ProfilerInfo profilerInfo, in Bundle options);// 省略
}
代码版本是Android12。分析一下从应用侧(比如Launcher)调用startActivity启动另一个应用的流程。
startActivity源码分析
应用侧可以通过context的startActivity函数调用AMS。比如下述例子。
Intent intent = new Intent();
// 启动com.linduo.test这个Package中的 MainActivity
intent.setComponent(new ComponentName("com.linduo.test", "com.linduo.test.MainActivity"));
context.startActivity(intent);
context是abstract的类,其实现在ContextImpl中。
// frameworks/base/core/java/android/content/ContextWrapper.java
public class ContextWrapper extends Context {
}// frameworks/base/core/java/android/app/ContextImpl.java
class ContextImpl extends Context {
@Override
public void startActivity(Intent intent) {warnIfCallingFromSystemProcess();startActivity(intent, null);
}@Override
public void startActivity(Intent intent, Bundle options) {warnIfCallingFromSystemProcess();// Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is// generally not allowed, except if the caller specifies the task id the activity should// be launched in. A bug was existed between N and O-MR1 which allowed this to work. We// maintain this for backwards compatibility.final int targetSdkVersion = getApplicationInfo().targetSdkVersion;if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0&& (targetSdkVersion < Build.VERSION_CODES.N|| targetSdkVersion >= Build.VERSION_CODES.P)&& (options == null|| ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {throw new AndroidRuntimeException("Calling startActivity() from outside of an Activity "+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."+ " Is this really what you want?");}mMainThread.getInstrumentation().execStartActivity(getOuterContext(), mMainThread.getApplicationThread(), null,(Activity) null, intent, -1, options);
}
}
ContextImpl通过mMainThread(ActivityThread类型)得到了Instrumentation对象,调用Instrumentation的execStartActivity函数。execStartActivity函数中,如果开启了Activity监听模式,会通知监听者onStartActivity,然后调用ActivityTaskManagerService的startActivity函数启动Activity。最后检查启动的结果,如果启动失败抛出对应的异常。
onStartActivity的监听,可以使用Instrumentation.addMonitor方式,具体内容可以查看Instrumentation.java
// frameworks/base/core/java/android/app/Instrumentation.java
@UnsupportedAppUsage
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {IApplicationThread whoThread = (IApplicationThread) contextThread;Uri referrer = target != null ? target.onProvideReferrer() : null;if (referrer != null) {intent.putExtra(Intent.EXTRA_REFERRER, referrer);}if (mActivityMonitors != null) {// 如果开启了监听,会给监听者通过 onStartActivitysynchronized (mSync) {final int N = mActivityMonitors.size();for (int i=0; i<N; i++) {final ActivityMonitor am = mActivityMonitors.get(i);ActivityResult result = null;if (am.ignoreMatchingSpecificIntents()) {result = am.onStartActivity(intent);}if (result != null) {am.mHits++;return result;} else if (am.match(who, null, intent)) {am.mHits++;if (am.isBlocking()) {return requestCode >= 0 ? am.getResult() : null;}break;}}}}try {intent.migrateExtraStreamToClipData(who);intent.prepareToLeaveProcess(who);// 调用ActivityTaskManagerService( AMS中提供的服务)int result = ActivityTaskManager.getService().startActivity(whoThread,who.getOpPackageName(), who.getAttributionTag(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()), token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);// 检查启动的结果,如果失败会throw异常。checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;
}
ActivityTaskManagerService继承了IActivityTaskManager.Stub,属于AMS提供的服务。在其startActivity函数中,会调用startActivityAsUser,判断package与UID是否相符、是否为Isolated模式的应用、并检查用户权限(源码中可以看出系统用户权限非常大,所以为了安全要慎用系统用户权限),最终调用ActivityStarter的execute启动Activity。
// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,Bundle bOptions) {// 调用startActivityAsUserreturn startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,UserHandle.getCallingUserId());
}@Override
public int startActivityAsUser(IApplicationThread caller, String callingPackage,String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,Bundle bOptions, int userId) {// 调用startActivityAsUser,validateIncomingUser为truereturn startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,true /*validateIncomingUser*/);
}private int startActivityAsUser(IApplicationThread caller, String callingPackage,@Nullable String callingFeatureId, Intent intent, String resolvedType,IBinder resultTo, String resultWho, int requestCode, int startFlags,ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {// 判断调用的package和UID相符assertPackageMatchesCallingUid(callingPackage);// 禁止来自Isolated模式应用的调用enforceNotIsolatedCaller("startActivityAsUser");// 检测用户权限。这个函数中,可以看出系统用户权限非常大(感兴趣的可以看一下)userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");// 通过ActivityStartController,取得ActivityStarter,设置好系列参数后调用execute// TODO: Switch to user app stacks here.return getActivityStartController().obtainStarter(intent, "startActivityAsUser").setCaller(caller).setCallingPackage(callingPackage).setCallingFeatureId(callingFeatureId).setResolvedType(resolvedType).setResultTo(resultTo).setResultWho(resultWho).setRequestCode(requestCode).setStartFlags(startFlags).setProfilerInfo(profilerInfo).setActivityOptions(bOptions).setUserId(userId).execute();}
ActivityStarter的execute函数中,再次进行了一些检查,比如是否存在文件句柄泄露(Intent)、config是否要变更,并调用了executeRequest函数。
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
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);final int callingUid = mRequest.realCallingUid == Request.DEFAULT_REAL_CALLING_UID? Binder.getCallingUid() : mRequest.realCallingUid;launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mRequest.intent, caller, callingUid);}// 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);}// Add checkpoint for this shutdown or reboot attempt, so we can record the original// intent action and package name.if (mRequest.intent != null) {String intentAction = mRequest.intent.getAction();String callingPackage = mRequest.callingPackage;if (intentAction != null && callingPackage != null&& (Intent.ACTION_REQUEST_SHUTDOWN.equals(intentAction)|| Intent.ACTION_SHUTDOWN.equals(intentAction)|| Intent.ACTION_REBOOT.equals(intentAction))) {ShutdownCheckPoints.recordCheckPoint(intentAction, callingPackage, null);}}int res;synchronized (mService.mGlobalLock) {final boolean globalConfigWillChange = mRequest.globalConfig != null&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;// 获取当前处于Top且Focus的RootTaskfinal Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();if (rootTask != null) {// RookTask非空,更新一下ConfigChange的Flag。rootTask.mConfigWillChange = globalConfigWillChange;}ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config "+ "will change = %b", globalConfigWillChange);final long origId = Binder.clearCallingIdentity();res = resolveToHeavyWeightSwitcherIfNeeded();if (res != START_SUCCESS) {return res;}// 这个函数中,会创建ActivityRecord,并启动Activityres = executeRequest(mRequest);// 后面的函数,主要是Config的变更。如果有变化会给config变化的通知。// 以及结果的检查。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 (rootTask != null) {rootTask.mConfigWillChange = false;}ProtoLog.v(WM_DEBUG_CONFIGURATION,"Updating to new configuration after starting activity.");mService.updateConfigurationLocked(mRequest.globalConfig, null, false);}// The original options may have additional info about metrics. The mOptions is not// used here because it may be cleared in setTargetRootTaskIfNeeded.final ActivityOptions originalOptions = mRequest.activityOptions != null? mRequest.activityOptions.getOriginalOptions() : null;// If the new record is the one that started, a new activity has created.final boolean newActivityCreated = mStartActivity == mLastStartActivityRecord;// 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,newActivityCreated, mLastStartActivityRecord, originalOptions);if (mRequest.waitResult != null) {mRequest.waitResult.result = res;res = waitResultIfNeeded(mRequest.waitResult, mLastStartActivityRecord,launchingState);}return getExternalResult(res);}} finally {onExecutionComplete();}
}
ActivityStarter的executeRequest比较长,下面省略一部分内容。根据其注释内容“Executing activity start request and starts the journey of starting an activity”也可以看出,在这里处理了activity启动的请求。函数中,进行了权限检查,包括判断了是否为Home应用,创建了ActivityRecord记录这次启动为最后一次启动的Activity,然后调用startActivityUnchecked启动应用。
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
/*** Executing activity start request and starts the journey of starting an activity. Here* begins with performing several preliminary checks. The normally activity launch flow will* go through {@link #startActivityUnchecked} to {@link #startActivityInner}.*/
private int executeRequest(Request request) {// 检查启动 权限boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,requestCode, callingPid, callingUid, callingPackage, callingFeatureId,request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord,resultRootTask);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");// 判断是否可以启动Activity.// 这里也会判断是否为Home应用,如果是Home应用的可以启动。如果不是,还要进行其他判断。restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,request.originatingPendingIntent, request.allowBackgroundActivityStart,intent);} finally {Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);}}// 创建ActivityRecordfinal ActivityRecord r = new ActivityRecord.Builder(mService).setCaller(callerApp).setLaunchedFromPid(callingPid).setLaunchedFromUid(callingUid).setLaunchedFromPackage(callingPackage).setLaunchedFromFeature(callingFeatureId).setIntent(intent).setResolvedType(resolvedType).setActivityInfo(aInfo).setConfiguration(mService.getGlobalConfiguration()).setResultTo(resultRecord).setResultWho(resultWho).setRequestCode(requestCode).setComponentSpecified(request.componentSpecified).setRootVoiceInteraction(voiceSession != null).setActivityOptions(checkedOptions).setSourceRecord(sourceRecord).build();// 记录为最后一次启动的ActivitymLastStartActivityRecord = r;// 调用startActivityUnchecked,启动ActivitymLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,restrictedBgActivity, intentGrants);if (request.outActivity != null) {request.outActivity[0] = mLastStartActivityRecord;}return mLastStartActivityResult;
}
ActivityStarter的startActivityUnchecked函数会调用startActivityInner函数,在这个函数中获取加载Activity的RootTask加载Activity,并调用RootWindowContainer的resumeFocusedTasksTopActivities函数fork出一个进程来运行Activity。
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java/*** Start an activity while most of preliminary checks has been done and caller has been* confirmed that holds necessary permissions to do so.* Here also ensures that the starting activity is removed if the start wasn't successful.*/
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, Task inTask,boolean restrictedBgActivity, NeededUriGrants intentGrants) {// 省略try {mService.deferWindowLayout();Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");// 调用startActivityInnerresult = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);} finally {// 省略}postStartActivityProcessing(r, result, startedActivityRootTask);return result;
}/*** Start an activity and determine if the activity should be adding to the top of an existing* task or delivered new intent to an existing activity. Also manipulating the activity task* onto requested or valid root-task/display.** Note: This method should only be called from {@link #startActivityUnchecked}.*/// TODO(b/152429287): Make it easier to exercise code paths through startActivityInner
@VisibleForTesting
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, Task inTask,boolean restrictedBgActivity, NeededUriGrants intentGrants) {// 省略if (mTargetRootTask == null) {// 获取目标RootTaskmTargetRootTask = getLaunchRootTask(mStartActivity, mLaunchFlags, targetTask, mOptions);}// 调用RootTask,startActivity(细节可以顺着这个函数看)mTargetRootTask.startActivityLocked(mStartActivity,topRootTask != null ? topRootTask.getTopNonFinishingActivity() : null, newTask,mKeepCurTransition, mOptions, sourceRecord);// mDoResume为Tureif (mDoResume) {if (!mTargetRootTask.isTopActivityFocusable()|| (topTaskActivity != null && topTaskActivity.isTaskOverlay()&& mStartActivity != topTaskActivity)) {// 省略} else {// If the target root-task was not previously focusable (previous top running// activity on that root-task was not visible) then any prior calls to move the// root-task to the will not update the focused root-task. If starting the new// activity now allows the task root-task to be focusable, then ensure that we// now update the focused root-task accordingly.if (mTargetRootTask.isTopActivityFocusable()&& !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {mTargetRootTask.moveToFront("startActivityInner");}mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);}}mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask);// Update the recent tasks list immediately when the activity startsmSupervisor.mRecentTasks.add(mStartActivity.getTask());mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask);return START_SUCCESS;
}
启动的Activity如果是另一个应用,Android会fork出一个进程来加载Activity。那么是在哪里进行的fork?
在ActivityStarter的handleStartResult中,会进行fork进程操作(调用Process.start实现)
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
/*** Start an activity while most of preliminary checks has been done and caller has been* confirmed that holds necessary permissions to do so.* Here also ensures that the starting activity is removed if the start wasn't successful.*/
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, Task inTask,TaskFragment inTaskFragment, boolean restrictedBgActivity,NeededUriGrants intentGrants) {int result = START_CANCELED;final Task startedActivityRootTask;// Create a transition now to record the original intent of actions taken within// startActivityInner. Otherwise, logic in startActivityInner could start a different// transition based on a sub-action.// Only do the create here (and defer requestStart) since startActivityInner might abort.final TransitionController transitionController = r.mTransitionController;Transition newTransition = (!transitionController.isCollecting()&& transitionController.getTransitionPlayer() != null)? transitionController.createTransition(TRANSIT_OPEN) : null;RemoteTransition remoteTransition = r.takeRemoteTransition();if (newTransition != null && remoteTransition != null) {newTransition.setRemoteTransition(remoteTransition);}transitionController.collect(r);try {mService.deferWindowLayout();Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, inTaskFragment, restrictedBgActivity,intentGrants);} finally {Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);// 这里会fork出一个进程(调用RootWindowContainer.ensureVisibilityAndConfig),最终调用Process.startstartedActivityRootTask = handleStartResult(r, options, result, newTransition,remoteTransition);mService.continueWindowLayout();}postStartActivityProcessing(r, result, startedActivityRootTask);return result;
}
下面是调用的堆栈(Android13输出的,大概流程一样),感兴趣的可以依照堆栈看源码.
ActivityTaskManager: at com.android.server.wm.ActivityTaskManagerService.startProcessAsync(ActivityTaskManagerService.java:4683)
ActivityTaskManager: at com.android.server.wm.ActivityTaskSupervisor.startSpecificActivity(ActivityTaskSupervisor.java:1013)
ActivityTaskManager: at com.android.server.wm.EnsureActivitiesVisibleHelper.makeVisibleAndRestartIfNeeded(EnsureActivitiesVisibleHelper.java:265)
ActivityTaskManager: at com.android.server.wm.EnsureActivitiesVisibleHelper.setActivityVisibilityState(EnsureActivitiesVisibleHelper.java:191)
ActivityTaskManager: at com.android.server.wm.EnsureActivitiesVisibleHelper.process(EnsureActivitiesVisibleHelper.java:139)
ActivityTaskManager: at com.android.server.wm.TaskFragment.updateActivityVisibilities(TaskFragment.java:985)
ActivityTaskManager: at com.android.server.wm.Task.lambda$ensureActivitiesVisible$15(Task.java:4866)
ActivityTaskManager: at com.android.server.wm.Task$$ExternalSyntheticLambda20.accept(Unknown Source:10)
ActivityTaskManager: at com.android.server.wm.Task.forAllLeafTasks(Task.java:3185)
ActivityTaskManager: at com.android.server.wm.Task.ensureActivitiesVisible(Task.java:4865)
ActivityTaskManager: at com.android.server.wm.DisplayContent.lambda$ensureActivitiesVisible$46(DisplayContent.java:6000)
ActivityTaskManager: at com.android.server.wm.DisplayContent$$ExternalSyntheticLambda17.accept(Unknown Source:10)
ActivityTaskManager: at com.android.server.wm.Task.forAllRootTasks(Task.java:3197)
ActivityTaskManager: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:1806)
ActivityTaskManager: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:1806)
ActivityTaskManager: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:1806)
ActivityTaskManager: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:1806)
ActivityTaskManager: at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:1799)
ActivityTaskManager: at com.android.server.wm.DisplayContent.ensureActivitiesVisible(DisplayContent.java:5999)
ActivityTaskManager: at com.android.server.wm.RootWindowContainer.ensureActivitiesVisible(RootWindowContainer.java:2075)
ActivityTaskManager: at com.android.server.wm.RootWindowContainer.ensureVisibilityAndConfig(RootWindowContainer.java:1876)
ActivityTaskManager: at com.android.server.wm.ActivityStarter.handleStartResult(ActivityStarter.java:1669)
ActivityTaskManager: at com.android.server.wm.ActivityStarter.startActivityUnchecked(ActivityStarter.java:1598)
ActivityTaskManager: at com.android.server.wm.ActivityStarter.executeRequest(ActivityStarter.java:1185)
ActivityTaskManager: at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:672)
ActivityTaskManager: at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1243)
ActivityTaskManager: at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1215)
ActivityTaskManager: at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:969)
ActivityTaskManager: at com.android.server.wm.ActivityTaskManagerService.onTransact(ActivityTaskManagerService.java:5152)
ActivityTaskManager: at android.os.Binder.execTransactInternal(Binder.java:1179)
ActivityTaskManager: at android.os.Binder.execTransact(Binder.java:1143)
ActivityTaskManager: java.lang.Throwable
相关文章:

【Android12】Android Framework系列--AMS启动Activity分析
AMS启动Activity分析 通过ActivityManagerService(AMS)提供的方法,可以启动指定的Activity。比如Launcher中点击应用图标后,调用AMS的startActivity函数启动应用。 AMS提供的服务通过IActivityManager.aidl文件定义。 // frameworks/base/core/java/an…...
Hive的几种排序方式、区别,使用场景
一、几种排序和区别 Hive 支持两种主要的排序方式:ORDER BY 和 SORT BY。除此之外,还有 DISTRIBUTE BY 和 CLUSTER BY 语句,它们也在排序和数据分布方面发挥作用。 1. ORDER BY ORDER BY 在 Hive 中用于对查询结果进行全局排序࿰…...

设计模式-外观模式
设计模式专栏 模式介绍模式特点应用场景外观模式和里氏替换原则的区别代码示例Java实现外观模式python实现外观模式 外观模式在spring中的应用 模式介绍 外观模式(Facade Pattern)是一种结构性设计模式,它隐藏了系统的复杂性,并向…...

Kubernetes实战(九)-kubeadm安装k8s集群
1 环境准备 1.1 主机信息 iphostname10.220.43.203master10.220.43.204node1 1.2 系统信息 $ cat /etc/redhat-release Alibaba Cloud Linux (Aliyun Linux) release 2.1903 LTS (Hunting Beagle) 2 部署准备 master/与slave主机均需要设置。 2.1 设置主机名 # master h…...

【HarmonyOS开发】拖拽动画的实现
动画的原理是在一个时间段内,多次改变UI外观,由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画。UI的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率FPS(F…...

提高问卷填写率的策略与方法
在现代社会的研究中,问卷调研是一种常见的数据收集方式。但是,随着数据的快速传播和竞争激烈的市场环境,怎样吸引大量的人填好问卷成为了科研人员关心的问题。本文将介绍一些方式和策略,以帮助你吸引大量的人填好问卷,…...

软件工程考试复习
第一章、软件工程概述 🌟软件程序数据文档(考点) 🌟计算机程序及其说明程序的各种文档称为 ( 文件 ) 。计算任务的处理对象和处理规则的描述称为 ( 程序 )。有关计算机程序功能、…...

PHP基础 - 类型比较
在 PHP 中,作为一种弱类型语言,它提供了松散比较和严格比较两种方式来比较变量的值和类型。 松散比较: 使用两个等号(==)进行比较,只会比较变量的值,而不会考虑它们的数据类型。例如: $a = 5; // 整数 $b = 5; // 字符串if ($a == $b) {echo "相等"; // 输…...

张正友相机标定法原理与实现
张正友相机标定法是张正友教授1998年提出的单平面棋盘格的相机标定方法。传统标定法的标定板是需要三维的,需要非常精确,这很难制作,而张正友教授提出的方法介于传统标定法和自标定法之间,但克服了传统标定法需要的高精度标定物的缺点,而仅需使用一个打印出来的棋盘格就可…...

【LeetCode】2723. 两个 Promise 对象相加
两个 Promise 对象相加 题目题解 题目 给定两个 promise 对象 promise1 和 promise2,返回一个新的 promise。promise1 和 promise2 都会被解析为一个数字。返回的 Promise 应该解析为这两个数字的和。 示例 1: 输入: promise1 new Promise…...
设计模式--命令模式的简单例子
引入:以一个对数组的增删改查为例。通过命令模式可以对数组进行增删改查以及撤销回滚。 一、基本概念 命令模式有多种分法,在本文中主要分为CommandMgr、Command、Receiver. CommandMgr主要用于控制命令执行等操作、Command为具体的命令、Receiver为命…...

排序算法之六:快速排序(非递归)
快速排序是非常适合使用递归的,但是同时我们也要掌握非递归的算法 因为操作系统的栈空间很小,如果递归的深度太深,容易造成栈溢出 递归改非递归一般有两种改法: 改循环借助栈(数据结构) 图示算法 不是…...

【概率方法】重要性采样
从一个极简分布出发 假设我们有一个关于随机变量 X X X 的函数 f ( X ) f(X) f(X),满足如下分布 p ( X ) p(X) p(X)0.90.1 f ( X ) f(X) f(X)0.10.9 如果我们要对 f ( X ) f(X) f(X) 的期望 E p [ f ( X ) ] \mathbb{E}_p[f(X)] Ep[f(X)] 进行估计࿰…...

MyBatis 四大核心组件之 StatementHandler 源码解析
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot 🌺 仓库主页: Gitee 💫 Github 💫 GitCode 💖 欢迎点赞…...
用Guava做本地缓存示例
缓存的作用 提升系统性能,暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用 本地缓存和分布式缓存 缓存分为本地缓存与分布式缓存。本地缓存为了保证线程安全问题,一般使用ConcurrentMap的方式保存在内存之中,而常…...
Django多对多ManyToManyField字段
Django是一个支持多对多关系的Web框架,可以在模型中定义多对多关系。多对多关系通常涉及两个实体之间的复杂交互,例如用户和组之间的关系,或者课程和学生之间的关系。在Django中,可以使用ManyToManyField字段来定义多对多关系。 …...

docker-centos中基于keepalived+niginx模拟主从热备完整过程
文章目录 一、环境准备二、主机1、环境搭建1.1 镜像拉取1.2 创建网桥1.3 启动容器1.4 配置镜像源1.5 下载工具包1.6 下载keepalived1.7 下载nginx 2、配置2.1 配置keepalived2.2 配置nginx2.2.1 查看nginx.conf2.2.2 修改index.html 3、启动3.1 启动nginx3.2 启动keepalived 4、…...

软件科技成果鉴定测试需提供哪些材料?
为了有效评估科技成果的质量,促进科技理论向实际应用转化,所以需要进行科技成果鉴定测试。申请鉴定的科技成果范围是指列入国家和省、自治区、直辖市以及国务院有关部门科技计划内的应用技术成果,以及少数科技计划外的重大应用技术成果。 …...

办公word-从不是第一页添加页码
总结 实际需要注意的是,分隔符、分节符和分页符并不是一个含义 分隔符包含其他两个;分页符:是增加一页;分节符:指将文档分为几部分。 从不是第一页插入页码1步骤 1,插入默认页码 自己可以测试时通过**…...

Android笔记(十七):PendingIntent简介
PendingIntent翻译成中文为“待定意图”,这个翻译很好地表示了它的涵义。PendingIntent描述了封装Intent意图以及该意图要执行的目标操作。PendingIntent封装Intent的目标行为的执行是必须满足一定条件,只有条件满足,才会触发意图的目标操作。…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...