Android进阶 四大组件的工作过程(一):Activity的工作过程

news2024/11/15 9:12:45

Android进阶 四大组件的工作过程(一):Activity的工作过程

在这里插入图片描述

导语

本系列文章主要是来介绍Android中四大组件的工作过程的,参照书籍为Android进阶解密,主要还是会涉及到源码的阅读。关于源码,大家可以到Android Code Search上查看,网址放在这里了:Android Code Search。除此之外,最好还需要掌握一些Android系统启动过程的知识:

  1. Android系统的启动流程(一):进入Zygote进程的初始化
  2. Android系统的启动流程(二):SystemServer处理过程
  3. Android系统的启动过程(三):Launcher启动过程
  4. Android应用程序进程的启动过程

本篇文章,主要就是来介绍Activity的工作工程的,这里以启动根Activity为例,查看Activity是如何通过系统启动的。这里我们将其分为三个部分来分析,在这之前还是需要介绍一些基础的知识。

基础知识

  1. Launcher是Android系统启动后启动的第一个App,实际上就是手机桌面,它本质上也是一个Activity。我们就是通过这个Launcher来启动其他的App的。
  2. AMS(ActivityManagerService)是属于系统服务中的核心服务,主要就是用来管理应用程序进程的创建等任务的,我们的Activity的启动也会涉及到这个服务。
  3. Activity组件启动之前需要AMS委托Zygote进程来创建出应用程序的进程。有了进程之后才能启动Activity。

Launcher请求AMS(实际上是ATMS)过程

显然我们打开一个应用程序是通过桌面点击图标开始的,那我们启动一个跟Activity显然也是通过Launcher开始的。当我们点击某个应用程序的图标时,就会调用Launcher的startActivitySafely方法,而这个方法又会调用超类的startActivitySafely方法,我们直接看超类的(ActivityContext)方法:

default boolean startActivitySafely(
            View v, Intent intent, @Nullable ItemInfo item) {

        Context context = (Context) this;//1----1
        if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
            Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
            return false;
        }

        Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;//2----2
        UserHandle user = item == null ? null : item.user;

        // Prepare intent
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//3----3
        if (v != null) {
            intent.setSourceBounds(Utilities.getViewBounds(v));
        }
        try {
            boolean isShortcut = (item instanceof WorkspaceItemInfo)
                    && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
                    || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                    && !((WorkspaceItemInfo) item).isPromise();
            if (isShortcut) {
                // Shortcuts need some special checks due to legacy reasons.
                startShortcutIntentSafely(intent, optsBundle, item);
            } else if (user == null || user.equals(Process.myUserHandle())) {
                // Could be launching some bookkeeping activity
                context.startActivity(intent, optsBundle);//4----4
            } else {
                context.getSystemService(LauncherApps.class).startMainActivity(
                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
            }
            if (item != null) {
                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
                logAppLaunch(getStatsLogManager(), item, instanceId);
            }
            return true;
        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
            Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
        }
        return false;
    }

我们分析一下这个方法,首先在注释一处它会将context对象设置为自身,这个context将会在后面使用到。接着再注释二处它获得了Activity启动的一些参数。在注释三处,它设置Intent的标志(Intent.FLAG_ACTIVITY_NEW_TASK),表示将在新的任务中启动Activity。

接着我们可以在注释四处发现有多处分支,具体来说,这里会根据情况进行启动Activity的操作。如果要启动的是一个快捷方式(Shortcut),则调用startShortcutIntentSafely方法来安全地启动快捷方式。否则,根据用户是否为当前用户来决定是调用startActivity方法还是通过LauncherApps来启动MainActivity。

这两种方式的区别在于:

  1. 使用context.startActivity(intent, optsBundle)来启动Activity是直接通过Context的方法启动,不涉及其他Launcher应用的介入。这种方式适用于启动当前应用的内部Activity,或者启动非Launcher应用的Activity。
  2. 使用LauncherApps.startMainActivity()方法启动MainActivity是通过系统的Launcher应用来启动目标Activity。这种方式通常用于启动其他应用的MainActivity,可以触发Launcher应用的一些额外处理逻辑,例如应用图标的动画、桌面的切换效果等。它还允许指定不同的用户(UserHandle)来启动Activity。

这里我们就进入注释四处的分支来启动了,这里将Intent和启动参数传入开始启动。这个方法如果继续点击去的话会跳到Context中,是个空方法,具体实现是在Activity类中,我们进入Activity中查看:

    public void startActivity(Intent intent, @Nullable Bundle options) {
        getAutofillClientController().onStartActivity(intent, mIntent);
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }

可以看到这个方法本质上是调用到startActivityForResult方法,第二个参数设置为-1表示Launcher不需要知道Activity启动的结果。接下来看startActivityForResult方法:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

我们来看判断的第一个分支,mParent表示父Activity,由于当前启动的就是根Activity,所以mParent==null成立,会进入到这个分支里面来。很明显,接下来会通过Instrumentation的execStartActivity方法来继续启动Activity。这个Instrumentation类是Android框架提供的一个重要类,用于监测和控制应用程序的运行。它允许开发者在应用程序的各个生命周期阶段插入自定义的代码逻辑,以实现一些特定的功能,例如性能监测、代码覆盖率分析、UI自动化测试等。

接下来继续看execStartActivity方法:

    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;
		.......
        try {
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            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);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

我们主要看后面的部分,这一段比较重要。这一段最主要的就是调用到了ActivityTaskManager的getService,然后通过这个Service来启动Activity,至于这个Service是什么,我们可以看一看这个getService方法:

    public static IActivityTaskManager getService() {
        return IActivityTaskManagerSingleton.get();
    }

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

很显然这是一个AIDL的调用,获取到的是ActivityTaskManagerService的远程代理调用,而ActivityTaskManagerService是是 Android 系统中的一个重要组件,用于管理应用程序的活动(Activity)和任务(Task)。它是 Android 系统中的系统服务之一,负责跟踪和管理活动的生命周期、任务的堆栈以及任务切换等操作。我们之前提到的ActivityManagerService也会依赖到ActivityTaskService。

所以说这里最终会调用到ActivityTaskManagerService的startActivity方法:

    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) {
        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }

那么其实到这里Launcher的启动请求已经进入到了ActivityTaskManagerService中了,我们接下来看第二部分,也就是从ATMS开始。

AMS(ATMS)到ApplicationThread的调用过程

上面说到到这里请求已经传递到了ActivityTaskManagerService中,具体还是需要ActivityTaskManagerService来启动。调用其startActivity方法后就会继续跳转,调用它的startActivityAsUser方法,我们来看这个startActivityAsUser方法:

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) {
        assertPackageMatchesCallingUid(callingPackage);
        enforceNotIsolatedCaller("startActivityAsUser");
        if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
            SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
                    SdkSandboxManagerLocal.class);
            if (sdkSandboxManagerLocal == null) {
                throw new IllegalStateException("SdkSandboxManagerLocal not found when starting"
                        + " an activity from an SDK sandbox uid.");
            }
            sdkSandboxManagerLocal.enforceAllowedToStartActivity(intent);
        }

        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        // 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();

    }

这个方法到最后一句return语句之前都是在检查相关的权限,判断是否有启动Activity的权限;最后一句则是继续推进启动Activity,可以看到它设置了许多启动的参数,最后调用execute方法执行了命令。具体来说,就是先通过ActivityStarterController获得一个ActivityStarter,从名字就可以看出来ActivityStarter是用来启动Activity的,所以接下来进入到ActivityStarter的execute方法中:

int execute() {
        try {
            if (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);//1-----1
                final int callingUid = mRequest.realCallingUid == Request.DEFAULT_REAL_CALLING_UID
                        ?  Binder.getCallingUid() : mRequest.realCallingUid;
                launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(
                        mRequest.intent, caller, callingUid);
            }

            if (mRequest.activityInfo == null) {
                mRequest.resolveActivity(mSupervisor); //2-----2
            }
			.........

            int res;
            synchronized (mService.mGlobalLock) {
                final boolean globalConfigWillChange = mRequest.globalConfig != null
                        && mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
                final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
                if (rootTask != null) {
                    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;
                }
                res = executeRequest(mRequest);//3-----3

              	........
    }

我们从上往下继续看,注释一处获取到了ActivityRecord类的对象,这个类是用来描述Activity信息的,也就是我们要启动的Activity的信息,注释二处将会解析Activity的信息,接着在注释三处执行请求,其实主要还是看注释三处,我们来看这个executeRequest方法:

    private int executeRequest(Request request) {
        if (TextUtils.isEmpty(request.reason)) {
            throw new IllegalArgumentException("Need to specify a reason.");
        }
        mLastStartReason = request.reason;
        mLastStartActivityTimeMs = System.currentTimeMillis();
        mLastStartActivityRecord = null;

        final IApplicationThread caller = request.caller; //1---------1
        Intent intent = request.intent;
        NeededUriGrants intentGrants = request.intentGrants;
        String resolvedType = request.resolvedType;
        ActivityInfo aInfo = request.activityInfo;
        ResolveInfo rInfo = request.resolveInfo;
       ..........

        int err = ActivityManager.START_SUCCESS;
        final Bundle verificationBundle =
                options != null ? options.popAppVerificationBundle() : null;

        WindowProcessController callerApp = null;
        if (caller != null) {   //2---------2
            callerApp = mService.getProcessController(caller);
            if (callerApp != null) {
                callingPid = callerApp.getPid();
                callingUid = callerApp.mInfo.uid;
            } else {
                Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid
                        + ") when starting: " + intent.toString());
                err = START_PERMISSION_DENIED;
            }
        }

     	.........

        final ActivityRecord r = new ActivityRecord.Builder(mService)//3---------3
                .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();

        mLastStartActivityRecord = r;

        if (r.appTimeTracker == null && sourceRecord != null) {
            r.appTimeTracker = sourceRecord.appTimeTracker;
        }

        WindowProcessController homeProcess = mService.mHomeProcess;
        boolean isHomeProcess = homeProcess != null
                && aInfo.applicationInfo.uid == homeProcess.mUid;
        if (!restrictedBgActivity && !isHomeProcess) {
            mService.resumeAppSwitches();
        }

        mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,//4----------4
                request.voiceInteractor, startFlags, true /* doResume */, checkedOptions,
                inTask, inTaskFragment, restrictedBgActivity, intentGrants);

        if (request.outActivity != null) {
            request.outActivity[0] = mLastStartActivityRecord;
        }

        return mLastStartActivityResult;
    }

这个方法是在是非常长,我们截取部分重要的代码,在看代码之前我们先看一眼它的注释:

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 startActivityUnchecked to startActivityInner.

翻译过来就是:执行活动启动请求并开始启动活动的旅程。这里从执行几个初步检查开始。正常的活动启动流程将通过startActivityUnchecked为startActivityInner。

接下来看代码,注释一处将会获得要启动Activity的进程,然后会在注释二处进行判断,所以说启动一个应用程序的Activity前是必须要创建出它的进程的。接着在注释三处,创建出了一个ActivityRecord的实例,前面说到过这个类是用来描述Activity的信息的,在这里就是描述的是要启动的Activity的信息。接着在注释四处就进入到了另一个方法中开始继续启动Activity。所以继续看startActivityUnchecked方法:

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;

        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);
            startedActivityRootTask = handleStartResult(r, options, result, newTransition,
                    remoteTransition);
            mService.continueWindowLayout();
        }
        postStartActivityProcessing(r, result, startedActivityRootTask);

        return result;
    }

看一眼注释先:

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.

翻译:启动活动时,大多数初步检查已经完成,并确认调用方拥有执行该活动所需的权限。这里还确保在启动不成功时删除启动活动。

这里它首先会调用startActivityInner方法启动Activity,最后在finally中用handleStartResult处理结果。我们先看startActivityInner方法:

    int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment, boolean restrictedBgActivity,
            NeededUriGrants intentGrants) {
        setInitialState(r, options, inTask, inTaskFragment, doResume, startFlags, sourceRecord,
                voiceSession, voiceInteractor, restrictedBgActivity);

        computeLaunchingTaskFlags();

        computeSourceRootTask();

      	.........

        final boolean isTaskSwitch = startedTask != prevTopTask && !startedTask.isEmbedded();
        mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,
                mOptions, sourceRecord);//1-------1
        if (mDoResume) {
            final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked(); //2-------2
            if (!mTargetRootTask.isTopActivityFocusable()
                    || (topTaskActivity != null && topTaskActivity.isTaskOverlay()
                    && mStartActivity != topTaskActivity)) {
                // If the activity is not focusable, we can't resume it, but still would like to
                // make sure it becomes visible as it starts (this will also trigger entry
                // animation). An example of this are PIP activities.
                // Also, we don't want to resume activities in a task that currently has an overlay
                // as the starting activity just needs to be in the visible paused state until the
                // over is removed.
                // Passing {@code null} as the start parameter ensures all activities are made
                // visible.
                mTargetRootTask.ensureActivitiesVisible(null /* starting */,
                        0 /* configChanges */, !PRESERVE_WINDOWS);
                // Go ahead and tell window manager to execute app transition for this activity
                // since the app transition will not be triggered through the resume channel.
                mTargetRootTask.mDisplayContent.executeAppTransition();
            } 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); //3------3
            }
        }
        mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask);

        // Update the recent tasks list immediately when the activity starts
        mSupervisor.mRecentTasks.add(startedTask);
        mSupervisor.handleNonResizableTaskIfNeeded(startedTask,
                mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask);

        // If Activity's launching into PiP, move the mStartActivity immediately to pinned mode.
        // Note that mStartActivity and source should be in the same Task at this point.
        if (mOptions != null && mOptions.isLaunchIntoPip()
                && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()) {
            mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity,
                    sourceRecord, "launch-into-pip");
        }

        return START_SUCCESS;
    }

老规矩,先来看一眼注释:启动一个活动,并确定该活动是应该添加到现有任务的顶部,还是应该向现有活动交付新的意图。还将活动任务操作到请求的或有效的根任务/显示上。注意:这个方法只能从startActivityUnchecked调用。

首先,这个方法会通过一系列的方法调用和计算,确定了目标任务(targetTask)和启动参数(launchParams)。然后,检查是否允许在指定任务上启动活动,并返回相应的启动结果。如果不允许启动,则会发送 RESULT_CANCELED 的结果给启动活动的结果接收者。

接着我们看注释一处,这里调用了Task类的startActivityLocked,虽然叫做startActivityLocked,但是这个方法并没有真正地启动Activity,它所做的是一些预处理和准备工作。注释二处调用topRunningActivityLocked() 获取位于 startedTask 任务堆栈顶部正在运行的活动(Activity)。

在接下来的if块中将会判断获取到的Activity,if (!mTargetRootTask.isTopActivityFocusable() || …这一行代码开始一个条件语句块,用于判断目标任务堆栈是否可见或是否需要执行特殊处理。条件包括:

  • 如果目标任务堆栈的顶部活动不可见或顶部活动是一个任务叠加层(Task Overlay)并且不是启动的活动本身,则执行以下操作:
    • 确保目标任务堆栈中的所有活动都可见。
    • 执行应用程序切换动画。
  • 否则,如果目标任务堆栈之前不可见并且现在可见,并且目标任务堆栈不是当前焦点所在的任务堆栈,则将目标任务堆栈移动到前台。

显然,我们要启动的Activity将会进入到第二个分支当中,接下来看注释三处的resumeFocusedTasksTopActivities方法:

 boolean resumeFocusedTasksTopActivities(
            Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,
            boolean deferPause) {
        if (!mTaskSupervisor.readyToResume()) {
            return false;
        }

        boolean result = false;
        if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea()
                || getTopDisplayFocusedRootTask() == targetRootTask)) {
            result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions,
                    deferPause); //1----------1
        }

       .........

        return result;
    }

这里我们就看注释一处的跳转,会跳转到resumeTopActivityUncheckedLocked方法,然后在这个方法中又会跳转到resumeTopActivityInnerLocked方法中,接下来又会跳转到ActivityTaskSupervisor的startSpecificActivity方法中,最后会跳转到ActivityTaskSupervisor的realStartActivityLocked方法中,我们来看其中的一段重要代码:

  final ClientTransaction clientTransaction = ClientTransaction.obtain(
                        proc.getThread(), r.token);

                final boolean isTransitionForward = r.isTransitionForward();
                final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                        System.identityHashCode(r), r.info,
                        // TODO: Have this take the merged configuration instead of separate global
                        // and override configs.
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
                        proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
                        results, newIntents, r.takeOptions(), isTransitionForward,
                        proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
                        r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken));

                // Set desired final state.
                final ActivityLifecycleItem lifecycleItem;
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(isTransitionForward);
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // Schedule transaction.
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);

实际上就是通过这段代码继续启动Activity的,这里新创建了一个客户端事务,用于远程调用的,点开mService.getLifecycleManager().scheduleTransaction(clientTransaction)方法:

    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        if (!(client instanceof Binder)) {
            // If client is not an instance of Binder - it's a remote call and at this point it is
            // safe to recycle the object. All objects used for local calls will be recycled after
            // the transaction is executed on client in ActivityThread.
            transaction.recycle();
        }
    }

我们看中间注释的翻译://如果client不是bind的实例,它是一个远程调用,此时回收对象是安全的。所有用于本地调用的对象将在//在ActivityThread的客户端上执行事务后被回收。所以说这里将会跳转到ActivityThread中运行,到这里我们就已经进入到ActivityThread中了。那为什么我们要说从ATMS到ApplicationThread呢?因为ApplicationThread是ActivityThread的内部类,实际上是实现了AIDL的类,也就是我们远程调用到的类:

private class ApplicationThread extends IApplicationThread.Stub {
        private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";

        public final void scheduleReceiver(Intent intent, ActivityInfo info,
                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                boolean sync, int sendingUser, int processState) {
            updateProcessState(processState, false);
            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                    sync, false, mAppThread.asBinder(), sendingUser);
            r.info = info;
            r.compatInfo = compatInfo;
            ............
}

所以我们总结下来就是ATMS通过远程操控应用程序在其进程中调用其ActivityThread类来创建Activity:
在这里插入图片描述

ActivityThread启动Activity

接下来是最后一部分,已经跳转到了ActivityThread中了。前面说到,在ATMS中已经通过远程调用发送了客户端事务了,接下来就需要在ActivityThread中处理这些事务了。在ActivityThread中是通过Handler来处理的,我们可以看这个Handler中的一些标志位:

case BIND_APPLICATION: return "BIND_APPLICATION";
                    case EXIT_APPLICATION: return "EXIT_APPLICATION";
                    case RECEIVER: return "RECEIVER";
                    case CREATE_SERVICE: return "CREATE_SERVICE";
                    case SERVICE_ARGS: return "SERVICE_ARGS";
                    case STOP_SERVICE: return "STOP_SERVICE";
                    case CONFIGURATION_CHANGED: return "CONFIGURATION_CHANGED";
                    case CLEAN_UP_CONTEXT: return "CLEAN_UP_CONTEXT";
                    case GC_WHEN_IDLE: return "GC_WHEN_IDLE";
                    case BIND_SERVICE: return "BIND_SERVICE";
                    case UNBIND_SERVICE: return "UNBIND_SERVICE";
                    case DUMP_SERVICE: return "DUMP_SERVICE";
                    case LOW_MEMORY: return "LOW_MEMORY";
                    case PROFILER_CONTROL: return "PROFILER_CONTROL";
                    case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
                    case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
                    case SUICIDE: return "SUICIDE";
                    case REMOVE_PROVIDER: return "REMOVE_PROVIDER";
                    case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST";
                    case SCHEDULE_CRASH: return "SCHEDULE_CRASH";
                    case DUMP_HEAP: return "DUMP_HEAP";
                    case DUMP_ACTIVITY: return "DUMP_ACTIVITY";
                    case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS";
                    case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
                    case DUMP_PROVIDER: return "DUMP_PROVIDER";
                    case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
                    case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS";
                    case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
                    case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
                    case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
                    case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
                    case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
                    case ATTACH_AGENT: return "ATTACH_AGENT";
                    case APPLICATION_INFO_CHANGED: return "APPLICATION_INFO_CHANGED";
                    case RUN_ISOLATED_ENTRY_POINT: return "RUN_ISOLATED_ENTRY_POINT";
                    case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
                    case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
                    case PURGE_RESOURCES: return "PURGE_RESOURCES";
                    case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS";
                    case UPDATE_UI_TRANSLATION_STATE: return "UPDATE_UI_TRANSLATION_STATE";
                    case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK:
                        return "SET_CONTENT_CAPTURE_OPTIONS_CALLBACK";
                    case DUMP_GFXINFO: return "DUMP GFXINFO";
                    case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART";
                    case FINISH_INSTRUMENTATION_WITHOUT_RESTART:
                        return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
                    case DUMP_RESOURCES: return "DUMP_RESOURCES";

每一个标志都代表一些事件的处理,而我们需要知道的是启动Activity的标志,在这里,启动Activity的标志是RELAUNCH_ACTIVITY,接下来会在Handler的handleMessage方法中进行处理:

 case RELAUNCH_ACTIVITY:
                    handleRelaunchActivityLocally((IBinder) msg.obj);
                    break;

接下来会继续跳转到handleRelaunchActivityInner方法中,这个方法然后继续会跳转,跳转到handleLaunchActivity方法,我们直接看最后的这个方法:

    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
		..........
        WindowManagerGlobal.initialize();

        // Hint the GraphicsEnvironment that an activity is launching on the process.
        GraphicsEnvironment.hintActivityLaunch();

        final Activity a = performLaunchActivity(r, customIntent);//1-----1

        if (a != null) {
            r.createdConfig = new Configuration(mConfigurationController.getConfiguration());
            reportSizeConfigurations(r);
            if (!r.activity.mFinished && pendingActions != null) {
                pendingActions.setOldState(r.state);
                pendingActions.setRestoreInstanceState(true);
                pendingActions.setCallOnPostCreate(true);
            }
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED,
                    null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        }

        return a;
    }

我们直接看注释一处就行,会调用到performLauncherActivity方法,我们着重来分析这个方法:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        ContextImpl appContext = createBaseContextForActivity(r);//1-----1
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);//2------2
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
                    appContext.getAttributionSource());
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        }
        ...........

                appContext.setOuterContext(activity);
                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.activityConfigCallback,
                        r.assistToken, r.shareableActivityToken);//3-------3

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                if (r.mActivityOptions != null) {
                    activity.mPendingOptions = r.mActivityOptions;
                    r.mActivityOptions = null;
                }
                activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
                activity.mCalled = false;
                // Assigning the activity to the record before calling onCreate() allows
                // ActivityThread#getActivity() lookup for the callbacks triggered from
                // ActivityLifecycleCallbacks#onActivityCreated() or
                // ActivityLifecycleCallback#onActivityPostCreated().
                r.activity = activity;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);//4---------4
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
              	..........
            }
            r.setState(ON_CREATE);//5----------5

        } 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;
    }

首先在注释一处的ContextImpl appContext = createBaseContextForActivity®,主要是创建出了上下文对象,这个我们应该很熟悉;
注释二处,通过ClassLoader和Instrumentation创建出了Activity的实例;
注释三处,调用到了创建出的activity的attach方法,activity.attach() 方法是用于将 Activity 附加到系统的 ActivityThread 中,并进行一系列初始化和配置操作。具体来说,attach() 方法接收多个参数,每个参数都用于设置 Activity 的不同属性和状态;
注释四处无论如何都会调用mInstrumentation.callActivityOnCreate方法,这个方法很简单,主要是调用到activity的performCreate方法,在这个方法中,Activity的onCreate方法被正式调用;那么到这里,Activity就算正式启动了;
注释五处调用setState方法更新了Activity的状态,更新到了ON_CREATE;

Resume的启动

那么最后要讲的是启动Resume的过程,这里的流程和之前Android8.0的源码相比有较大不同,这里我也是仅做猜测,大家酌情查看。让我们回到之前一开始的handleRelaunchActivityLocally方法:

public void handleRelaunchActivityLocally(IBinder token) {
        final ActivityClientRecord r = mActivities.get(token);
        if (r == null) {
            Log.w(TAG, "Activity to relaunch no longer exists");
            return;
        }

        final int prevState = r.getLifecycleState();

        if (prevState < ON_START || prevState > ON_STOP) {
            Log.w(TAG, "Activity state must be in [ON_START..ON_STOP] in order to be relaunched,"
                    + "current state is " + prevState);
            return;
        }

        ActivityClient.getInstance().activityLocalRelaunch(r.token);//1----------1
        // Initialize a relaunch request.
        final MergedConfiguration mergedConfiguration = new MergedConfiguration(
                r.createdConfig != null
                        ? r.createdConfig : mConfigurationController.getConfiguration(),
                r.overrideConfig);
        final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
                null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
                mergedConfiguration, r.mPreserveWindow);
        // Make sure to match the existing lifecycle state in the end of the transaction.
        final ActivityLifecycleItem lifecycleRequest =
                TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
        // Schedule the transaction.
        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
        transaction.addCallback(activityRelaunchItem);
        transaction.setLifecycleStateRequest(lifecycleRequest);
        executeTransaction(transaction);//2--------2
    }

我们知道在之前的注释一处的的activityLocalRelaunch方法完成了Activity的Launch并且更新了Activity的状态为ON_CREATE了,最后几行代码中又会根据LifeCycle状态来执行事务,主要看注释二处的executeTransaction方法,最后会调用到TransactionExecutor的execute方法,我们接下来就紧接着看execute方法:

 public void execute(ClientTransaction transaction) {
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");

        final IBinder token = transaction.getActivityToken();
        if (token != null) {
            final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
                    mTransactionHandler.getActivitiesToBeDestroyed();
            final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token);
            if (destroyItem != null) {
                if (transaction.getLifecycleStateRequest() == destroyItem) {
                    activitiesToBeDestroyed.remove(token);
                }
                if (mTransactionHandler.getActivityClient(token) == null) {
                    Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:\n"
                            + transactionToString(transaction, mTransactionHandler));
                    return;
                }
            }
        }

        if (DEBUG_RESOLVER) Slog.d(TAG, transactionToString(transaction, mTransactionHandler));

        executeCallbacks(transaction);

        executeLifecycleState(transaction);//1----------1
        mPendingActions.clear();
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
    }

接下来会进入到注释一处的executeLifecycleState方法:

private void executeLifecycleState(ClientTransaction transaction) {
        final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
		............

        // Cycle to the state right before the final requested state.
        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);//1---------1

        // Execute the final transition with proper parameters.
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
    }

接下来会调用到注释一处的cycleToPath方法,最后会调用到TransactionExecutor的performLifecycleSequence方法,这个方法就会根据Activity的状态来执行响应的回调:

switch (state) {
                case ON_CREATE:
                    mTransactionHandler.handleLaunchActivity(r, mPendingActions,
                            null /* customIntent */);
                    break;
                case ON_START:
                    mTransactionHandler.handleStartActivity(r, mPendingActions,
                            null /* activityOptions */);
                    break;
                case ON_RESUME:
                    mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
                            r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
                    break;
                case ON_PAUSE:
                    mTransactionHandler.handlePauseActivity(r, false /* finished */,
                            false /* userLeaving */, 0 /* configChanges */, mPendingActions,
                            "LIFECYCLER_PAUSE_ACTIVITY");
                    break;
                case ON_STOP:
                    mTransactionHandler.handleStopActivity(r, 0 /* configChanges */,
                            mPendingActions, false /* finalStateRequest */,
                            "LIFECYCLER_STOP_ACTIVITY");
                    break;
                case ON_DESTROY:
                    mTransactionHandler.handleDestroyActivity(r, false /* finishing */,
                            0 /* configChanges */, false /* getNonConfigInstance */,
                            "performLifecycleSequence. cycling to:" + path.get(size - 1));
                    break;
                case ON_RESTART:
                    mTransactionHandler.performRestartActivity(r, false /* start */);
                    break;
                default:
                    throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
            }

这样就会开启各自的生命周期的回调了,包括Resume,之后会调用对应的handle方法,比如对Resume来说就会进入handleResumeActivity方法中,然后执行对应的perform方法执行activity的回调:

public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
            boolean isForward, String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        // skip below steps for double-resume and r.mFinish = true case.
        if (!performResumeActivity(r, finalStateRequest, reason)) {
            return;
        }
        if (mActivitiesToBeDestroyed.containsKey(r.token)) {
            // Although the activity is resumed, it is going to be destroyed. So the following
            // UI operations are unnecessary and also prevents exception because its token may
            // be gone that window manager cannot recognize it. All necessary cleanup actions
            // performed below will be done while handling destruction.
            return;
        }
        ......
   }

总之就是通过对应的ActivityThread对象来管理Activity的生命周期以及其回调。

根Activity启动过程中涉及到的进程

根Activity做为一个应用程序要启动的第一个Activity,其涉及到的进程比一般的Activity要多,因为它还涉及到自身进程的创建。总的来说,它会涉及到四个进程,分别是Zygote进程,Launcher进程,AMS和ATMS进程和要启动的应用程序进程,我们可以用下面这幅图来总结一下:
在这里插入图片描述

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

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

相关文章

Linux - 第25节 - Linux高级IO(三)

目录 1.Reactor模式 1.1.Reactor模式的定义 1.2.Reactor模式的角色构成 1.3.Reactor模式的工作流程 2.epoll ET服务器&#xff08;Reactor模式&#xff09; 2.1.epoll ET服务器源代码 2.2.epoll ET服务器源代码讲解 2.2.1.设计思路 2.2.2.Connection结构 2.2.3.TcpSe…

VMware Fusion网络配置 - 设置Nat静态IP

准备把主力机器从ThinkPad T460P替换到MacMini上, MacOS版本: 10.15.7 当前最新MacOS版本是13 于是面临一个问题, 很多最新的工程软件不支持我这IntelCPU的MacOS陈旧版本, 于是我准备装一个虚拟机, 把工程软件都安装到虚拟机里, 宿主机访问其中的服务, 这样还能继续保持我这…

从定义到实际应用,详解项目管理的基本概念与核心内容

项目管理是项目的管理者&#xff0c;在有限的资源约束下&#xff0c;运用系统的观点、方法和理论&#xff0c;对项目涉及的全部工作进行有效地管理。项目管理的内容包括项目范围管理&#xff0c;是为了实现项目的目标&#xff0c;对项目的工作内容进行控制的管理过程。它包括范…

mysql5安装【含mysql安装包】

mysql5安装【含mysql安装包】 安装包等资源安装流程 安装包等资源 安装包下载地址【CSDN免费】&#xff1a;https://download.csdn.net/download/qq_47168235/87881814 如果上面的个下载不了&#xff0c;就通过百度网盘吧 百度网盘连接&#xff1a;https://pan.baidu.com/s/1G…

碳排放预测模型 | Python实现基于CNN卷积神经网络的碳排放预测模型(预测未来发展趋势)

文章目录 效果一览文章概述研究内容环境准备源码设计学习总结参考资料效果一览 文章概述 碳排放预测模型 | Python实现基于CNN卷积神经网络的碳排放预测模型(预测未来发展趋势) 研究内容 这是数据集的链接:https://github.com/owid/co2-data/blob/master/owid-co2-data.csv …

zabbix之ODBC监控方式

如有错误&#xff0c;敬请谅解&#xff01; 此文章仅为本人学习笔记&#xff0c;仅供参考&#xff0c;如有冒犯&#xff0c;请联系作者删除&#xff01;&#xff01; 15.1 概述 ODBC监控对应于Zabbix前端中的 数据库监视器 监控项类型。 ODBC是C语言编写的中间件API&#xf…

uniapp 使用组件 uni-list 实现聊天列表功能

如何使用 uniapp 的组件实现聊天列表的功能呢&#xff0c;翻阅了半天文档&#xff0c;终于找到一个实用的方法&#xff0c;下面是具体的步骤 1、首先需要下载对应的插件 去uniapp的官方文档进行下载&#xff08;uni-ui - DCloud 插件市场&#xff09;&#xff0c;这里直接下载…

机器学习 day17( Tensorflow和Numpy中的数据形式 )

Numpy和Tensorflow NumPy (Numerical Python) 是 Python 语言的一个扩展程序库&#xff0c;支持大量的维度数组与矩阵运算&#xff0c;此外也针对数组运算提供大量的数学函数库。TensorFlow是Google开源的第二代用于数字计算的软件库。它是基于数据流图的处理框架&#xff0c;…

使用Docker-Compose对Docker容器集群快速编排

目录 一、Docker-Compose1、Docker-Compose使用场景2、Docker-Compose简介3、Docker-Compose安装部署4、YAML 文件格式及编写注意事项5、Docker Compose配置常用字段6、Docker Compose 常用命令7、Docker Compose 文件结构8、docker Compose撰写nginx 镜像9、docker Compose撰写…

leetcode 链表+双指针问题小结

文章目录 141. 环形链表142. 环形链表 II19. 删除链表的倒数第 N 个结点160. 相交链表 141. 环形链表 设置两个速度不一样的链表&#xff0c;如果其中他们两个在经过一定的步数(进入环之后&#xff0c;在 n ∣ 环的大小 ∣ n \times |环的大小| n∣环的大小∣ 步后会重合)之后…

Redis的复制

配置 在Redis中使用复制功能非常容易 在从Redis服务器的redis.conf中写入slaveof masterip masterport即可&#xff0c;主Redis服务器不需要做任何配置在启动Redis服务器的时候&#xff0c;指定主服务器&#xff0c;redis-server --slaveof masterip masterport在客户端指定主…

038_SSS_Multi-Architecture Multi-Expert Diffusion Models

Multi-Architecture Multi-Expert Diffusion Models 1. Motivations & Arguments & Contributions 本文提出了一种在diffusion的不同步数采用不同的网络结构的方法提高生成质量和效率。 Diffusion模型需要大量的计算时间成本&#xff0c;改进方式主要有两个方面&…

教你用栈实现队列怎么写

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 队列&#xff08;Queue&#xff09;和栈&#xff08;Stack&#xff09;是两个基本的数据结构。队列是一种先进先出&#xff08;First-In-First-Out, FIFO&#xff09;…

【Spring】——Spring生命周期

前言 ❤️❤️❤️Spring专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Spring_冷兮雪的博客-CSDN博客 前面我们讲完了Spring中有关Bean的读和取&#xff0c;我们还没有好好去了解了解Bean对象&#xff0c;这篇 …

XML文件原理详解

文章目录 一、简介1. XML定义2. 测试3. HTML和XML的区别 二、XML基本语法1. 语法规则2. 元素的属性3. CDATA4. DTD文件5. XSD文件 三、Java解析XML1. 简介2. 解析XML文件 四、Xpath1. 简介2. Xpath的使用 一、简介 1. XML定义 XML&#xff08;可扩展标记语言&#xff09;是一…

高压放大器在介电材料中的应用有哪些

高压放大器是一种能够输出高电压的放大器&#xff0c;具有多种应用&#xff0c;其中之一就是在介电材料中的应用。介电材料是指能够保持一定电荷和电场状态的物质&#xff0c;其特点包括绝缘性、极化性和介电常数等。下面安泰电子将详细介绍高压放大器在介电材料中的应用。 介电…

《HarmonyOS开发 – OpenHarmony开发笔记(基于小型系统)》第4章 OpenHarmony应用开发实例

开发环境&#xff1a; 开发系统&#xff1a;Ubuntu 20.04 开发板&#xff1a;Pegasus物联网开发板 MCU&#xff1a;Hi3861 OpenHarmony版本&#xff1a;3.0.1-LTS 4.1新建工程及配置 1.新建工程及源码 新建目录 $ mkdir hello在applications/sample/myapp中新建src目录以及…

【零基础学机器学习 5】机器学习中的分类:什么是分类以及分类模型

&#x1f468;‍&#x1f4bb; 作者简介&#xff1a;程序员半夏 , 一名全栈程序员&#xff0c;擅长使用各种编程语言和框架&#xff0c;如JavaScript、React、Node.js、Java、Python、Django、MySQL等.专注于大前端与后端的硬核干货分享,同时是一个随缘更新的UP主. 你可以在各个…

华为OD机试真题 JavaScript 实现【字符串加密】【2023Q1 100分】,附详细解题思路

一、题目描述 有一种技巧可以对数据进行加密&#xff0c;它使用一个单词作为它的密匙。下面是它的工作原理&#xff1a;首先&#xff0c;选择一个单词作为密匙&#xff0c;如TRAILBLAZERS。如果单词中包含有重复的字母&#xff0c;只保留第1个&#xff0c;将所得结果作为新字母…

Spark大数据处理学习笔记(2.4)IDEA开发词频统计项目

该文章主要为完成实训任务&#xff0c;详细实现过程及结果见【http://t.csdn.cn/0qE1L】 文章目录 一、词频统计准备工作1.1 安装Scala2.12.151.2 启动集群的HDFS与Spark1.3 在HDFS上准备单词文件 二、本地模式运行Spark项目2.1 新建Maven项目2.2 添加项目相关依赖2.3 创建日志…