eaaomk

eaaomk eaaomk
主页
博客
  • 常用命令
  • 情景再现
  • AMS
  • Zygote
  • 审计
  • 财务会计
  • 进程与线程
  • 消息传递
  • 处理机调度与死锁
  • 设计思想与代码质量优化
  • 设计模式
  • 数据结构
  • 算法
  • VSCODE
标签
时间轴
关于
author-avatar

eaaomk

37

文章

10

标签

主页
博客
  • 常用命令
  • 情景再现
  • AMS
  • Zygote
  • 审计
  • 财务会计
  • 进程与线程
  • 消息传递
  • 处理机调度与死锁
  • 设计思想与代码质量优化
  • 设计模式
  • 数据结构
  • 算法
  • VSCODE
标签
时间轴
关于

Activity任务栈

Activity任务栈

eaaomk 2021-12-27 23:00:00 JavaAndroid

# Activity任务栈

  • 相信读者看到这里也比较困惑,AMS不是管理所有Activity的调度吗?那为什么看不见它在哪里?大家在网上搜索关键词 <adb 命令查看Activity栈>有如下结果:
adb shell dumpsys activity activities
  • 我们连接上手机,输入这个命令,随后会打印出如下类似信息:
    ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
    Display #0 (activities from top to bottom):

    Stack #30: type=standard mode=fullscreen
    isSleeping=true
    mLastPausedActivity: ActivityRecord{3fc7301 u0 com.android.settings/.Settings$SprdUsbSettingsActivity t30}
    * Task{4733394 #30 visible=true type=standard mode=fullscreen translucent=true A=1000:com.android.settings U=0 StackId=30 sz=1}
      * Hist #0: ActivityRecord{3fc7301 u0 com.android.settings/.Settings$SprdUsbSettingsActivity t30}

    Stack #1: type=home mode=fullscreen
    isSleeping=true
    mLastPausedActivity: ActivityRecord{c2abe2d u0 com.***.***.launcher/com.android.***.recents_ui_overrides.src.com.android.launcher3.uioverrides.*** t21}
    * Task{e6d9bdb #21 visible=true type=home mode=fullscreen translucent=false A=10207:com.***.***.launcher.task.Launcher U=0 StackId=1 sz=1}
      * Hist #0: ActivityRecord{c2abe2d u0 com.***.***.launcher/com.android.***.recents_ui_overrides.src.com.android.launcher3.uioverrides.*** t21}
    Stack #28: type=standard mode=fullscreen
    isSleeping=true
    mLastPausedActivity: ActivityRecord{3b2528 u0 com.***20/.activities.***Activity t28}
    * Task{f6b1ebe #28 visible=false type=standard mode=fullscreen translucent=true A=10212:com.***20 U=0 StackId=28 sz=2}
      * Hist #1: ActivityRecord{3b2528 u0 com.***20/.activities.***Activity t28}
      * Hist #0: ActivityRecord{f07160e u0 com.***20/.activities.***Activity t28}
  • 如此可见,Activity在ATMS中如何保存的就可以清晰的观察出来了。
  • 在Display中,存在多个Stack,一个Stack中存在多个Task,一个Task中存在多个ActivityRecord。
  • 我们的一个应用对应了一个Stack,系统中存在多个应用在运行,Display就对应着多个Stack,这个Stack在Android 源码中对应的就是ActivityStack类,这时候就应该注意到了,我们的Activity有不同的启动模式,各个启动模式说法中所说的任务栈就是这个ActivityStack,举个栗子:SingleInstance模式中提到的开启一个新的栈就是ActivityStack。 分析到这里应该都明白了,ActivityRecord对应的就是一个Activity。
  • 我们回过头来看看,既然命令能够查看到Android系统的任务栈,那么它一定是有数据的,那么它是从哪里获得的这些数据呢?上源码:
//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
// 此方法就是在获取我们所有的Activity的信息。
 boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,String dumpPackage) {
    boolean printed = false;
    boolean needSep = false;
    for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
        DisplayContent displayContent = getChildAt(displayNdx);
        if (printed) {
            pw.println();
        }
        /// 开始打印了
        pw.print("Display #"); pw.print(displayContent.mDisplayId);
        pw.println(" (activities from top to bottom):");
        for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
            // 获取一个taskDisplayArea,一般情况下是默认的
            final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx);
            for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
                /// 遍历taskDisplayArea中的所有ActivityStack
                final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
                if (stack == null) {
                    continue;
                }
                if (needSep) {
                    pw.println();
                }
                // 开始调用ActivityStack的dump 继续打印
                needSep = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, false);
                printed |= needSep;
            }
        }
        ...
    }
    ...
    return printed;
}
//frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,String dumpPackage, final boolean needSep) {
    Runnable headerPrinter = () -> {
        if (needSep) {
            pw.println();
        }
        /// 我们的Stack 就是ActivityStack
        pw.println("  Stack #" + getRootTaskId()
                + ": type=" + activityTypeToString(getActivityType())
                + " mode=" + windowingModeToString(getWindowingMode()));
        pw.println("  isSleeping=" + shouldSleepActivities());
        pw.println("  mBounds=" + getRequestedOverrideBounds());
    };
    ...
    printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter);
    return printed;
}
//frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
            boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) {
        if (!hasChild()) {
            return false;
        }
        ...
        forAllLeafTasks((task) -> {
            final String prefix = "   ";
            ...
            final ArrayList<ActivityRecord> activities = new ArrayList<>();
            // Add activities by traversing the hierarchy from bottom to top, since activities
            // are dumped in reverse order in {@link ActivityStackSupervisor#dumpHistoryList()}.
            task.forAllActivities((Consumer<ActivityRecord>) activities::add,
                    false /* traverseTopToBottom */);
            //打印ActivityRecord
            dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
                    dumpPackage, false, headerPrinter, task);
        }, true /* traverseTopToBottom */);
        return printed.get();
    }

# 任务栈数据结构剖析

  • 数据结构相对来讲稍微复杂一点点,接下来用一个简化版的类图作为说明,注意,这里也涉及到的是WMS。

avatar

  • 上图中十分关键的类是WindowContainer类,在这个类中,拥有一个属性mChildren,它其实就是一个ArrayList,WindowList只不过是继承它,多写了几个方法罢了,下面的类再去继承这个WindowContainer,也就都拥有了mChildren属性。
  • 按照上图的顺序,Android系统中的任务栈数据结构就是《无限套娃》
  • 在系统服务启动的过程中,WMS与AMS的第一次套娃在这里:
//frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t){
    ...
    t.traceBegin("StartWindowManagerService");
    // WMS needs sensor service ready
    ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
    mSensorServiceStart = null;
    // 初始化WMS,将ATMS放进去
    wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
            new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
            DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
        /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
    t.traceEnd();
    t.traceBegin("SetWindowManagerService");
    // WMS 创建完成,将其放入到AMS中
    mActivityManagerService.setWindowManager(wm);
    t.traceEnd();
    t.traceBegin("WindowManagerServiceOnInitReady");
    // 准备工作结束,调用WMS的一些初始化后续的操作
    wm.onInitReady();
    t.traceEnd();
    ...
}
  • 看看setWindowManager方法在做什么:
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void setWindowManager(WindowManagerService wm) {
    synchronized (this) {
        // 引用WMS
        mWindowManager = wm;
        mWmInternal = LocalServices.getService(WindowManagerInternal.class);
        // 再设置到ATMS
        mActivityTaskManager.setWindowManager(wm);
    }
}

  • 这里也是将WMS存入了一个引用,方便后续操作直接调用WMS
  • 重点在这里,套娃开始,ATMS中的mRootWindowContainer其实是WMS中的一个属性,它在WMS诞生的的时候进行了初始化,后续会在WMS系列中讲到.
//frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public void setWindowManager(WindowManagerService wm) {
    synchronized (mGlobalLock) {
        // 引用WMS
        mWindowManager = wm;
        // 设置mRootWindowContainer
        mRootWindowContainer = wm.mRoot;
        ...
        // 继续向下设置WMS
        mLockTaskController.setWindowManager(wm);
        mStackSupervisor.setWindowManager(wm);
        mRootWindowContainer.setWindowManager(wm);
    }
}
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
// 定义的WMS静态变量sInstance
private static WindowManagerService sInstance;
static WindowManagerService getInstance() {
    return sInstance;
}
//初始化方法
public static WindowManagerService main(final Context context, final InputManagerService im,
        final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
        ActivityTaskManagerService atm) {
    return main(context, im, showBootMsgs, onlyCore, policy, atm,
            SurfaceControl.Transaction::new, Surface::new, SurfaceControl.Builder::new);
}

@VisibleForTesting
public static WindowManagerService main(final Context context, final InputManagerService im,
        final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
        ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
        Supplier<Surface> surfaceFactory,
        Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
    DisplayThread.getHandler().runWithScissors(() ->
        //真正的初始化在这里
            sInstance = new SprdWindowManagerService(context, im, showBootMsgs, onlyCore, policy,
                    atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0);
    return sInstance;
}

# 总结

  • mRootWindowContainer 是在WMS中进行初始化的,初始化完成后,又将其注入到了AMS、ATMS中,在WMS的初始化的过程中,又将其注入到了ATMS下的mStackSupervisor中。它就是一块砖,哪里需要哪里搬。WMS与AMS还有很多交互的地方需要调用对方的方法,所以会看见一个参数四处都在传递与引用。但总的包含关系已经在上面的类图中体现了:
    • RootWindowContainer 包含 DisplayContent
    • DisplayContent 包含 DisplayAreaPolicy,
    • DisplayAreaPolicy 包含 TaskDisplayArea
    • TaskDisplayArea 包含 ActivityStack
    • ActivityStack 包含 ActivityRecord
  • 其实这里有一个疑问,我们的堆栈信息看见的是一个Stack内部拥有的是一个对象是Task,那么Stack里面会有多个Task吗?还是说这个Task并不存在,Stack直接包含了ActivityRecord?笔者已经尝试了下面几种方式,供读者尝试:
    • 以SingleInstance模式启动,开启了新的ActivityStack,堆栈信息显示的是多了一个Stack
    • 在应用程序A中拉起应用程序B的一个Activity,开启了新的ActivityStack,堆栈信息显示的是多了一个Stack
    • 普通模式下,同应用程序内部启动一个新的Activity,会与启动它的Activity在同一个Stack下
    • 启动应用程序的第一个Activity,会打开一个新的ActivityStack
  • 这里我们暂时只需要关注这个ActivityStack即可。一般情况下,在Android任务栈中,一个ActivityStack记录了一个应用的所有启动的Activity信息,我们的应用程序默认都是运行在同一个进程中的,它们可能会存在不同的ActivityStack里面(以SingleInstance这种模式启动的将会打开一个新的ActivityStack),但是它们的进程一定是相同的,当然也可以实现一个应用程序多个进程(AIDL 跨进程通信),那么进程就不相同了。这里我们重点关注一般情况,ActivityStack里面包含了这个应用程序所有启动的 任务(ActivityRecord),堆栈信息中的Stack也就是大家平时都了解到的Activity的四大启动模式中提及到的任务栈(ActivityStack),不同的启动方式决定了我们启动的Activity存在于哪个任务栈,在这个任务栈中存储的有我们启动的Activity记录,它并不是以一个Activity的形式存在,而是以ActivityRecord的形式存在。