Android Activity的启动流程(Android-10)

前言

在Android开发中,我们经常会用到startActivity(Intent)方法,但是你知道startActivity(Intent)后Activity的启动流程吗?今天就专门讲一下最基础的startActivity(Intent)看一下Activity的启动流程,同时由于Launcher的启动后续和这里基本类似,就记录在一起。注意本章都是基于Android-10来讲解的。

客户端startActivity启动流程

startActivity启动流程本质上就是:向ActivityManagerService发送启动请求。由于发送端基本是在当前用户App进程或者Launcher进程(从桌面启动新的App)。除了SystemServer启动Launcher外,都需要借助AIDL发送请求。

1:客户端启动流程

startActivity方法在Activity,startActivity有两个重载方法,最终都会调用startActivityForResult(Intent,int, Bundle)方法,代码如下:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {//...Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);//...} else {if (options != null) {mParent.startActivityFromChild(this, intent, requestCode, options);} else {mParent.startActivityFromChild(this, intent, requestCode);}}
}

其中mParent是当前Activity的父Activity,目前我只能想到TabActivity场景下才会存在一个Activity中包含Activity的情况,虽然它已经被Fragment替代了。所以基本上代码都是走mParent == null的流程,调用Instrumentation的execStartActivity方法。当然,在startActivityFromChild 方法里也是如此(不难发现,该方法已经被废弃了),源码如下,俩个重载的方法均已经废弃:

    /** @deprecated */@Deprecatedpublic void startActivityFromChild(@NonNull Activity child, Intent intent, int requestCode) {throw new RuntimeException("Stub!");}/** @deprecated */@Deprecatedpublic void startActivityFromChild(@NonNull Activity child, Intent intent, int requestCode, @Nullable Bundle options) {throw new RuntimeException("Stub!");}

我们继续看execStartActivity方法的关键代码:

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {//...try {intent.migrateExtraStreamToClipData(who);intent.prepareToLeaveProcess(who);//备注一int result = ActivityTaskManager.getService().startActivity(whoThread,who.getBasePackageName(), who.getAttributionTag(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()), token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);//备注二checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;
}

其中,备注一处就是获取ActivityTaskManager服务并向其发送请求。备注二处会根据备注一处的返回结果检查Activity的启动状态,源码如下:

public static void checkStartActivityResult(int res, Object intent) {if (!ActivityManager.isStartResultFatalError(res)) {//如果没法发生启动错误,直接返回return;}switch (res) {case ActivityManager.START_INTENT_NOT_RESOLVED:case ActivityManager.START_CLASS_NOT_FOUND:if (intent instanceof Intent && ((Intent)intent).getComponent() != null)throw new ActivityNotFoundException("Unable to find explicit activity class "+ ((Intent)intent).getComponent().toShortString()+ "; have you declared this activity in your AndroidManifest.xml?");throw new ActivityNotFoundException("No Activity found to handle " + intent);case ActivityManager.START_PERMISSION_DENIED:throw new SecurityException("Not allowed to start activity "+ intent);case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:throw new AndroidRuntimeException("FORWARD_RESULT_FLAG used while also requesting a result");//...default:throw new AndroidRuntimeException("Unknown error code "+ res + " when starting " + intent);}
}

可以看到checkStartActivityResult在Activity里会根据启动失败的原因并抛出异常:注意里面的case ActivityManager.START_CLASS_NOT_FOUND,你一定知道如果没有在AndroidManifest.xml文件里声明Activity,就会抛出异常。没错,就是根据AMS返回的错误码,在这里抛出的异常。

当然,检查并不是在这里。checkStartActivityResult不负责任何启动Activity以及检查配置文件的工作,它之负责根据AMS的返回结果抛出异常。接下来回到备注一,看一下重点工作 ActivityTaskManager的getService方法。

public static IActivityTaskManager getService() {return IActivityTaskManagerSingleton.get();}@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =new Singleton<IActivityTaskManager>() {@Overrideprotected IActivityTaskManager create() {final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);return IActivityTaskManager.Stub.asInterface(b);}};

代码比较简单,一个匿名内部类IActivityTaskManagerSingleton,里面的create 方法返回了一个IActivityTaskManager对象,根据方法里的内容就能猜出它个AIDL,该AIDL文件可在IActivityTaskManager源码里查看。

接着看一个ActivityTaskManagerSingleton.get()方法,get方法在它的父类Singleton中定义:

public abstract class Singleton<T> {@UnsupportedAppUsagepublic Singleton() {}@UnsupportedAppUsageprivate T mInstance;protected abstract T create();@UnsupportedAppUsagepublic final T get() {synchronized (this) {if (mInstance == null) {mInstance = create();}return mInstance;}}
}

很简单的代码,一个懒汉式单例。如果实例为空,就调用create()方法,也就是上文中匿名内部类中复写的方法。到这里,一切就明了了:这里就准备开始跨进程通讯了ActivityTaskManager.getService方法就是客户端(发起创建Activity的当前App进程)向服务端(AMS)获取获取Binder对象用于跨进程通讯。而关键代码就是IActivityTaskManager.Stub.asInterface(b)。(注意IActivityTaskManager只是个AIDL文件,是为了自动生成代码的工具,更多关于AIDL的知识请看Android里的多进程和跨进程通讯方式)

接下来看final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);,看它是如何获得Binder实例的:

private static Map<String, IBinder> sCache = new ArrayMap<String, IBinder>();public static IBinder getService(String name) {try {IBinder service = sCache.get(name);if (service != null) {// 有缓存,直接从缓存获取return service;} else {// 没有缓存重新通过ServiceManager获取return Binder.allowBlocking(rawGetService(name));}} catch (RemoteException e) {Log.e(TAG, "error in getService", e);}return null;
}

上面是ServiceManager.getService方法的代码。ServiceManager是Android用来关系系统服务的,Android系统给我们提供了很多服务,每一个服务都有一个对应的类(AMS,WMS等),开发过程中我们就是通过这些类来获得我们所需要的服务。而ServiceManager则通过一个ArrayMap来缓存这些服务。这里也是如此:getService(Context.ACTIVITY_TASK_SERVICE)不难看出是要获得ActivityTaskManagerService的。看一下ActivityTaskManagerService:
在这里插入图片描述

它继承了IActivityTaskManager.Stub!!!就和上文中我们的AIDL文件对应上了。可以肯定ActivityTaskManagerService就是这次AIDL跨进程通讯的服务端了。

为了以防万一,我们还是要追踪一下ActivityTaskManagerService是如何添加到ServiceManager中的。这时,我们就要了解一下Android系统的启动流程了:ActivityTaskManagerService作为系统服务,是在SystemServer中被启动的:

 ActivityTaskManagerService atm = mSystemServiceManager.startService(ActivityTaskManagerService.Lifecycle.class).getService();
mActivityManagerService = ActivityManagerService.Lifecycle.startService(mSystemServiceManager, atm);

其中SystemServiceManager.startService是通过反射创建Service的:

public <T extends SystemService> T startService(Class<T> serviceClass) {try {final String name = serviceClass.getName();//..final T service;try {//通过反射创建实例Constructor<T> constructor = serviceClass.getConstructor(Context.class);service = constructor.newInstance(mContext);} catch (InstantiationException ex) {throw new RuntimeException("Failed to create service " + name+ ": service could not be instantiated", ex);} //...//启动服务startService(service);return service;} finally {Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);}
}

代码很简单,就是通过反射创建服务并启动。我们接着看启动服务的startService(service);方法:

public void startService(@NonNull final SystemService service) {//..try {service.onStart();} catch (RuntimeException ex) {throw new RuntimeException("Failed to start service " + service.getClass().getName()+ ": onStart threw an exception", ex);}}

这里可以看出,明明我们创建的是ActivityTaskManagerServiceLifecycle,但是上面这段代码却是接收一个SystemService并调用它的onStart方法?原因只有一个,ActivityTaskManagerServiceLifecycle是SystemService的子类:

public static final class Lifecycle extends SystemService {private final ActivityTaskManagerService mService;public Lifecycle(Context context) {super(context);mService = new ActivityTaskManagerService(context);}@Overridepublic void onStart() {publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);mService.start();}//...
}

看一下源码也确实如此,那么紧接着看SystemService在Lifecycle中的实现中的一行代码:publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);。这里有出现了Context.ACTIVITY_TASK_SERVICE。就是在这里将ActivityTaskManagerService添加到ServiceManager中的。查看源码可发现SystemService有多个publishBinderService重载,并最终都调用了:

protected final void publishBinderService(String name, IBinder service,boolean allowIsolated, int dumpPriority) {ServiceManager.addService(name, service, allowIsolated, dumpPriority);}

没错,这里就调用了ServiceManager.addService方法了。最终调用方法在ServiceManager中:

public static void addService(String name, IBinder service, boolean allowIsolated,int dumpPriority) {try {getIServiceManager().addService(name, service, allowIsolated, dumpPriority);} catch (RemoteException e) {Log.e(TAG, "error in addService", e);}
}private static IServiceManager getIServiceManager() {if (sServiceManager != null) {return sServiceManager;}// Find the service managersServiceManager = ServiceManagerNative.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));return sServiceManager;
}

至此,服务的获取也明确了,ActivityTaskManagerService就是在系统启动时创建的。

2:服务端接收请求流程

接下来,就是继续看startActivity方法了,客户端Instrumentation调用,服务端ActivityTaskManagerService响应。ActivityTaskManagerService中有多个startActivity重载方法。经过不断调用,最终会调用startActivityAsUser方法:

int startActivityAsUser(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,boolean validateIncomingUser) {enforceNotIsolatedCaller("startActivityAsUser");userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");return getActivityStartController().obtainStarter(intent, "startActivityAsUser").setCaller(caller).setCallingPackage(callingPackage).setResolvedType(resolvedType).setResultTo(resultTo).setResultWho(resultWho).setRequestCode(requestCode).setStartFlags(startFlags).setProfilerInfo(profilerInfo).setActivityOptions(bOptions).setMayWait(userId).execute(); //启动}

注意看ActivityStartController. obtainStarter方法!其中,最后的execute最终是执行的ActivityStarter的execute代码:

 /*** Starts an activity based on the request parameters provided earlier.* @return The starter result.*/int execute() {try {// TODO(b/64750076): Look into passing request directly to these methods to allow// for transactional diffs and preprocessing.if (mRequest.mayWait) {return startActivityMayWait(mRequest.caller, mRequest.callingUid,mRequest.callingPackage, mRequest.realCallingPid, mRequest.realCallingUid,mRequest.intent, mRequest.resolvedType,mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,mRequest.inTask, mRequest.reason,mRequest.allowPendingRemoteAnimationRegistryLookup,mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);} else {return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,mRequest.ignoreTargetSecurity, mRequest.componentSpecified,mRequest.outActivity, mRequest.inTask, mRequest.reason,mRequest.allowPendingRemoteAnimationRegistryLookup,mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);}} finally {onExecutionComplete();}}

其中,startActivity有三个的重载方法,经过不断的调用,最后会调用:startActivity(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity, boolean restrictedBgActivity) 方法:

 private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,ActivityRecord[] outActivity, boolean restrictedBgActivity) {try {mService.mWindowManager.deferSurfaceLayout();result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);} }

大致的流程图如下:
在这里插入图片描述

但是,到了Launcher启动Activity时变终止了,最终在这里合流。

public class ActivityStartController {.... ActivityStarter obtainStarter(Intent intent, String reason) {return mFactory.obtain().setIntent(intent).setReason(reason);}
}

所以,整个客户端的请求和处理流程大致如下:

在这里插入图片描述

接下来,就要开始创建了。

服务端处理流程

我们一步步分析看服务端的处理流程。

1:检验、初始化等基础工作

ActivityStarter有多个startActivity的重载方法。最终执行的关键代码如下:

private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,String callingPackage, int realCallingPid, int realCallingUid, int startFlags,SafeActivityOptions options,boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {//...//创建错误码,默认是成功的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;//..final int userId = aInfo != null && aInfo.applicationInfo != null? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;//..            final int launchFlags = intent.getFlags();//..//开始处理错误//找不到可以处理给定Intent的类if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {err = ActivityManager.START_INTENT_NOT_RESOLVED;}//找不到Intent中指定的Ativity类,就是对应我们上文中没有在配置文件里配置Activityif (err == ActivityManager.START_SUCCESS && aInfo == null) {err = ActivityManager.START_CLASS_NOT_FOUND;}//..final ActivityStack resultStack = resultRecord == null? null : resultRecord.getActivityStack();//如果错误码被改变,不为成功,则返回给客户端启动失败        if (err != START_SUCCESS) {if (resultRecord != null) {resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);}SafeActivityOptions.abort(options);return err;}// 检查启动activity的权限boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,inTask != null, callerApp, resultRecord, resultStack);abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,callingPid, resolvedType, aInfo.applicationInfo);abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,callingPackage);// 检查是否允许后台启动activityif (!abort) {try {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"shouldAbortBackgroundActivityStart");restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,originatingPendingIntent, allowBackgroundActivityStart, intent);} finally {Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);}}//..// 判断如果有权限需要哦用户审阅才能运行Activityif (aInfo != null) {// 获取此包使用的某些权限是否需要用户审核才能运行任何应用程序组件if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(aInfo.packageName, userId)) {IIntentSender target = mService.getIntentSenderLocked(ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,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);//..}}//创建ActivityRecordActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,mSupervisor, checkedOptions, sourceRecord);//..//创建ActivityStackfinal ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();//..// 进入启动activity的流程(final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);return res;}

该方法主要做了一下两件事:

  1. 执行初步的配置、权限等基本的检查工作;
  2. 创建ActivityRecord对象,并获取ActivityStack对象。

第一步的检查包括Intent的检查,包括里面Activity的类型检查。还要进行权限,是否允许后台启动等检查。第二部主要是创建Activity启动的辅助类,ActivityRecord负责存储一些关于Activity组件的相关信息。是应用层Activity组件在AMS中的代表,每一个在应用中启动的Activity,在AMS中都有一个ActivityRecord实例来与之对应。ActivityRecord则由TaskRecord通过一个ArrayList管理,而ActivityStack则用来管理TaskRecord。它们之间的关系大致如下图片所示:

在这里插入图片描述
紧接着又会调用startActivity的重载方法:

private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,ActivityRecord[] outActivity, boolean restrictedBgActivity) {int result = START_CANCELED;final ActivityStack startedActivityStack;try {//准备启动,暂停页面布局绘制mService.mWindowManager.deferSurfaceLayout();result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);} finally {final ActivityStack currentStack = r.getActivityStack();startedActivityStack = currentStack != null ? currentStack : mTargetStack;if (ActivityManager.isStartResultSuccessful(result)) {if (startedActivityStack != null) {//处理Activity信息,并确保显示,更新方向等配置信息final ActivityRecord currentTop =startedActivityStack.topRunningActivityLocked();if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {mRootActivityContainer.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),true /* markFrozenIfConfigChanged */, false /* deferResume */);}}} else {//如果失败了,请取消Activity与栈的关联。//让Activity处于不完整状态可能会导致问题,//例如在没有窗口容器的情况下执行操作。final ActivityStack stack = mStartActivity.getActivityStack();if (stack != null) {stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,null /* intentResultData */, "startActivity", true /* oomAdj */);}//执行失败的逻辑,移除堆信息if (startedActivityStack != null && startedActivityStack.isAttached()&& startedActivityStack.numActivities() == 0&& !startedActivityStack.isActivityTypeHome()) {startedActivityStack.remove();}}//重新开始绘制mService.mWindowManager.continueSurfaceLayout();}postStartActivityProcessing(r, result, startedActivityStack);return result;}

代码的详细解读写在了注释里面。这里关键的步骤就是调用startActivityUnchecked。

2:任务栈的创建和获取

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,ActivityRecord[] outActivity, boolean restrictedBgActivity) {//初始化Activity配置    //此处会直接将mDoResume设置为true//在该方法末尾会用到setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,voiceInteractor, restrictedBgActivity);final int preferredWindowingMode = mLaunchParams.mWindowingMode;//计算Activity的Flag,也就是mLaunchFlagscomputeLaunchingTaskFlags();computeSourceStack();//将mLaunchFlags设置给mIntentmIntent.setFlags(mLaunchFlags);//判断Activity是否可以放入到一个已经存在的任务栈//如果返回null则标识需要新的任务栈ActivityRecord reusedActivity = getReusableIntentActivity();//..//如果有可以复用的ActivityRecordif (reusedActivity != null) {//..//判断是否需要清空任务栈栈顶final boolean clearTopAndResetStandardLaunchMode =(mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED))== (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)&& mLaunchMode == LAUNCH_MULTIPLE;//将ActivityRecord加入到TaskRecordif (mStartActivity.getTaskRecord() == null && !clearTopAndResetStandardLaunchMode) {mStartActivity.setTask(reusedActivity.getTaskRecord());}// 判断是否要清除TaskRecordif ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0|| isDocumentLaunchesIntoExisting(mLaunchFlags)|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {//获取可复用的TaskRecordfinal TaskRecord task = reusedActivity.getTaskRecord();final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,mLaunchFlags);//重新进行赋值if (reusedActivity.getTaskRecord() == null) {reusedActivity.setTask(task);}//..}mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);//将被复用的Task防置到前台reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);final ActivityRecord outResult =outActivity != null && outActivity.length > 0 ? outActivity[0] : null;// When there is a reused activity and the current result is a trampoline activity,// set the reused activity as the result.if (outResult != null && (outResult.finishing || outResult.noDisplay)) {outActivity[0] = reusedActivity;}if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {//START_FLAG_ONLY_IF_NEEDED:只需要使task放到前台,无需去启动,(eg. 从桌面点击图标恢复task的情况)。resumeTargetStackIfNeeded();return START_RETURN_INTENT_TO_CALLER;}if (reusedActivity != null) {setTaskFromIntentActivity(reusedActivity);if (!mAddingToTask && mReuseTask == null) {//将Stack调用到前台resumeTargetStackIfNeeded();//..return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;}}}// ..// 接下来判断要启动的Activity和当前栈顶的是否一样//并且判断时序需要启动final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();final ActivityRecord topFocused = topStack.getTopActivity();final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);//判断是否需要启动Activityfinal boolean dontStart = top != null && mStartActivity.resultTo == null&& top.mActivityComponent.equals(mStartActivity.mActivityComponent)&& top.mUserId == mStartActivity.mUserId&& top.attachedToProcess()&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))&& (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);if (dontStart) {//无需重启,直接将当前activity显示到前台topStack.mLastPausedActivity = null;if (mDoResume) {mRootActivityContainer.resumeFocusedStacksTopActivities();}//..//返回启动结果,结束return START_DELIVERED_TO_TOP;}boolean newTask = false;final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)? mSourceRecord.getTaskRecord() : null;int result = START_SUCCESS;// 判断是否需要一个新的任务栈// 并分情况设置任务栈if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {newTask = true;result = setTaskFromReuseOrCreateNewTask(taskToAffiliate);} else if (mSourceRecord != null) {result = setTaskFromSourceRecord();} else if (mInTask != null) {result = setTaskFromInTask();} else {result = setTaskToCurrentTopOrCreateNewTask();}//根据设置任务栈的返回值判断是否发生错误//如果发生了错误,那么就直接返回if (result != START_SUCCESS) {return result;}//..//这里我们只关注mDoResume为true的情况//因为在我们上文中startActivity方法中有如下调用://final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,//        true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);//其中的doResume,在该方法开头第一步,就会将mDoResume设置为trueif (mDoResume) {final ActivityRecord topTaskActivity =mStartActivity.getTaskRecord().topRunningActivityLocked();//如果当前任务栈没有获得焦点走这里//我们按照主流程会走else        if (!mTargetStack.isFocusable()|| (topTaskActivity != null && topTaskActivity.mTaskOverlay&& mStartActivity != topTaskActivity)) {//..} else {if (mTargetStack.isFocusable()&& !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {mTargetStack.moveToFront("startActivityUnchecked");}mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, mStartActivity, mOptions);}} else if (mStartActivity != null) {//..}//..return START_SUCCESS;}

该方法代码量很大,关键的节点已经写在注释里面了。但是它的工作其实也很简单:主要就是准备好任务栈,并且将acticityRecord加入到TaskRecord中。而它的复杂支出就在于复用等一系列的判断和准备工作。

这里补充一下getReusableIntentActivity判断任务栈复用的关键代码:

 private ActivityRecord getReusableIntentActivity() {//android会尽量的将Activity放入到已经存在的任务栈中//尤其是使用了singleTask和singleInstance模式启动的Activity//而对于使用了FLAG_ACTIVITY_NEW_TASK也会尽量保持这种风格//以下集中情况才会允许插入到已经存在的任务栈里//standard启动模式而且intent且添加FLAG_ACTIVITY_NEW_TASK//使用singleTask和singleInstance启动的Activityboolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);ActivityRecord intentActivity = null;if (mOptions != null && mOptions.getLaunchTaskId() != -1) {final TaskRecord task = mRootActivityContainer.anyTaskForId(mOptions.getLaunchTaskId());intentActivity = task != null ? task.getTopActivity() : null;} else if (putIntoExistingTask) {//..//省略复用的逻辑}//不能复用桌面Activityif (intentActivity != null&& (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())&& intentActivity.getDisplayId() != mPreferredDisplayId) {intentActivity = null;}return intentActivity;}

此时,任务栈已经准备好了。紧接着关注一下 mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, mStartActivity, mOptions);也就是RootActivityContainer.resumeFocusedStacksTopActivities方法:

boolean resumeFocusedStacksTopActivities(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {//..boolean result = false;if (targetStack != null && (targetStack.isTopStackOnDisplay()|| getTopDisplayFocusedStack() == targetStack)) {//如果Activity的目标栈正处于前台,那么直接调用方法//将ActivityRecord(target)放置到顶部result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);}for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {boolean resumedOnDisplay = false;final ActivityDisplay display = mActivityDisplays.get(displayNdx);//..//如果目标栈没有显示在屏幕上//则获取当前的ActivityStack,将ActivityRecord(target)放置到顶部if (!resumedOnDisplay) {final ActivityStack focusedStack = display.getFocusedStack();if (focusedStack != null) {focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);}}}return result;}

该段代码的目的就是为了让目标的Activity的表述ActivityRecord(target)放置在前台。当然,是通过ActivityStack.resumeTopActivityUncheckedLocked方法实现的。注意此时:放置我们要启动Activity的ActivityRecord的TaskRecord已经放在了前台,同时该ActivityRecord处在顶部。

3:通知ActivityThread创建

接下来关注关键的步骤,调用了resumeTopActivityInnerLocked方法:


private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {if (!mService.isBooting() && !mService.isBooted()) {// Not ready yet!return false;}//找到当前Task里最顶层的ActivityRecord//注意不是Activity的任务栈,而是ActivityTask//可以理解为是启动我们Activity(B)的Activity(A)//A可能是不同App的,比如Launcher,也可能是和B同一应用内//还记得上段代码的任务吗:放置我们要启动Activity的ActivityRecord的TaskRecord已经放在了前台//同时该ActivityRecord处在顶部。//这个next就是我们要启动的了ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);//..//判断是否还有其他RESUMED状态的Activityif (mResumedActivity == next && next.isState(RESUMED)&& display.allResumedActivitiesComplete()) {// Make sure we have executed any pending transitions, since there// should be nothing left to do at this point.executeAppTransition(options);if (DEBUG_STATES) Slog.d(TAG_STATES,"resumeTopActivityLocked: Top activity resumed " + next);return false;}//..//重点一//调用ActivityDisplay.pauseBackStacks方法暂停后台Stack的Activityboolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);//暂停掉当前Stacks里处于活动状态的Activityif (mResumedActivity != null) {if (DEBUG_STATES) Slog.d(TAG_STATES,"resumeTopActivityLocked: Pausing " + mResumedActivity);pausing |= startPausingLocked(userLeaving, false, next, false);}//判断要启动的Activity所要运行的进程是否已经存在if (next.attachedToProcess()) {try {//重点二//当Activity之前启动过,直接resumefinal ClientTransaction transaction =ClientTransaction.obtain(next.app.getThread(), next.appToken);//..mService.getLifecycleManager().scheduleTransaction(transaction);if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "+ next);} catch (Exception e) {// Whoops, need to restart this activity!//..//重点三mStackSupervisor.startSpecificActivityLocked(next, true, false);}//.. } else {//进程没有运行,走启动进程的流程//..//重点四//创建进程的的逻辑就不展开了,创建完成之后之后最终还是会走后续的流程mStackSupervisor.startSpecificActivityLocked(next, true, true);}return true;}

代码注释比较详细了,这里解读一下关键点:

  1. 重点一:暂停掉处在前台的Activity,该步骤最终是通过startPausingLocked方法实现的,在这里调用了当前正在显示的Activity的onPause方法。
  2. 重点二:如果之前Activity启动过,直接resume,否则的话抛出异常,进入重点三
  3. 重点三:向ActivityThread发送创建Activity的请求。
  4. 重点四:如果Activity所要运行的线程还未创建,那么就会请求Zygote创建进程(创建过程就不展开说了)。而在进程创建完之后。Zygote会通过Binder通知ATMS(ActivityTaskManagerService),紧接着又会通过类似重点二的方式请求ActivityThread创建。

接着,我们看看是如何通知ActivityThread创建Activity的。分析重点三处的代码,它会通过ActivityStackSupervisor.startSpecificActivityLocked调用ActivityStackSupervisor.realStartActivityLocked方法:

boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,boolean andResume, boolean checkConfig) throws RemoteException {//..try {//..// 创建启动Activity的任务final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.appToken);final DisplayContent dc = r.getDisplay().mDisplayContent;// 非常重要:添加 LaunchActivityItem callback(备注一)clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),System.identityHashCode(r), r.info,mergedConfiguration.getGlobalConfiguration(),mergedConfiguration.getOverrideConfiguration(), r.compat,r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),r.icicle, r.persistentState, results, newIntents,dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),r.assistToken));//..// 执行启动Activity的任务mService.getLifecycleManager().scheduleTransaction(clientTransaction);//} //..return true;}

请记住备注一,这里添加了个LaunchActivityItem作为CallBack,它很重要。但是在讲解它之前,我们首先看ClientTransaction.obtain方法:

public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);if (instance == null) {instance = new ClientTransaction();}instance.mClient = client;instance.mActivityToken = activityToken;return instance;}

代码很简单,就是初始化ClientTransaction,关键点在于mClient成员变量。它是一个IApplicationThread类型的,没错,它是AIDL。而它的具体实现在实现是ActivityThread.ApplicationThread,是ActivityThread的一个内部类。紧接着看mService.getLifecycleManager().scheduleTransaction(transaction);,这段代码调用了ClientLifecycleManager.scheduleTransaction:

void scheduleTransaction(ClientTransaction transaction) throws RemoteException {//..transaction.schedule();//..}

紧接着调用ClientTransaction.schedule方法

public void schedule() throws RemoteException {mClient.scheduleTransaction(this);}

而这个mClient,就是我们的AIDL客户端(AMS)。使用它向我们的服务端(ActivityThread)发送请求:它实现就在ActivityThread$ApplicationThread中。

ActivityThread接受请求开始创建

接下来就是ActivityThread处理请求了:

@Overridepublic void scheduleTransaction(ClientTransaction transaction) throws RemoteException {ActivityThread.this.scheduleTransaction(transaction);}

由于 ActivityThread继承自ClientTransactionHandler,所以scheduleTransaction会调用ClientTransactionHandler. scheduleTransaction方法:

void scheduleTransaction(ClientTransaction transaction) {transaction.preExecute(this);sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);}

而sendMessage定义在ActivityThread中,它有多个重载方法,最终会执行:

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {if (DEBUG_MESSAGES) {Slog.v(TAG,"SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj);}Message msg = Message.obtain();msg.what = what;msg.obj = obj;msg.arg1 = arg1;msg.arg2 = arg2;if (async) {msg.setAsynchronous(true);}mH.sendMessage(msg);}

很明显这是一个Handler,接着看mH的具体实现:

public void handleMessage(Message msg) {//..switch (msg.what) {//..//执行生命周期的调度工作case EXECUTE_TRANSACTION:final ClientTransaction transaction = (ClientTransaction) msg.obj;mTransactionExecutor.execute(transaction);//..break;//..}//..
}

很明显,AMS使用Binder请求ActivityThread创建Activity,然后ActivityThread通过Handler机制向TransactionExecutor发送创建的请求。

紧接着看TransactionExecutor的execute方法:

public void execute(ClientTransaction transaction) {//..executeCallbacks(transaction);//..
}

关键方法就是调用了executeCallbacks:

    public void executeCallbacks(ClientTransaction transaction) {final List<ClientTransactionItem> callbacks = transaction.getCallbacks();//..final int size = callbacks.size();for (int i = 0; i < size; ++i) {//..item.execute(mTransactionHandler, token, mPendingActions);item.postExecute(mTransactionHandler, token, mPendingActions);//..}}

没错,在这里就用到了CallBack了,这里面必定有刚刚备注一里提到的LaunchActivityItem。我们直接看它的execute方法:

 public void execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {//..client.handleLaunchActivity(r, pendingActions, null /* customIntent */);Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}

调用了ClientTransactionHandler的handleLaunchActivity方法。还记得上文说的ActivityThread继承自ClientTransactionHandler。其实这个ClientTransactionHandler就可以认为是ActivityThread,其中handleLaunchActivity的实现如下:

public Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, Intent customIntent) {final Activity a = performLaunchActivity(r, customIntent);}

只留下关键代码,调用了performLaunchActivity方法:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {ActivityInfo aInfo = r.activityInfo;//..//创建上下文环境ContextImpl appContext = createBaseContextForActivity(r);Activity activity = null;try {//重点一:java.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);StrictMode.incrementExpectedActivityCount(activity.getClass());r.intent.setExtrasClassLoader(cl);r.intent.prepareToEnterProcess();if (r.state != null) {r.state.setClassLoader(cl);}} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to instantiate activity " + component+ ": " + e.toString(), e);}}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);//..if (activity != null) {//..//重点二:activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.configCallback,r.assistToken);//设置主题int theme = r.activityInfo.getThemeResource();if (theme != 0) {activity.setTheme(theme);}//重点三:if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {mInstrumentation.callActivityOnCreate(activity, r.state);}//..}//..} catch (SuperNotCalledException e) {throw e;} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to start activity " + component+ ": " + e.toString(), e);}}return activity;}

最最最重要的来了,我们关注代码注释里的几点: 重点一:通过反射创建Activity; 重点二:调用了Activity.attach()方法; 重点三:通过Instrumentation对象执行Activity的onCreate()方法。

到这里,Activity就算是创建完成了。

总结

到这里,Activity的创建就完成了。放一个极简版序列图:

在这里插入图片描述
其中Ams为ActivityManagerService,Android最近一些较新的版本为了解决ActivityManagerService臃肿的问题引入了ActivityTaskManagerService来分担Ams工作,就是图中的Atms。

Activity的关系大致如下图:

如上图所示,除了启动Activity的进程和Activity运行的进程可能为同一进程,它们互为不同进程。当要创建一个Activity时:

  1. 如果Activity所依赖的进程已经存在,那么流程为:Step:1->2。如果启动进程和运行进程是一个,那么该过程只涉及到两个进程(启动进程和AMS进程),否则就是三个(启动进程、运行进程和AMS进程)
  2. 如果Activity所依赖的进程不存在,那么流程为:Step:1->3->4->2。

本文只讲述了请求创建的流程,其中省略了很多其他操作。包括,Activity生命周期的调用(都是由Instrumentation对象来调用的)、进程的创建、activity的attach等其他重要操作。相关知识可以由于能力有限,大家可自行查看Android源码自行了解。

最后推荐一些比较好的博客,供参考学习:

  1. ActivityRecord、TaskRecord、ActivityStack以及Activity启动模式详解
  2. 四大组件之ActivityRecord
  3. 深入理解ActivityRecord、TaskRecord、ActivityStack的作用及关系
  4. framework之Activity启动流程
  5. Android启动流程源码解析

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32——DMA知识点及实战总结

1.DMA概念介绍 DMA&#xff0c;全称Direct Memory Access&#xff0c;即直接存储器访问。 DMA传输 将数据从一个地址空间复制到另一个地址空间。 注意&#xff1a;DMA传输无需CPU直接控制传输 2.DMA框图 3.DMA处理过程 外设的 8 个请求独立连接到每个通道&#xff0c;由 DMA_…

YOLOv5改进 | 融合改进篇 | 轻量化CCFM + SENetv2进行融合改进涨点 (全网独家首发)

一、本文介绍 本文给大家带来的改进机制是轻量化的Neck结构CCFM配合SENetv2改进的网络结构进行融合改进,其中CCFM为我本人根据RT-DETR模型一比一总结出来的,文中配其手撕结构图,其中SENetV2为网络结构重构化模块,通过其改进主干从而提取更有效的特征,这两个模块搭配在一起…

Java实现海南旅游景点推荐系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…

探索单元测试和 E2E 测试:提升软件质量的关键步骤(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

探索Redis特殊数据结构:Bitmaps(位图)在实际中的应用

一、概述 Redis官方提供了多种数据类型&#xff0c;除了常见的String、Hash、List、Set、zSet之外&#xff0c;还包括Stream、Geospatial、Bitmaps、Bitfields、Probabilistic&#xff08;HyperLogLog、Bloom filter、Cuckoo filter、t-digest、Top-K、Count-min sketch、Confi…

【机组】算术逻辑运算单元实验的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 &#x1f33a; 一、 实验目的…

为您的网站添加网站底部美化代码 支持任意网站

将下面代码放在网站底部或者侧边栏&#xff0c;主要是cssjs的代码&#xff01;html格式&#xff01; 在逛 Gitee、Github 等各大代码仓库时&#xff0c;往往能看到以下这种徽章式的网页链接&#xff0c;美观好看又方便&#xff0c;那么这个徽章是如何制作出来的呢&#xff1f;…

区间预测 | Matlab实现BiLSTM-Adaboost-ABKDE的集成双向长短期记忆网络自适应带宽核密度估计多变量回归区间预测

区间预测 | Matlab实现BiLSTM-Adaboost-ABKDE的集成双向长短期记忆网络自适应带宽核密度估计多变量回归区间预测 目录 区间预测 | Matlab实现BiLSTM-Adaboost-ABKDE的集成双向长短期记忆网络自适应带宽核密度估计多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 …

web架构师编辑器内容-编辑器组件图层面板功能开发01-锁定隐藏功能的开发

我们这一部分主要是对最右侧图层面板功能进行剖析&#xff0c;完成对应的功能的开发: 每个图层都对应编辑器上面的元素&#xff0c;有多少个元素就对应多少个图层&#xff0c;主要的功能如下&#xff1a; 锁定功能&#xff1a;点击锁定&#xff0c;在编辑器中没法编辑对应的组…

瑞_Java开发手册_(四)安全规约

&#x1f64a;前言&#xff1a;本文章为瑞_系列专栏之《Java开发手册》的安全规约篇。由于博主是从阿里的《Java开发手册》学习到Java的编程规约&#xff0c;所以本系列专栏主要以这本书进行讲解和拓展&#xff0c;有需要的小伙伴可以点击链接下载。本文仅供大家交流、学习及研…

如何用Python调用智谱AI的API进行智能问答

一、引言 随着人工智能技术的不断演进&#xff0c;以ChatGPT为首的智能聊天机器人如&#xff1a;文心一言、通义千问、智谱AI等受到越来越多人的喜欢。这些智能引擎不仅有网页版&#xff0c;而且开放了免费的api接口&#xff0c;并给出了样例代码。 这样&#xff0c;我们可以…

Rust 错误处理(上)

目录 1、用 panic! 处理不可恢复的错误 对应 panic 时的栈展开或终止 1.1 使用 panic! 的 backtrace 2、用 Result 处理可恢复的错误 2.1 匹配不同的错误 2.2 失败时 panic 的简写&#xff1a;unwrap 和 expect 2.3 传播错误 错误是软件中不可否认的事实&#xff0c;所…

jmeter-线程数设置为1,循环10次没问题,循环100次出现异常

一、多次尝试&#xff0c;发现出现异常的接口大致相同。 解决办法&#xff1a;在第一个出现异常的接口下添加超时时间&#xff0c;固定定时器&#xff1a;2000ms&#xff0c;再次运行就没问题了。 二、压力机自身存在的问题 1&#xff09;在网络编程中&#xff0c;特别是在短…

20240112-补 制作两个字符串字谜的最少步骤数

题目要求 给你两个长度相同的字符串 s 和 t。在一个步骤中&#xff0c;你可以选择 t 中的任意一个字符并用另一个字符替换它。 返回将 t 变为 s 的变位所需的最少步数。 字符串的 "字谜 "是指字符串中的相同字符具有不同&#xff08;或相同&#xff09;的排列顺序…

使用PyTorch实现混合专家(MoE)模型

Mixtral 8x7B 的推出在开放 AI 领域引发了广泛关注&#xff0c;特别是混合专家&#xff08;Mixture-of-Experts&#xff1a;MoEs&#xff09;这一概念被大家所认知。混合专家(MoE)概念是协作智能的象征&#xff0c;体现了“整体大于部分之和”的说法。MoE模型汇集了各种专家模型…

CMU15-445-Spring-2023-Project #4 - Concurrency Control

前置知识&#xff0c;参考上一篇博客&#xff1a;CMU15-445-Spring-2023-Project #4 - 前置知识&#xff08;lec15-20&#xff09; 通过添加一个锁管理器在 BusTub 中支持事务&#xff0c;然后将其用于并发查询执行。锁管理器将支持五种锁模式下的表锁和元组锁&#xff1a;int…

「解析」Jetson配置 git服务

这两天感冒了在家休养&#xff0c;想着把之前买的 Jetson 开发板用起来&#xff0c;买Jetson的初衷就是用来学习Linux系统&#xff0c;顺道可以部署算法&#xff0c;以及一些其他需求&#xff0c;相比树莓派而言&#xff0c;Jetson开发相对更贵&#xff0c;但是其配备了英伟达的…

电子学会C/C++编程等级考试2023年09月(八级)真题解析

C/C++编程(1~8级)全部真题・点这里 第1题:最短路径问题 平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。 若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离。现在的任务是找出从一点到另一…

详细讲解Python中的aioschedule定时任务操作

目录 前言1. 基本概念2. 基本API3. Demo 前言 如果下面的函数库无法执行&#xff0c;出现类似&#xff1a;&#xff08;前提是python3.7以上&#xff09; AttributeError: module ‘asyncio‘ has no attribute ‘run‘请检查run是否可跳转&#xff0c;如果无法跳转&#xff…

element plus 可选择树形组件(el-tree) 怎样一键展开/收起?实现方法详解

实现代码&#xff1a; 按钮&#xff1a; <el-button click"takeall" style"height: 24px">{{zhanstatus % 2 ! 0 ? "收起所有" : "展开所有"}} </el-button> 组件&#xff1a; <el-form-item label"可选择菜单…