Android Activity 启动过程(下)

前言

本篇是 Activity 启动流程(下) 篇。主要内容为 App进程启动后与SystemServer ATMS bindApplication过程以及Activity 的创建和生命周期转换过程。

本文中代码基于 android-security-11.0.0_r66 分支.

Android Activity 启动过程(上) - 主要描述SystemServer 进程如何处理startActivity请求以及如何走到请求Zygote fork app 进程的流程

Android Activity 启动过程(中) - Zygote进程接收到 来自SystemServer进程请求创建App进程流程

4. App 进程启动后与ATMS的绑定流程

4.1 ActivityThread 入口启动过程

frameworks/base/core/java/android/app/ActivityThread.java

ini 复制代码
public static final String PROC_START_SEQ_IDENT = "seq=";
​
public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    // 省略
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {// 获取seq 值 
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    // 省略 Handler的创建过程
}

在应用的入口函数 ActivityThread::main方法中, 创建了 ActivityThread对象,并从参数中解析处 seq 值。seq 值对应着在 ProcessList::startProcessLocked方法中生成的 startSeq值。

frameworks/base/core/java/android/app/ActivityThread.java

java 复制代码
​
@UnsupportedAppUsage
private void attach(boolean system, long startSeq) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                UserHandle.myUserId());
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread, startSeq);// 代码1
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        BinderInternal.addGcWatcher(new Runnable() {//GcWatcher利用了利用了Java的finalize那一套:JVM垃圾回收器准备释放内存前,会先调用该对象finalize(如果有的话)。在finalize 这个GC节点去处理事务
            // 此处是在遇到GC 时,如果当前可用容量不足最大值1/4 ,就去尝试释放一些不可见Activity 以释放内存
            @Override public void run() {
                if (!mSomeActivitiesChanged) {
                    return;
                }
                Runtime runtime = Runtime.getRuntime();
                long dalvikMax = runtime.maxMemory();
                long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
                if (dalvikUsed > ((3*dalvikMax)/4)) {
                    if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
                            + " total=" + (runtime.totalMemory()/1024)
                            + " used=" + (dalvikUsed/1024));
                    mSomeActivitiesChanged = false;
                    try {
                        ActivityTaskManager.getService().releaseSomeActivities(mAppThread);
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
        });
    } 
    //省略
}

mAppThreadApplicationThread 对象,ApplicationThread 是个Binder对象,也是应用进程和AMS进行通信的桥梁。 代码1 处调用 ActivityManagerService::attachApplication方法,并将其 Binder对象 和 seq 传入,来告诉ATMS,将该应用加入管理。

4.2 ActivityTaskManagerService 处理attachApplication流程

frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

arduino 复制代码
 @Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
    if (thread == null) {
        throw new SecurityException("Invalid application interface");
    }
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid, callingUid, startSeq);
        Binder.restoreCallingIdentity(origId);
    }
}
​

attachApplicationBinder.clearCallingIdentity,Binder.restoreCallingIdentity 这两个方法讲下。

  1. clearCallingIdentity会在当前线程中重置到来的IPC标识,在将要处理调用时,调用者可能会需要调用本进程其他对象的接口。 这些对象都需要再调用上做权限检查(因此它们检查当前进程的权限,而不是其他发起IPC调用的进程)。
  2. restoreCallingIdentity会在当前线程恢复先前被clearCallingIdentity清理的IPC标记。 从进程A Binder线程 调用到 进程B的 Binder 线程时,B的Binder 会存储着进程A的pid,uid信息。而系统中的很多方法会进行权限> 检查,如果此时是调用者进程的pid,uid 就无法通过权限检查。

调用到 attachApplicationLocked 方法。

scss 复制代码
@GuardedBy("this")
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    Log.i(TAG,"chld attachApplicationLocked firset enter0" + startSeq);
​
    // Find the application record that is being attached...  either via
    // the pid if we are running in multiple processes, or just pull the
    // next app record if we are emulating process with anonymous threads.
    ProcessRecord app;
    long startTime = SystemClock.uptimeMillis();
    long bindApplicationTimeMillis;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            // 代码1
            app = mPidsSelfLocked.get(pid);
        }
        Log.i(TAG,"chld attachApplicationLocked firset enter1 processName:" + app.processName);
        // 省略
    } else {
        app = null;
    }
    //代码2  
    if (app == null && startSeq > 0) {
        final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
        if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq
                && mProcessList.handleProcessStartedLocked(pending, pid, pending
                        .isUsingWrapper(),
                        startSeq, true)) {
            Log.i(TAG,"chld attachApplicationLocked firset enter4 processName:" + app.processName);
            app = pending;
        }
    }
    
    //...省略
    //该段代码主要是执行 bindApplication操作.将 App 进程信息与AMS中实例进行绑定的
    try {
        //...省略
        final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
            if (app.isolatedEntryPoint != null) {
                // This is an isolated process which should just call an entry point instead of
                // being bound to an application.
                thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
            } else if (instr2 != null) {
                Log.i(TAG,"chld attachApplication bindApplication1 processName:" + processName);
                thread.bindApplication(processName, appInfo, providerList,
                        instr2.mClass,
                        profilerInfo, instr2.mArguments,
                        instr2.mWatcher,
                        instr2.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.compat, getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions,
                        app.mDisabledCompatChanges);
            } else {// 一般情况下,应用走这
                //代码3
                Log.i(TAG,"chld attachApplication bindApplication2 processName:" + processName);
                thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.compat, getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions,
                        app.mDisabledCompatChanges);
            }
            if (profilerInfo != null) {
                profilerInfo.closeFd();
                profilerInfo = null;
            }
​
    }catch(Exception e) {
        //...省略 APP进程绑定失败异常
    }
    
    // 省略
    boolean badApp = false;
    boolean didSomething = false;
​
    // See if the top visible activity is waiting to run in this process...
    if (normalMode) {//代码4 chld 如果是普通的启动模式,则需要检测其 应用启动中是否包为了Activity的启动
        try {
            didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
            badApp = true;
        }
    }
​
    // Find any services that should be running in this process...
    if (!badApp) {// 代码5 chldd
        try {
            didSomething |= mServices.attachApplicationLocked(app, processName);
            checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
            badApp = true;
        }
    }
​
    // Check if a next-broadcast receiver is in this process...
    if (!badApp && isPendingBroadcastProcessLocked(pid)) {//代码6
        try {
            didSomething |= sendPendingBroadcastsLocked(app);
            checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");
        } catch (Exception e) {
            // If the app died trying to launch the receiver we declare it 'bad'
            Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
            badApp = true;
        }
    }
​
    // Check whether the next backup agent is in this process...
    if (!badApp && backupTarget != null && backupTarget.app == app) {
        if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
                "New app is backup target, launching agent for " + app);
        notifyPackageUse(backupTarget.appInfo.packageName,
                            PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
        try {
            thread.scheduleCreateBackupAgent(backupTarget.appInfo,
                    compatibilityInfoForPackage(backupTarget.appInfo),
                    backupTarget.backupMode, backupTarget.userId);
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
            badApp = true;
        }
    }
}
​
​
​

代码1 attachApplicationLocked 中一般是先根据pid 去查询mPidsSelfLocked 数组的 ProcessRecord 对象信息。但是,这并不是一定的?为何这么说?请查看ProcessList::startProcessLocked方法。 在该方法中先调用了ProcessList::startProcess去与SystemServer通信,SystemServer fork 出了一个应用进程出来。并且通过socket通信将pid数据返回。层层反回到了PrcocessList::startProcessLocked方法,在获得ProcessList::startProcess 返回的 ProcessStartResult 对象中取出pid值,并调用ProcessList::handleProcessStart方法传入pid值,最终将以pid值为key存入ActivityManagerServicemPidsSelfLocked map对象中. 在这个流程中 SystemServer 通过socket 返回pid数据的同时,应用进程也在同步进行创建,因此最终是 PrcocessRecord 信息先被存入 mPidsSelfLocked,还是应用进程先调用到ActivityManagerService::attachApplicationLocked,是无法确定的。因此google 还使用了另一个更稳妥的值startSeq。该值是在应用进程创建之前就被确定的,且是作为参数携带到应用进程的ActivityThread::main函数的。 因此,当app == null 时还会去代码2 处通过 startSeq 去从 ActivityMamagerService.mProcessList.mPendingStarts map对象获取ProcessRecord.信息。

一般启动APP的原因有四种 : Activity启动,Service 启动,ContentProvider启动,静态 Receiver 启动。

代码3 处 调用thread.bindApplication方法去进行了Application的创建 以及ContentProvider的创建.该段代码的调用,此处限于篇幅,且不去过多干扰整体Activity的创建流程,请查看另一篇文章Android ContentProvider的onCreate 方法 和Application的onCreate 方法那个先调用?,在文章中搜索bindApplication方法.

代码4 处检测是否有顶层Activity需要在该进程启动 代码5 处检测是否有 Service 需要在该进程启动 代码6 处检测是否广播需要该进程处理

本篇着重讲代码4 处,Activity的启动。 mAtmInternalActivityTaskManagerInternal对象。其实现类为frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java::LocalService

less 复制代码
@HotPath(caller = HotPath.PROCESS_CHANGE)
@Override
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
    synchronized (mGlobalLockWithoutBoost) {
        if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);
        }
        try {
            return mRootWindowContainer.attachApplication(wpc);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }
}

调用到了 frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.javaattachApplication方法。

ini 复制代码
boolean attachApplication(WindowProcessController app) throws RemoteException {
    final String processName = app.mName;
    boolean didSomething = false;
    for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {//代码1
        final DisplayContent display = getChildAt(displayNdx);
        final ActivityStack stack = display.getFocusedStack();
        if (stack == null) {
            continue;
        }
​
        mTmpRemoteException = null;
        mTmpBoolean = false; // Set to true if an activity was started.
        final PooledFunction c = PooledLambda.obtainFunction(
                RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
                PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivity());// 代码2
        stack.forAllActivities(c);
        c.recycle();
        if (mTmpRemoteException != null) {
            throw mTmpRemoteException;
        }
        didSomething |= mTmpBoolean;
    }
    if (!didSomething) {
        ensureActivitiesVisible(null, 0, false /* preserve_windows */);
    }
    return didSomething;
}
​

代码1 通过For循环去所有的DisplayContent,取出可见栈。然后再对每个可见的ActivityStack进行操作。

DisplayContent 用于管理屏幕,一块屏幕对应一个 DisplayContent 对象,虽然手机只有一个显示屏,但是可以创建多个 DisplayContent 对象,如投屏时,可以创建一个虚拟的 DisplayContent。

代码2 使用了池化技术存储了函数对象 ,实际上相当于如下代码

arduino 复制代码
stack.forAllActivityies(activityRecord ->{
    startActivityForAttachedApplicationIfNeeded(activityRecord, app, stack.topRunningActivity());
})

4.3 ActivityStackSupervisor::realStartActivityLocked 启动Activity过程

frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

startActivityForAttachedApplicationIfNeeded

kotlin 复制代码
private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r,
        WindowProcessController app, ActivityRecord top) {
    if (r.finishing || !r.okToShowLocked() || !r.visibleIgnoringKeyguard || r.app != null
            || app.mUid != r.info.applicationInfo.uid || !app.mName.equals(r.processName)) {
        return false;
    }
​
    try {
        if (mStackSupervisor.realStartActivityLocked(r, app,
                top == r && r.getTask().canBeResumed(r) /*andResume*/,
                true /*checkConfig*/)) {// 代码1
            mTmpBoolean = true;
        }
    } catch (RemoteException e) {
        Slog.w(TAG, "Exception in new application when starting activity "
                + top.intent.getComponent().flattenToShortString(), e);
        mTmpRemoteException = e;
        return true;
    }
    return false;
}

代码1 处调用了 ActivityStackSupervisor::realStartActivityLocked方法去启动Activity. 前面 2.4 节开头部分已经分析过,应用启动Activity 时默认会走到

ActivityStackSupervisor::startSpecificActivity方法中,而在该方法中首先判断进程是否已经启动,已经启动则会直接调用realStartActivityLocked方法。 之前的代码如下:

frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java

java 复制代码
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
    // Is this activity's application already running?
    final WindowProcessController wpc =
            mService.getProcessController(r.processName, r.info.applicationInfo.uid);
​
    boolean knownToBeDead = false;
    if (wpc != null && wpc.hasThread()) {// 代码1
        try {
            realStartActivityLocked(r, wpc, andResume, checkConfig);//代码2
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception when starting activity "
                    + r.intent.getComponent().flattenToShortString(), e);
        }
​
        // chld 如果应用进程已死亡,携带该信息去重新启动应用
        // If a dead object exception was thrown -- fall through to
        // restart the application.
        knownToBeDead = true;
    }
​
    r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
​
    final boolean isTop = andResume && r.isTopRunningActivity();
    mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");//代码3
}

接下来,我们去看下realStartActivityLocked的真正实现吧.

frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java

scss 复制代码
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
                                boolean andResume, boolean checkConfig) throws RemoteException {
​
    if (!mRootWindowContainer.allPausedActivitiesComplete()) {// 代码1  检测所有Activity是否已处于暂停状态。要让一个Activity进入resume,需要保证之前的Activity处于Pause状态之后.
        //...
        return false;
    }
​
    final Task task = r.getTask();
    final ActivityStack stack = task.getStack();
​
    beginDeferResume();
​
    try {
        r.startFreezingScreenLocked(proc, 0);
​
        // schedule launch ticks to collect information about slow apps.
        //记录启动缓慢的app信息
        r.startLaunchTickingLocked();
​
        r.setProcess(proc);// 绑定ActivityRecord 对应的进程
​
        // Ensure activity is allowed to be resumed after process has set.
        //确保Activity能resume
        if (andResume && !r.canResumeByCompat()) {
            andResume = false;
        }
​
        // ...省略
​
        //记录Activity启动次数和最后启动事件.以及更新该应用进程最后一次启动Activity时间
        r.launchCount++;
        r.lastLaunchTime = SystemClock.uptimeMillis();
        proc.setLastActivityLaunchTime(r.lastLaunchTime);
​
        if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
​
        final LockTaskController lockTaskController = mService.getLockTaskController();
        if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
            || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
            || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED
                && lockTaskController.getLockTaskModeState()
                == LOCK_TASK_MODE_LOCKED)) {
            lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
        }
​
        try {
            if (!proc.hasThread()) {
                throw new RemoteException();
            }
            //... 省略
            if (r.isActivityTypeHome()) {//如果是Launcer Activity,进行处理
                // Home process is the root process of the task.
                updateHomeProcess(task.getBottomMostActivity().app);
            }
            //记录相关信息
            mService.getPackageManagerInternalLocked().notifyPackageUse(
                r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
            r.setSleeping(false);
            r.forceNewConfig = false;
            
            //检测Activity的启动,是否存在不兼容的ComplieSdk 或TargetSdk 或不支持的显示大小.具体点击进入这个函数看调用的方法
            mService.getAppWarningsLocked().onStartActivity(r);
            r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
​
            //... 省略 
            
            // Create activity launch transaction.
            //代码2 创建一个Activity启动的事务.Activity 生命周期状态由其控制转换,ClientTransaction 是从Android 8 引入的
            final ClientTransaction clientTransaction = ClientTransaction.obtain(
                proc.getThread(), r.appToken);
​
            final DisplayContent dc = r.getDisplay().mDisplayContent;
            // 添加一条Activity启动的事务
            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.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                                                                    r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
                                                                    dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                                                                    r.assistToken, r.createFixedRotationAdjustmentsIfNeeded()));
​
            // Set desired final state.
            //设置期望到达的状态
            final ActivityLifecycleItem lifecycleItem;
            if (andResume) {
                lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
            } else {
                lifecycleItem = PauseActivityItem.obtain();
            }
            
            clientTransaction.setLifecycleStateRequest(lifecycleItem);
​
            // Schedule transaction.
            // 代码3 调用scheduleTransaction,通过App进程代理对象去控制App端Activity生命状态转换.
            mService.getLifecycleManager().scheduleTransaction(clientTransaction);
​
            if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
                && mService.mHasHeavyWeightFeature) {
                // This may be a heavy-weight process! Note that the package manager will ensure
                // that only activity can run in the main process of the .apk, which is the only
                // thing that will be considered heavy-weight.
                if (proc.mName.equals(proc.mInfo.packageName)) {
                    if (mService.mHeavyWeightProcess != null
                        && mService.mHeavyWeightProcess != proc) {
                        Slog.w(TAG, "Starting new heavy weight process " + proc
                               + " when already running "
                               + mService.mHeavyWeightProcess);
                    }
                    mService.setHeavyWeightProcess(r);
                }
            }
​
        } catch (RemoteException e) {
            if (r.launchFailed) {
                // This is the second time we failed -- finish activity and give up.
                Slog.e(TAG, "Second failure launching "
                       + r.intent.getComponent().flattenToShortString() + ", giving up", e);
                proc.appDied("2nd-crash");
                r.finishIfPossible("2nd-crash", false /* oomAdj */);
                return false;
            }
​
            // This is the first time we failed -- restart process and
            // retry.
            //如果第一次启动失败,尝试重新启动一次
            r.launchFailed = true;
            proc.removeActivity(r);
            throw e;
        }
    } finally {
        endDeferResume();
    }
​
    r.launchFailed = false;
​
    // TODO(lifecycler): Resume or pause requests are done as part of launch transaction,
    // so updating the state should be done accordingly.
    if (andResume && readyToResume()) {
        // As part of the process of launching, ActivityThread also performs
        // a resume.
        //将ActivityRecord 状态标记为 RESUME
        stack.minimalResumeActivityLocked(r);
    } else {
        // This activity is not starting in the resumed state... which should look like we asked
        // it to pause+stop (but remain visible), and it has done so and reported back the
        // current icicle and other state.
        if (DEBUG_STATES) Slog.v(TAG_STATES,
                                 "Moving to PAUSED: " + r + " (starting in paused state)");
        r.setState(PAUSED, "realStartActivityLocked");
        mRootWindowContainer.executeAppTransitionForAllDisplay();
    }
    // Perform OOM scoring after the activity state is set, so the process can be updated with
    // the latest state.
    proc.onStartActivity(mService.mTopProcessState, r.info);
​
    // Launch the new version setup screen if needed.  We do this -after-
    // launching the initial activity (that is, home), so that it can have
    // a chance to initialize itself while in the background, making the
    // switch back to it faster and look better.
    if (mRootWindowContainer.isTopDisplayFocusedStack(stack)) {
        mService.getActivityStartController().startSetupActivity();
    }
​
    // Update any services we are bound to that might care about whether
    // their client may have activities.
    if (r.app != null) {
        r.app.updateServiceConnectionActivities();
    }
​
    return true;
}
  • 代码1 检测所有Activity是否已处于暂停状态。要让一个Activity进入resume,需要保证之前的Activity处于Pause状态之后.让当前其他Activity处于暂停状态的方法. 2.3章节中 ActivityStack::resumeTopActivityInnerLocked方法中调用了startPausingLocked方法去停止当前处于resume状态 Activity
  • 代码2,代码3: 代码2 处创建一个Activity启动的clientTransaction事务.Activity 生命周期状态由其控制转换.(ClientTransaction 是从Android 8 引入的). 接着在代码3处,设置Activity期望转换为的生命周期状态,并调用scheduleTransaction方法去进行传递给App 进程进行处理. 此处一般是从Launcher

    mService.getLifecycleManager().scheduleTransaction(clientTransaction); 代码实现为

    frameworks/base/services/core/java/com/android/server/wm/ClientLifecycleManager.java

    java 复制代码
    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        if (!(client instanceof Binder)) {
            transaction.recycle();
        }
    }

接下来先看下ClientTransaction 类的结构吧.

csharp 复制代码
public class ClientTransaction implements Parcelable, ObjectPoolItem {
​
    /** A list of individual callbacks to a client. */
    @UnsupportedAppUsage
    private List<ClientTransactionItem> mActivityCallbacks;
    /**
     * Final lifecycle state in which the client activity should be after the transaction is
     * executed.
     */
    private ActivityLifecycleItem mLifecycleStateRequest;
​
    /** Target client. */
    private IApplicationThread mClient;
​
    /** Target client activity. Might be null if the entire transaction is targeting an app. */
    private IBinder mActivityToken;
​
    public IApplicationThread getClient() {
        return mClient;
    }
    public void addCallback(ClientTransactionItem activityCallback) {
        if (mActivityCallbacks == null) {
            mActivityCallbacks = new ArrayList<>();
        }
        mActivityCallbacks.add(activityCallback);
    }
​
    public List<ClientTransactionItem> getCallbacks() {
        return mActivityCallbacks;
    }
    public IBinder getActivityToken() {
        return mActivityToken;
    }
    public ActivityLifecycleItem getLifecycleStateRequest() {
        return mLifecycleStateRequest;
    }
​
​
    public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) {
        mLifecycleStateRequest = stateRequest;
    }
​
    public void preExecute(android.app.ClientTransactionHandler clientTransactionHandler) {
        if (mActivityCallbacks != null) {
            final int size = mActivityCallbacks.size();
            for (int i = 0; i < size; ++i) {
                mActivityCallbacks.get(i).preExecute(clientTransactionHandler, mActivityToken);
            }
        }
        if (mLifecycleStateRequest != null) {
            mLifecycleStateRequest.preExecute(clientTransactionHandler, mActivityToken);
        }
    }
​
    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }
    // 省略构造
}

如上代码. ClientTransaction是一个 Parcelable 对象,可在进行通信中进行传输. 其mClient 持有了App进行的代理对象.schedule方法通过mCliecnt代理对象的scheduleTransaction方法将ClientTranscation 信息传递给了App端.

接下去App 进程AtivityThread 类中去看下scheduleTransaction的实现吧.

5. App 进程内部 Activity的启动过程

frameworks/base/core/java/android/app/ActivityThread.java

scala 复制代码
private class ApplicationThread extends IApplicationThread.Stub {
    @Override
    public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        ActivityThread.this.scheduleTransaction(transaction);
    }
}

ActivityThread 继承自ClientTransactionHandler

frameworks/base/core/java/android/app/ClientTransactionHandler.java

scss 复制代码
void scheduleTransaction(ClientTransaction transaction) {
    transaction.preExecute(this);
    sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}

frameworks/base/core/java/android/app/servertransaction/ClientTransaction.java

scss 复制代码
public void preExecute(android.app.ClientTransactionHandler clientTransactionHandler) {
    if (mActivityCallbacks != null) {
        final int size = mActivityCallbacks.size();
        for (int i = 0; i < size; ++i) {
            mActivityCallbacks.get(i).preExecute(clientTransactionHandler, mActivityToken);
        }
    }
    if (mLifecycleStateRequest != null) {
        mLifecycleStateRequest.preExecute(clientTransactionHandler, mActivityToken);
    }
}

遍历了mActivityCallbacks 列表调用 preExecute 和 mLifecycleStateRequest 的preExecute

frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java

typescript 复制代码
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
    client.countLaunchingActivities(1);
    client.updateProcessState(mProcState, false);
    client.updatePendingConfiguration(mCurConfig);
}

frameworks/base/core/java/android/app/servertransaction/ResumeActivityItem.java

typescript 复制代码
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
    if (mUpdateProcState) {
        client.updateProcessState(mProcState, false);
    }
}

可以看到主要是更新了当前正在启动的Activity 的数目、当前进程状态,当前Configureation信息.接下来主要是看下EXECUTE_TRANSACTION 消息在ActivityThread的处理

frameworks/base/core/java/android/app/ActivityThread.java

java 复制代码
private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
public void handleMessage(Message msg) {
    switch (msg.what) {
        case EXECUTE_TRANSACTION:
            final ClientTransaction transaction = (ClientTransaction) msg.obj;
            mTransactionExecutor.execute(transaction);
            if (isSystem()) {
                // Client transactions inside system process are recycled on the client side
                // instead of ClientLifecycleManager to avoid being cleared before this
                // message is handled.
                transaction.recycle();
            }
            // TODO(lifecycler): Recycle locally scheduled transactions.
            break;
    }
}

如上调用了TransactionExecutor::execute方法去执行.

frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java

java 复制代码
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) {//代码1 处理需要销毁的Activity
            if (transaction.getLifecycleStateRequest() == destroyItem) {
                // It is going to execute the transaction that will destroy activity with the
                // token, so the corresponding to-be-destroyed record can be removed.
                activitiesToBeDestroyed.remove(token);
            }
            if (mTransactionHandler.getActivityClient(token) == null) {
                // The activity has not been created but has been requested to destroy, so all
                // transactions for the token are just like being cancelled.
                Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:\n"
                       + transactionToString(transaction, mTransactionHandler));
                return;
            }
        }
    }
​
    if (DEBUG_RESOLVER) Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
    // 代码2
    executeCallbacks(transaction);
    //代码3
    executeLifecycleState(transaction);
    mPendingActions.clear();
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}

代码1 处主要是处理要销毁的Activity.不用管。 重点是代码2和代码3处,执行各个ClientTransactionItem的回调消息.

frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java

java 复制代码
/** Cycle through all states requested by callbacks and execute them at proper times. */
@VisibleForTesting
public void executeCallbacks(ClientTransaction transaction) {
    final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
    if (callbacks == null || callbacks.isEmpty()) {
        // No callbacks to execute, return early.
        return;
    }
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");
​
    final IBinder token = transaction.getActivityToken();
    ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
​
    // In case when post-execution state of the last callback matches the final state requested
    // for the activity in this transaction, we won't do the last transition here and do it when
    // moving to final state instead (because it may contain additional parameters from server).
    // 获取Activity要到达的最终状态
    final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();// 
    final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()
        : UNDEFINED;
    // Index of the last callback that requests some post-execution state.
    final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);
​
    final int size = callbacks.size();
    for (int i = 0; i < size; ++i) {
        final ClientTransactionItem item = callbacks.get(i);
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
        final int postExecutionState = item.getPostExecutionState();
        final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                                                                                 item.getPostExecutionState());
        //生命周期的转换,对于Activity创建阶段不会走到.LaunchActivityItem.getPostExecutionState()值为 UNDEFINED
        if (closestPreExecutionState != UNDEFINED) {
            cycleToPath(r, closestPreExecutionState, transaction);
        }
        //代码1
        item.execute(mTransactionHandler, token, mPendingActions);
        //代码2
        item.postExecute(mTransactionHandler, token, mPendingActions);
        if (r == null) {
            // Launch activity request will create an activity record.
            r = mTransactionHandler.getActivityClient(token);
        }
        // Activity的创建阶段不会走到该处代码
        if (postExecutionState != UNDEFINED && r != null) {
            // Skip the very last transition and perform it by explicit state request instead.
            final boolean shouldExcludeLastTransition =
                i == lastCallbackRequestingState && finalState == postExecutionState;
            cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
        }
    }
}

代码1,代码2 处执行了LaunchActivityItem::executeLaunchActivityItem::postExecute方法

typescript 复制代码
@Override
public void execute(ClientTransactionHandler client, IBinder token,
                    PendingTransactionActions pendingActions) {
    Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
    ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                                                      mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                                                      mPendingResults, mPendingNewIntents, mIsForward,
                                                      mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
    client.handleLaunchActivity(r, pendingActions, null /* customIntent */);//代码1
    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
​
@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
                        PendingTransactionActions pendingActions) {
    client.countLaunchingActivities(-1);//执行完了启动Activity的操作,将当前正在启动Activity数目减1
}

如上,重点还是execute方法,该方法中创建了ActivityClientRecord 对象,该对象记录了Activity的信息和 生命周期状态.接下来调用ActivityThread::handleLaunchActivity方法.

(在Android 8.0之前,没有ClientTranscation类。是在 ActivityStackSupervisor 中调用app.thread.scheduleLaunchActivity -> ActivityThread::scheduleLaunchActivity->ActivityThread::handleLaunchActivity . )

frameworks/base/core/java/android/app/ActivityThread.java

scss 复制代码
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
                                     PendingTransactionActions pendingActions, Intent customIntent) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    //        Log.d(TAG,"chld handleLaunchActivity",new Exception());
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;
​
    if (r.profilerInfo != null) {
        mProfiler.setProfiler(r.profilerInfo);
        mProfiler.startProfiling();
    }
​
    // Make sure we are running with the most recent config.
    handleConfigurationChanged(null,null);
​
    if (localLOGV) Slog.v(
        TAG, "Handling launch of " + r);
​
    // Initialize before creating the activity
    if (!ThreadedRenderer.sRendererDisabled
        && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
        HardwareRenderer.preload();
    }
    WindowManagerGlobal.initialize();// 初始化获取WindowManagerService的代理对象(已初始化了不会重复执行)
​
    // Hint the GraphicsEnvironment that an activity is launching on the process.
    GraphicsEnvironment.hintActivityLaunch();
    //代码1
    final Activity a = performLaunchActivity(r, customIntent);
​
    if (a != null) {
    
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        if (!r.activity.mFinished && pendingActions != null) {
            pendingActions.setOldState(r.state);
            pendingActions.setRestoreInstanceState(true);
            pendingActions.setCallOnPostCreate(true);//此处设置了 onPostCreate为true.
        }
    } else {
        // If there was an error, for any reason, tell the activity manager to stop us.
        try {
            // Activity 创建存在异常,通知 ATM移除该Activity
            ActivityTaskManager.getService()
                .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                                Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
​
    return a;
}
  • 代码1 调用performLaunchActivity 方法执行了 Activity的创建以及执行到Activity::onCreate 状态.

(在Android 8.0系统之前,handleLaunchActivity方法是直接到 onResume 方法的)

如下是Android 7.1 代码

scss 复制代码
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    //...省略
    WindowManagerGlobal.initialize();
​
    Activity a = performLaunchActivity(r, customIntent);//创建了Activity 且调用了 onCreate,onStart
​
    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;
        //调用到了onResume
        handleResumeActivity(r.token, false, r.isForward,
                             !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
       //...省略
    } else {
        //...省略
    }
}

frameworks/base/core/java/android/app/ActivityThread.java

scss 复制代码
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);
    }
    // 确定Activity ComponentName 信息
    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);
    }
    //代码1 创建 Activity 的上下文对象
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        // 代码2 通过反射调用创建Activity 实例
        activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);
        //...省略
    } catch (Exception e) {
        //...Activity创建失败移除处理
    }
    try {
        //...省略
        if (activity != null) {
            //代码3 attach 方法中实现了Activity的初始化和PhoneWindow创建
            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);
​
            //...省略一些状态的更新
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                // 代码4
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            if (!activity.mCalled) {
                throw new SuperNotCalledException(
                    "Activity " + r.intent.getComponent().toShortString() +
                    " did not call through to super.onCreate()");
            }
            r.activity = activity;
            mLastReportedWindowingMode.put(activity.getActivityToken(),
                                           config.windowConfiguration.getWindowingMode());
        }
        //代码5 设置当前的状态
        r.setState(ON_CREATE);
​
        // updatePendingActivityConfiguration() reads from mActivities to update
        // ActivityClientRecord which runs in a different thread. Protect modifications to
        // mActivities to avoid race.
        synchronized (mResourcesManager) {
            mActivities.put(r.token, r);
        }
​
    } 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;
}

performLaunchActivity 方法主要完成了如下工作:

(1) Actvity 上下文的创建和 Activity 实例对象创建

(2) 调用了Activity::attach 方法

(3) mInstrumentation.callActivityOnCreate 方法执行,最终调用到Activity::onCrate

代码5处,设置下了当前Activity的状态,ON_CRAETE

接下来先看下Acitivty::attach方法

frameworks/base/core/java/android/app/Activity.java

ini 复制代码
@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
                  Instrumentation instr, IBinder token, int ident,
                  Application application, Intent intent, ActivityInfo info,
                  CharSequence title, Activity parent, String id,
                  NonConfigurationInstances lastNonConfigurationInstances,
                  Configuration config, String referrer, IVoiceInteractor voiceInteractor,
                  Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    //调用了Application的attachBaseContext方法
    attachBaseContext(context);
    
    //初始化Fragment控制器 FragmentController
    mFragments.attachHost(null /*parent*/);
    
    //创建了 PhoneWindow 对象,用于显示
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(mWindowControllerCallback);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    mUiThread = Thread.currentThread();
​
    mMainThread = aThread;
    mInstrumentation = instr;
    mToken = token;
    mAssistToken = assistToken;
    mIdent = ident;
    mApplication = application;
    mIntent = intent;
    mReferrer = referrer;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    if (voiceInteractor != null) {
        if (lastNonConfigurationInstances != null) {
            mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
        } else {
            mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                                                   Looper.myLooper());
        }
    }
    //绑定WindowManagerService本地代理对象 和 ActivityRecord
    mWindow.setWindowManager(
        (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
        mToken, mComponent.flattenToString(),
        (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;
​
    mWindow.setColorMode(info.colorMode);
    mWindow.setPreferMinimalPostProcessing(
        (info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);
​
    setAutofillOptions(application.getAutofillOptions());
    setContentCaptureOptions(application.getContentCaptureOptions());
}

该方法主要对Activity 进行了一个初始化的作用.

接下来再看下mInstrumentation.callActivityOnCreate方法.

frameworks/base/core/java/android/app/Instrumentation.java

scss 复制代码
public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
}

执行到了 Activity::preformCrate 方法

frameworks/base/core/java/android/app/Activity.java

scss 复制代码
@UnsupportedAppUsage
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
    dispatchActivityPreCreated(icicle);
    mCanEnterPictureInPicture = true;
    // initialize mIsInMultiWindowMode and mIsInPictureInPictureMode before onCreate
    final int windowingMode = getResources().getConfiguration().windowConfiguration
        .getWindowingMode();
    mIsInMultiWindowMode = inMultiWindowMode(windowingMode);
    mIsInPictureInPictureMode = windowingMode == WINDOWING_MODE_PINNED;
    restoreHasCurrentPermissionRequest(icicle);
    //调用了onCrate方法
    if (persistentState != null) {
        onCreate(icicle, persistentState);
    } else {
        onCreate(icicle);
    }
    EventLogTags.writeWmOnCreateCalled(mIdent, getComponentName().getClassName(),
                                       "performCreate");
    mActivityTransitionState.readState(icicle);
​
    mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
        com.android.internal.R.styleable.Window_windowNoDisplay, false);
    mFragments.dispatchActivityCreated();
    mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
    dispatchActivityPostCreated(icicle);
}

可以看到如上代码调用了onCreate方法.此时Activity处于ON_CREATE状态中了.

回到TransactionExecutor::execute方法

java 复制代码
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) {//代码1 处理需要销毁的Activity
            if (transaction.getLifecycleStateRequest() == destroyItem) {
                // It is going to execute the transaction that will destroy activity with the
                // token, so the corresponding to-be-destroyed record can be removed.
                activitiesToBeDestroyed.remove(token);
            }
            if (mTransactionHandler.getActivityClient(token) == null) {
                // The activity has not been created but has been requested to destroy, so all
                // transactions for the token are just like being cancelled.
                Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:\n"
                       + transactionToString(transaction, mTransactionHandler));
                return;
            }
        }
    }
​
    if (DEBUG_RESOLVER) Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
    // 代码2
    executeCallbacks(transaction);
    //代码3
    executeLifecycleState(transaction);
    mPendingActions.clear();
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}

执行完executeCallbacks方法后,Activity 已经到了onCreate 状态.ActivityClientRecord::mLifecycleState 值已被赋值为ON_CREATE.此处补充下Acitcvity的所有状态值

/home/starrynight/aosp/android_master/aosp/frameworks/base/core/java/android/app/servertransaction/ActivityLifecycleItem.java

java 复制代码
@Retention(RetentionPolicy.SOURCE)
public @interface LifecycleState{}
public static final int UNDEFINED = -1;
public static final int PRE_ON_CREATE = 0;
public static final int ON_CREATE = 1;
public static final int ON_START = 2;
public static final int ON_RESUME = 3;
public static final int ON_PAUSE = 4;
public static final int ON_STOP = 5;
public static final int ON_DESTROY = 6;
public static final int ON_RESTART = 7;

frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java

java 复制代码
private void executeLifecycleState(ClientTransaction transaction) {
    final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
    if (lifecycleItem == null) {
        // No lifecycle request, return early.
        return;
    }
​
    final IBinder token = transaction.getActivityToken();
    final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);//此处获取的ActivityClientRecord 对象是在executeCallbacks 方法中创建的
    if (DEBUG_RESOLVER) {
        Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
               + lifecycleItem + " for activity: "
               + getShortActivityName(token, mTransactionHandler));
    }
​
    if (r == null) {
        // Ignore requests for non-existent client records for now.
        return;
    }
​
    // Cycle to the state right before the final requested state.
    cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);
​
    // Execute the final transition with proper parameters.
    lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
    lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}

当前Activity处于的生命周期是ON_CREATE状态,现在要走到的是ON_RESUME状态.来看下cycleToPath方法

frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java

java 复制代码
@VisibleForTesting
public void cycleToPath(ActivityClientRecord r, int finish, ClientTransaction transaction) {
    cycleToPath(r, finish, false /* excludeLastState */, transaction);
}
​
/**
     * Transition the client between states with an option not to perform the last hop in the
     * sequence. This is used when resolving lifecycle state request, when the last transition must
     * be performed with some specific parameters.
     */
private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState,
                         ClientTransaction transaction) {
    final int start = r.getLifecycleState();
    if (DEBUG_RESOLVER) {
        Slog.d(TAG, tId(transaction) + "Cycle activity: "
               + getShortActivityName(r.token, mTransactionHandler)
               + " from: " + getStateName(start) + " to: " + getStateName(finish)
               + " excludeLastState: " + excludeLastState);
    }
    //代码1
    final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
    //代码2
    performLifecycleSequence(r, path, transaction);
}
  • 代码1 获取从当前状态到目标状态的执行路径
  • 代码2 执行代码1处获得的路径.

先来看下getLifecyclePath方法.

frameworks/base/core/java/android/app/servertransaction/TransactionExecutorHelper.java

scss 复制代码
public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) {
    if (start == UNDEFINED || finish == UNDEFINED) {
        throw new IllegalArgumentException("Can't resolve lifecycle path for undefined state");
    }
    if (start == ON_RESTART || finish == ON_RESTART) {
        throw new IllegalArgumentException(
            "Can't start or finish in intermittent RESTART state");
    }
    if (finish == PRE_ON_CREATE && start != finish) {
        throw new IllegalArgumentException("Can only start in pre-onCreate state");
    }
​
    mLifecycleSequence.clear();
    if (finish >= start) {
        if (start == ON_START && finish == ON_STOP) {
            // A case when we from start to stop state soon, we don't need to go
            // through the resumed, paused state.
            mLifecycleSequence.add(ON_STOP);
        } else {
            // just go there
            for (int i = start + 1; i <= finish; i++) {
                mLifecycleSequence.add(i);//代码1
            }
        }
    } else { // finish < start, can't just cycle down
        if (start == ON_PAUSE && finish == ON_RESUME) {
            // Special case when we can just directly go to resumed state.
            mLifecycleSequence.add(ON_RESUME);
        } else if (start <= ON_STOP && finish >= ON_START) {
            // Restart and go to required state.
​
            // Go to stopped state first.
            for (int i = start + 1; i <= ON_STOP; i++) {
                mLifecycleSequence.add(i);
            }
            // Restart
            mLifecycleSequence.add(ON_RESTART);
            // Go to required state
            for (int i = ON_START; i <= finish; i++) {
                mLifecycleSequence.add(i);
            }
        } else {
            // Relaunch and go to required state
​
            // Go to destroyed state first.
            for (int i = start + 1; i <= ON_DESTROY; i++) {
                mLifecycleSequence.add(i);
            }
            // Go to required state
            for (int i = ON_CREATE; i <= finish; i++) {
                mLifecycleSequence.add(i);
            }
        }
    }
​
    // Remove last transition in case we want to perform it with some specific params.
    //代码2
    if (excludeLastState && mLifecycleSequence.size() != 0) {
        mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
    }
​
    return mLifecycleSequence;
}

当前状态是ON_CREATE,目前状态是ON_RESUME.下述内容是走到代码1处. mLifecycleSequence内容为[ON_START,ON_RESUME]. 又因为 excludeLastState 值为true,走到代码2,移除了ON_RESUME.所以最终返回的值为[ON_START]

来看下performLifecycleSequence方法是如何执行的吧

frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java

arduino 复制代码
private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
                                      ClientTransaction transaction) {
    final int size = path.size();
    for (int i = 0, state; i < size; i++) {
        state = path.get(i);
        if (DEBUG_RESOLVER) {
            Slog.d(TAG, tId(transaction) + "Transitioning activity: "
                   + getShortActivityName(r.token, mTransactionHandler)
                   + " to state: " + getStateName(state));
        }
        switch (state) {
            case ON_CREATE:
                mTransactionHandler.handleLaunchActivity(r, mPendingActions,
                                                         null /* customIntent */);
                break;
            case ON_START://代码1
                mTransactionHandler.handleStartActivity(r.token, mPendingActions);
                break;
            case ON_RESUME:
                mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
                                                         r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
                break;
            case ON_PAUSE:
                mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
                                                        false /* userLeaving */, 0 /* configChanges */, mPendingActions,
                                                        "LIFECYCLER_PAUSE_ACTIVITY");
                break;
            case ON_STOP:
                mTransactionHandler.handleStopActivity(r.token, 0 /* configChanges */,
                                                       mPendingActions, false /* finalStateRequest */,
                                                       "LIFECYCLER_STOP_ACTIVITY");
                break;
            case ON_DESTROY:
                mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
                                                          0 /* configChanges */, false /* getNonConfigInstance */,
                                                          "performLifecycleSequence. cycling to:" + path.get(size - 1));
                break;
            case ON_RESTART:
                mTransactionHandler.performRestartActivity(r.token, false /* start */);
                break;
            default:
                throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
        }
    }
}

走到代码1处,执行了mTransactionHandler.handleStartActivity

frameworks/base/core/java/android/app/ActivityThread.java

scss 复制代码
@Override
public void handleStartActivity(IBinder token, PendingTransactionActions pendingActions) {
   //...省略
​
    // 代码1
    activity.performStart("handleStartActivity");
    r.setState(ON_START);//设置当前STATUS
​
    if (pendingActions == null) {
        // No more work to do.
        return;
    }
    // Restore instance state
    if (pendingActions.shouldRestoreInstanceState()) {
        if (r.isPersistable()) {
            if (r.state != null || r.persistentState != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                                                    r.persistentState);
            }
        } else if (r.state != null) {
            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
        }
    }
​
    // Call postOnCreate()
    // 该处只有在Activity创建后.从onCreate -> onStart 周期时会调用.
    // 在 ActivityThread::handleLaunchActivity方法中,调用了`pendingActions.setCallOnPostCreate(true);`
    if (pendingActions.shouldCallOnPostCreate()) {
        activity.mCalled = false;
        if (r.isPersistable()) {
            mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                                      r.persistentState);
        } else {
            mInstrumentation.callActivityOnPostCreate(activity, r.state);
        }
        if (!activity.mCalled) {
            throw new SuperNotCalledException(
                "Activity " + r.intent.getComponent().toShortString()
                + " did not call through to super.onPostCreate()");
        }
    }
​
    updateVisibility(r, true /* show */);//更新可见性,此时Activity理论上应该可见了,但是此时mDecorView还未添加到Window,是没有影响的
    mSomeActivitiesChanged = true;
}

activity.performStart 方法最终也是调用到了 Activity::OnStart方法。调用路径Activity::performStart -> Instrumentation::callActivityOnStart -> Activity::OnStart. Activity的生命周期调用后处于ON_START 状态.

回到TransactionExecutor::executeLifecycleState方法,接下来是执行ResumeActivityItemexcutepostExecute方法

frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java

ini 复制代码
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);

frameworks/base/core/java/android/app/servertransaction/ResumeActivityItem.java

typescript 复制代码
@Override
public void execute(ClientTransactionHandler client, IBinder token,
                    PendingTransactionActions pendingActions) {
    Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
    client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
                                "RESUME_ACTIVITY");
    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
​
@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
                        PendingTransactionActions pendingActions) {
    try {
        // TODO(lifecycler): Use interface callback instead of AMS.
        // 更新 ATM 中,ActivityRecord 生命周期状态
        ActivityTaskManager.getService().activityResumed(token);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

以上 execute 方法主要是执行 本地应用进行resume过程,最终调用Activity::onResume方法。而 postExecute则是调用 ATM中的接口,将ActivityRecord状态更新为resume. postExecute 方法不过多往下扩展了,看下client.handleResumeActivity

frameworks/base/core/java/android/app/ActivityThread.java

scss 复制代码
public void handleResumeActivity(IBinder token, 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
    // 代码1 performResumeActivity 方法最终执行调用`Activity::onResume`
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    Log.d(TAG, "chld handleResumeActivity1: r:" + r);
    if (r == null) {
        // We didn't actually resume the activity, so skipping any follow-up actions.
        return;
    }
    //如果Activity 即将被销毁,则没必要继续了.
    if (mActivitiesToBeDestroyed.containsKey(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;
    }
​
    final Activity a = r.activity;
​
    if (localLOGV) {
        Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity
               + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);
    }
​
    final int forwardBit = isForward
        ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
​
    // If the window hasn't yet been added to the window manager,
    // and this guy didn't finish itself or start another activity,
    // then go ahead and add the window.
    // a.mStarted Activity 值必定为false.在performResumeActivity 该值被赋值为false了.
    boolean willBeVisible = !a.mStartedActivity;
    if (!willBeVisible) {
        try {
            willBeVisible = ActivityTaskManager.getService().willActivityBeVisible(
                a.getActivityToken());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    //代码2 对Window 进行初始初始化,且将decorWindow 通过 ViewManager::addView 添加Window,与WindowManagerService 进行通信.
    // 此处的 wm 对象的类是 `WindowManagerImpl`
    // 此时由于decior 被设置为INVISIBLE,所以,Activity仍然是不可见的
    // 一般是在 Activity onCreate -> onStart -> onResume 生命周期变化时进该方法.最终调用 wm.addView
    if (r.window == null && !a.mFinished && willBeVisible) {
        Log.d(TAG,"chld handleResumeActivity2");
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        l.softInputMode |= forwardBit;
        Log.d(TAG,"chld handleResumeActivity3 mPreserveWindow:" + r.mPreserveWindow + "  a.mVisibleFromClient:" + a.mVisibleFromClient + " mWindowAdded:" + a.mWindowAdded);
        if (r.mPreserveWindow) {
            a.mWindowAdded = true;
            r.mPreserveWindow = false;
            // Normally the ViewRoot sets up callbacks with the Activity
            // in addView->ViewRootImpl#setView. If we are instead reusing
            // the decor view we have to notify the view root that the
            // callbacks may have changed.
            ViewRootImpl impl = decor.getViewRootImpl();
            if (impl != null) {
                impl.notifyChildRebuilt();
            }
        }
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                //添加View,该过程会创建 ViewRootImpl 对象
                wm.addView(decor, l);
            } else {
                // The activity will get a callback for this {@link LayoutParams} change
                // earlier. However, at that time the decor will not be set (this is set
                // in this method), so no action will be taken. This call ensures the
                // callback occurs with the decor set.
                a.onWindowAttributesChanged(l);
            }
        }
​
        // If the window has already been added, but during resume
        // we started another activity, then don't yet make the
        // window visible.
    } else if (!willBeVisible) {
        if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
        r.hideForNow = true;
    }
​
    // Get rid of anything left hanging around.
    cleanUpPendingRemoveWindows(r, false /* force */);
​
    // The window is now visible if it has been added, we are not
    // simply finishing, and we are not starting another activity.
    // 更新Activity 并显示,每次Activity生命周期发生变化时,从其他生命周期走到 onResume时都会进入该方法. 且会调用 wm.updateViewLayout 更新 deor 和 WindowManager.LayoutParams
    if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
        Log.d(TAG,"chld handleResumeActivity4");
        if (r.newConfig != null) {
            performConfigurationChangedForActivity(r, r.newConfig);//设备配置更新变化通知
            if (DEBUG_CONFIGURATION) {
                Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig "
                       + r.activity.mCurrentConfig);
            }
            r.newConfig = null;
        }
        if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
        ViewRootImpl impl = r.window.getDecorView().getViewRootImpl();
        WindowManager.LayoutParams l = impl != null
            ? impl.mWindowAttributes : r.window.getAttributes();
        if ((l.softInputMode
             & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
            != forwardBit) {
            l.softInputMode = (l.softInputMode
                               & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                | forwardBit;
            if (r.activity.mVisibleFromClient) {
                Log.d(TAG,"chld handleResumeActivity5");
                ViewManager wm = a.getWindowManager();
                View decor = r.window.getDecorView();
                wm.updateViewLayout(decor, l);//新View的LayoutParams并替换掉老的LayoutParams
            }
        }
​
        r.activity.mVisibleFromServer = true;
        mNumVisibleActivities++;
        if (r.activity.mVisibleFromClient) {
            Log.d(TAG,"chld handleResumeActivity6");
            r.activity.makeVisible();//使View可见,调用了 mDecor.setVisibility(View.VISIBLE);
        }
    }
​
    r.nextIdle = mNewActivities;
    mNewActivities = r;
    if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
    Looper.myQueue().addIdleHandler(new Idler());
}
​
  • 代码1 处 performResumeActivity 方法中将会调用到Activity::onResume
  • 代码2 处 仅第一次进入resume时,会调用 wm.addView,将decorView绑定到 WMS.真正的与窗口产生交互.

此时Activity 创建流程重点关注的还是performResumeActivity 方法.

frameworks/base/core/java/android/app/ActivityThread.java

scss 复制代码
public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
                                                  String reason) {
    final ActivityClientRecord r = mActivities.get(token);
    if (localLOGV) {
        Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished);
    }
    if (r == null || r.activity.mFinished) {
        return null;
    }
    if (r.getLifecycleState() == ON_RESUME) {//已在ON_RESUM,不需要进行其他操作
        //...省略
        return null;
    }
    if (finalStateRequest) {
        r.hideForNow = false;
        r.activity.mStartedActivity = false;//正在onResume过程中,此时Activity未彻底启动完成.
    }
    try {
        r.activity.onStateNotSaved();
        r.activity.mFragments.noteStateNotSaved();
        checkAndBlockForNetworkAccess();
        //携带的intetn数据处理,最终调用到Activity::onNewIntent
        if (r.pendingIntents != null) {
            deliverNewIntents(r, r.pendingIntents);
            r.pendingIntents = null;
        }
        //关于result的处理,如果是从A调用startActivityForResult到B,B Activity结束返回A时,存在该数据,最终调用到onActivityResult
        if (r.pendingResults != null) {
            deliverResults(r, r.pendingResults, reason);
            r.pendingResults = null;
        }
        
        r.activity.performResume(r.startsNotResumed, reason);
​
        r.state = null;
        r.persistentState = null;
        //设置AcitivityClientRecord 当前生命周期状态为 ON_RESUME
        r.setState(ON_RESUME);
​
        reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming");
    } catch (Exception e) {
        if (!mInstrumentation.onException(r.activity, e)) {
            throw new RuntimeException("Unable to resume activity "
                                       + r.intent.getComponent().toShortString() + ": " + e.toString(), e);
        }
    }
    return r;
}
​

该方法中进行了 intent 数据和从其他Activity返回的result 数据处理. 并调用到了r.activity.performResume.

frameworks/base/core/java/android/app/Activity.java

scss 复制代码
final void performResume(boolean followedByPause, String reason) {
    dispatchActivityPreResumed();
    // 当Activity 处于 STOP状态时,会执行 Activity::onRestart
    // 一般不会是从此处走到onRestart状态,这处代码应该只是防御性编程,防止万一的情况
    performRestart(true /* start */, reason);
​
    mFragments.execPendingActions();
​
    mLastNonConfigurationInstances = null;
​
    if (mAutoFillResetNeeded) {
        // When Activity is destroyed in paused state, and relaunch activity, there will be
        // extra onResume and onPause event,  ignore the first onResume and onPause.
        // see ActivityThread.handleRelaunchActivity()
        mAutoFillIgnoreFirstResumePause = followedByPause;
        if (mAutoFillIgnoreFirstResumePause && DEBUG_LIFECYCLE) {
            Slog.v(TAG, "autofill will ignore first pause when relaunching " + this);
        }
    }
​
    mCalled = false;
    // mResumed is set by the instrumentation
    //执行调用Activity::onResume
    mInstrumentation.callActivityOnResume(this);
    EventLogTags.writeWmOnResumeCalled(mIdent, getComponentName().getClassName(), reason);
    if (!mCalled) {
        throw new SuperNotCalledException(
            "Activity " + mComponent.toShortString() +
            " did not call through to super.onResume()");
    }
​
    //...非法状态处理
​
    // Now really resume, and install the current status bar and menu.
    mCalled = false;
    // Activity下的Fragment 收到onResume状态
    mFragments.dispatchResume();
    mFragments.execPendingActions();
    
    onPostResume();
    if (!mCalled) {
        throw new SuperNotCalledException(
            "Activity " + mComponent.toShortString() +
            " did not call through to super.onPostResume()");
    }
    dispatchActivityPostResumed();
}

mInstrumentation.callActivityOnResume

frameworks/base/core/java/android/app/Instrumentation.java

typescript 复制代码
public void callActivityOnResume(Activity activity) {
    activity.mResumed = true;
    activity.onResume();
    //...省略
}

至此调用到了Activity::onResume 处。

6. 总结

(1)一个Activity 启动的过程.根据App进程是否已创建的区别. 其流程在Android Activity 启动过程(上)2.4 章节中 ActivityStackSupervisor::startSpecificActivity 方法中分道了.

  • App进程已存在时,直接调用ActivityStackSupervisor::realStartActivityLocked 方法.
  • 进程未创建时调用ActivityTaskManagerService::startProcessAsync 去创建App 进程,App进程创建完成后,调用到ActivityTaskManagerService::attachApplication 去绑定应用。然后调用到RootWindowContainer::startActivityForAttachedApplicationIfNeeded 方法,在该方法中调用到ActivityStackSupervisor::realStartActivityLocked

(2) Activity 在App 进程中的生命周期是由 TransactionExecutorClientTransaction 进行控制调度的.ATMS 通过调用ActivityThread的scheduleTransaction 方法传入ClientTranscation 对象,再由TransactionExecutor去解析和执行Activity生命周期的转换.

至此基于 android-security-11.0.0_r66 分支的,一个Acitvity的启动过程,以及这其中的App 进程的创建过程是介绍完了.本篇文章还缺少一个流程图,去更加直观的展现,等后面补充下吧。

相关推荐
LCG元1 小时前
【面试问题】JIT 是什么?和 JVM 什么关系?
面试·职场和发展
拭心3 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
GISer_Jing5 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245525 小时前
吉利前端、AI面试
前端·面试·职场和发展
带电的小王5 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡6 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道6 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
TodoCoder7 小时前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试
阿甘知识库7 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道8 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频