【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的目标行为的执行是必须满足一定条件,只有条件满足,才会触发意图的目标操作。…...

为 Compose MultiPlatform 添加 C/C++ 支持(2):在 jvm 平台使用 jni 实现桌面端与 C/C++ 互操作
前言 在上篇文章中我们已经介绍了实现 Compose MultiPlatform 对 C/C 互操作的基本思路。 并且先介绍了在 kotlin native 平台使用 cinterop 实现与 C/C 的互操作。 今天这篇文章将补充在 jvm 平台使用 jni。 在 Compose MultiPlatform 中,使用 jvm 平台的是 An…...

【PyTorch】卷积神经网络
文章目录 1. 理论介绍1.1. 从全连接层到卷积层1.1.1. 背景1.1.2. 从全连接层推导出卷积层 1.2. 卷积层1.2.1. 图像卷积1.2.2. 填充和步幅1.2.3. 多通道 1.3. 池化层(又称汇聚层)1.3.1. 背景1.3.2. 池化运算1.3.3. 填充和步幅1.3.4. 多通道 1.4. 卷积神经…...

qt可以详细写的项目或技术
1.QT 图形视图框架 2.QT 模型视图结构 3.QT列表显示大量信息 4.QT播放器 5.QT 编解码 6.QT opencv...

操作系统笔记——储存系统、文件系统(王道408)
文章目录 前言储存系统地址转换内存扩展覆盖交换 储存器分配——连续分配固定大小分区动态分区分配动态分区分配算法 储存器分配——非连续分配页式管理基本思想地址变换硬件快表(TLB)多级页表 段式管理段页式管理 虚拟储存器——基于交换的内存扩充技术…...

基于Html+腾讯云播SDK开发的m3u8播放器
周末业余时间在家无事,学习了一下腾讯的云播放sdk,并制作了一个小demo(m3u8播放器),该在线工具是基于腾讯的云播sdk开发的,云播sdk非常牛,可以支持多种播放格式。 预览地址 m3u8player.org 源码…...

uniapp小程序分享为灰色
引用:https://www.cnblogs.com/panwudi/p/17074172.html uniapp开发的微信小程序,没有转发,分享: 创建一个mixin:common/share.js export default {onShareAppMessage(res) { //发送给朋友return {}},onShareTimeline(res) {//…...

python:五种算法(OOA、WOA、GWO、PSO、GA)求解23个测试函数(python代码)
一、五种算法简介 1、鱼鹰优化算法OOA 2、鲸鱼优化算法WOA 3、灰狼优化算法GWO 4、粒子群优化算法PSO 5、遗传算法GA 二、5种算法求解23个函数 (1)23个函数简介 参考文献: [1] Yao X, Liu Y, Lin G M. Evolutionary programming made…...

DIP——添加运动模糊与滤波
1.运动模糊 为了模拟图像退化的过程,在这里创建了一个用于模拟运动模糊的点扩散函数,具体模糊的方向取决于输入的motion_angle。如果运动方向接近水平,则模糊效果近似水平,如果运动方向接近垂直,则模糊效果近似垂直。具…...

SQL Server查询计划(Query Plan)——SQL处理过程
6. 查询计划(Query Plan) 6.1. SQL处理过程 就SQL语句的处理过程而言,各关系库间大同小异,尤其是商业库之间实现机制和细节差别更小些,其功能及性能支持方面也更加强大和完善。SQL Server作为商业库中的后起之秀,作为SQL语句处理过程的主要支撑和保障,其优化器及相关机…...

【动手学深度学习】(十二)现代卷积神经网络
文章目录 一、深度卷积神经网络AlexNet1.理论知识 一、深度卷积神经网络AlexNet 1.理论知识 ImageNet(2010) 图片自然物体的彩色图片手写数字的黑色图片大小468 * 38728*28样本数1.2M60K类数100010 AlexNet AlexNet赢了2012ImageNet竞赛更深更大的LeNet主要改进ÿ…...