AMS相关数据结构

一 数据结构

总体架构:

图中的方框可以理解为一个中包含关系:譬如一个TaskRecord中包含多个ActivityRecord; 图中的连接线可以理解为等价关系,譬如同一个ActivityRecord会被TaskRecord和ProcessRecord引用,两者是从不同维度来管理ActivityRecord。

  • ActivityRecord是Activity管理的最小单位,它对应着一个用户界面;
  • TaskRecord也是一个栈式管理结构,每一个TaskRecord都可能存在一个或多个ActivityRecord,栈顶的ActivityRecord表示当前任务可见的界面;
  • ActivityStack是一个栈式管理结构,每一个ActivityStack都可能存在一个或多个TaskRecord,栈顶的TaskRecord表示当前可见的任务;
  • ActivityStackSupervisor管理着多个ActivityStack,但当前只会有一个获取焦点(Focused)的ActivityStack;
  • ProcessRecord记录着属于一个进程的所有ActivityRecord,运行在不同TaskRecord中的ActivityRecord可能是属于同一个 ProcessRecord。
  • 同一个TaskRecord的ActivityRecord可能分别处于不同的进程中,每个ActivityRecord所处的进程跟task没有关系

层次结构:

1.1 ProcessRecord

一个 APK 文件运行时会对应一个进程, 当然, 多个 APK 文件也可以运行在同一个进程中。ProcessRecord 正是记录一个进程中的相关信息, 该类中内部变量可分为三个部分,主要信息包括:该进程对应的 APK 文件的内部信息,该进程的内存状态信息,以及该进程中包含的所有 Activity、Provider、Service 等组件信息。详细见java进程管理(四)之进程记录表(ProcessRecord)

class ProcessRecord implements WindowProcessListener {
    ......
    final ApplicationInfo info; //进程中第一个APK包信息 
    ......
    final String processName;
    ......
    // List of packages running in the process
    final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList = new ArrayMap<>();
    ......
    IApplicationThread thread;
    ......
    int pid;
    ......
    final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();
    ......
    // all ServiceRecord running in this process
    final ArraySet<ServiceRecord> services = new ArraySet<>();
    ......
    final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
}

1.2 TaskRecord

关于TaskRecord详细讲解见 AMS中最重要的数据结构-TaskRecord(任务栈)

class TaskRecord extends ConfigurationContainer {    
    ......
    final int taskId;       // Unique identifier for this task.
    String affinity;        // The affinity name for this task, or null; may change identity.
    ......
    /** List of all activities in the task arranged in history order */
    final ArrayList<ActivityRecord> mActivities; 
    /** Current stack. Setter must always be used to update the value. */
    private ActivityStack mStack;//当前任务的管理者    
    ......
}

TaskRecord 的 affinity 属性是第一次创建这个 Task 时指定的值,可以把他当作这个 Task 的名字,Activity 所在的 Task 可以通过 AndroidManifest.xml 中 标签中的 android:taskAffinity=“xxx” 来指定,通常不去主动设置一个 Activity 的 taskAffinity 属性,那么 taskAffinity 的值缺省使用包名。所以,同一个应用中所有的 Activity 的 taskAffinity 属性值默认都是相同的,都是包名.所以在应用中使用 FLAG_ACTIVITY_NEW_TASK 标志去启动一个本应用中的一个 Activity,也不会创建一个新的 Task,除非这个 Activity 额外指定了不同的 taskAffinity 属性值。

mActivities 表示当前栈中管理的所有 ActivityRecord.

mStack 表示当前 TaskRecord 所属的 ActivityStack。TaskRecord 除了要维护管理它包含的 ActivityRecord,它本身作为一个栈,还需要维护自身任务栈的状态。

1.3 ActivityRecord

AMS 中使用 ActivityRecord 数据类来保存每个 Activity 的相关信息,该数据类中的变量主要包含两部分:

  • 配置信息:该 Activity 组件相关的 XML 中的配置信息 ,比如 ,属于哪个 Package ,所在的进程名称、任务名、组件类名、logo、主题,等等,这些信息基本上是固定的.
  • 运行状态信息:比如 idle、stop、fishing 等,这些变量一般为 boolean 类型,这些状态值与应用程序中所说的 onCreate、onPause、onStart 等生命周期状态有所不同.
final class ActivityRecord extends ConfigurationContainer {
    ......
    final ActivityTaskManagerService mAtmService;// owner
    final IApplicationToken.Stub appToken;// window manager token
    private TaskRecord task;        // the task this is in.
    ......
}

ActivityRecord 中的大部分成员变量都是记录 Activity 的相关信息,其中有一个成员变量 appToken 需要重点关注,从定义可以看出 appToken 是用来进行跨进程通信的,Android 系统中用 AMS 来管理 Activity,而它和应用程序是运行在不同进程中的,appToken 就可以看做是连接系统进程和应用进程的桥梁,我们后面会详细介绍它的作用.成员变量 task 代表当前 Activity 属于哪一个栈,相关的栈信息就保存在 TaskRecord 中。ActivityRecord 与 TaskRecord 的关系简单总结如下:

TaskRecord 的职责就是管理 ActivityRecord,记录 Activity 开启的先后顺序。每个 ActivityRecord 都必须属于一个 TaskRecord,TaskRecord 与 ActivityRecord 是一对多的关系,栈顶的 ActivityRecord 表示当前用户可见的界面.

1.4 ActivityStack

对于多个 Task 的组织及管理方式,Android 设计了一个 ActivityStack 类来负责上述工作,它的职责是管理 TaskRecord。每个 TaskRecord 都必须属于一个 ActivityStack,ActivityStack 与 TaskRecord 是一对多的关系,栈顶的 TaskRecord 代表当前用户可见的任务。ActivityStack 用 mTaskHistory 这个 ArrayList 保存所有属于该 ActivityStack 的 TaskRecord 实例(这个集合叫做任务栈)。另外 ActivityStack 有多种类型:mHomeStack,mLastFocusedStack 等,在 ActivityStack 中用 mStackId 来标识。

class ActivityStack extends ConfigurationContainer {
    ...... 
    private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>(); 
    ......
    final int mStackId;
    ......
    /** Run all ActivityStacks through this */
    protected final ActivityStackSupervisor mStackSupervisor;
    ......
    enum ActivityState {
        INITIALIZING,
        RESUMED,
        PAUSING,
        PAUSED,
        STOPPING,
        STOPPED,
        FINISHING,
        DESTROYING,
        DESTROYED,
        RESTARTING_PROCESS
    }
    ......
}

每个 Activity 都会有一个状态,譬如显示,销毁。这些状态的变化都需要 ActivityStack 来进行管理。Activity 的状态是通过 ActivityStack 中的成员变量 ActivityState 来定义的。

1.5 ActivityDisplay

ActivityDisplay 表示一个屏幕,Android 支持三种屏幕:主屏幕,外接屏幕(HDMI等),虚拟屏幕(投屏)。一般情况下,即只有主屏幕时,ActivityStackSupervisor 与 ActivityDisplay 都是系统唯一。

1.6 ActivityStackSupervisor

在 Android4.4 以后,并不采用原先的 mHistory 来管理所有的 ActivityRecord ,而是按层次进行管理,这其中就引入了一个新的类:ActivityStackSupervisor,用来管理所有的 ActivityStack(之前只有一个 mMainStack,由 AMS 来管理的 )

public class ActivityStackSupervisor implements RecentTasks.Callbacks {
    ......
    final ActivityTaskManagerService mService;
    ......
    WindowManagerService mWindowManager;
    ......
}

二  AMS与APP中Activity通信

我们知道,在 Android 中的 system_server 进程中运行着系统中重要的服务(AMS,PMS,WMS).对系统服务 AMS 来说,它需要与应用程序进行通信,调度 Activity 执行,管理 Activity 的状态。

对每一个应用程序来说,它运行在独立的进程中,有自己独立的内存空间,它需要接受 AMS 的调度,执行 Activity 相应的生命周期回调函数。所以不管是从系统到应用还是从应用到系统,它们都需要跨进程调用。

Android 中的跨进程通信方法就是 Binder。Android 为系统与应用间的相互通信设计了两个 Binder 接口。如下图:

IApplicationThread: 系统进程请求应用进程的接口。

IActivityTaskManager,IActivityManager:应用进程请求系统进程的接口。以前的版本只有 IActivityManager 一个接口,但是因为 AMS 的代码实在太多了,所以谷歌在最近的版本里新加了 IActivityTaskManager,把之前一些 AMS 里面的工作挪到了 ActivityTaskManager 里面.

我们在介绍 Activiy 有关的重要数据结构的时候提到了 ProcessRecord,ProcessRecord 的一个重要的作用就是 AMS 可以利用它远程调用到应用进程中的函数。在应用进程 ActivityThread 创建的时候,它会将自己的 ApplicationThread 绑定到 AMS 中,整个绑定过程如下:

ActivityThread.main() -> ActivityThread.attach() -> IActivityManager.attachApplication(ApplicationThread)

AMS 作为 IActivityManager 接口服务端的实现,会响应客户端的请求,最终 AMS.attachApplication() 会被执行,该函数接收跨进程传递过来的 ApplicationThread 实例,然后将其赋值给 AMS 维护的 ProcessRecord.thread。这样后面 AMS 就可以通过 ProcessRecord.thread 调用应用进程中的函数了。

我们除了要在应用进程和系统进程中进行通信,还需要一个方法能够在应用进程和系统进程中同步 Activity 的状态。

AMS 中采用 ActivityRecord 来描述 Activity,应用进程中采用 ActivityClientRecord 和 Activity。这三者之间一一对应,从而达到 Activity 状态在系统进程和应用进程之间的同步。 

2.1 AMS类图

 

2.2 APP如何调用AMS方法

2.3 AMS如何调用App方法 

2.4 ActivityThread和ApplicationThread

ActivityThread 类代表的就是 Android 应用程序进程中的主线程,注意它代表主线程而不是说它就是一个 Thread 线程类,因为系统在创建完一个新应用程序进程后,会在这个进程的主线程中调用 ActivityThread 类的 main 函数来初始化这个进程,在这个 main 函数里会执行一个 loop 循环使当前主线程进入消息循环,所以我们称 Android 应用程序进程的入口函数是 ActivityThread 类的 main 函数,也就是说一个ActivityThread 类对应于一个应用程序进程。

public final class ActivityThread extends ClientTransactionHandler {
    ......
    final ApplicationThread mAppThread = new ApplicationThread();
    final Looper mLooper = Looper.myLooper();
    final H mH = new H();
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    ......
    final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
    ......
    Application mInitialApplication;
    final ArrayList<Application> mAllApplications
            = new ArrayList<Application>();
    ......
    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
        = new ArrayMap<IBinder, ProviderClientRecord>();
    ......
    private class ApplicationThread extends IApplicationThread.Stub {
        ......   
    }
    ......
}

ActivityThread 中有几个重要的实例变量需要简要介绍下,成员变量 mActivities 包含了当前进程中所有的 Activity 组件信息,注意集合中不是简单的保存 Activity 实例,而是将每个 Activity 实例对应的相关数据封装成一个 ActivityClientRecord 保存起来,内部保存了很多 Activity 相关的数据,其中当然也有其对应的 Activity 实例。同样的,mServices 保存的是当前进程中所有的 Service 组件信息,不过就是直接存放的当前 Service 的信息,没有封装。mLocalProviders 保存的是当前进程中所有的 ContentProvider 组件信息。这些集合都有一个 IBinder 类型的 Key,其作用也就是显而易见的,即为了进行 IPC 调用,这个 IBinder 类型的 Key 作为一个唯一标识使用,通过这个 Key 可以在 AMS 中找到其对应的组件信息记录。

mAppThread 是 ApplicationThread 类型的变量,它是 ActivityThread 的内部类,也是一个 Binder 类型的对象,也就是说可以作为服务端实现跨进程通信。那么,在应用进程中的 ActivityThread 通过 Binder 机制和系统进程中的 AMS 通信时,使用的就是 ApplicationThread 对象来作为应用进程的服务端,接收 AMS 的指令并将指令发送给 ActivityThread 执行,所以它是 ActivityThread 与 AMS 跨进程通信的桥梁。因为 ApplicationThread 对象会被 Binder 线程调用,而 ActivityThread 是运行在主线程中的,所以 ApplicationThread 会通过 mH 对象发送消息给主线处理。

2.5  ActivityRecord 提到的成员变量 appToken 

final class ActivityRecord extends ConfigurationContainer {
	final IApplicationToken.Stub appToken; // window manager token 
	ActivityRecord(ActivityTaskManagerService _service, ......) {
			......
			appToken = new Token(this, _intent);
			......
	} 
	static class Token extends IApplicationToken.Stub {
        private final WeakReference<ActivityRecord> weakActivity;
        private final String name;

        Token(ActivityRecord activity, Intent intent) {
            weakActivity = new WeakReference<>(activity);
            name = intent.getComponent().flattenToShortString();
        }
        private static @Nullable ActivityRecord tokenToActivityRecordLocked(
                Token token) {
            if (token == null) {
                return null;
            }
            ActivityRecord r = token.weakActivity.get();
            if (r == null || r.getActivityStack() == null) {
                return null;
            }
            return r;
        }
        ......
        @Override
        public String getName() {
            return name;
        }
    }
}

在创建 ActivityRecord 的时候我们会对 appToken 进行赋值,然后我们发现 Token 中持有 ActivityRecord 的弱引用,也就是说可以通过 appToken 找到 ActivityRecord。因此我们只要把 appToken 传到 App 进程中,并赋值给 ActivityClientRecord 和 Activity,就可以建立这三者之间的对应关系。

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

public final Activity startActivityNow(Activity parent, String id,
        Intent intent, ActivityInfo activityInfo, IBinder token,
        Bundle state, ......, IBinder assistToken) {
        ActivityClientRecord r = new ActivityClientRecord();
            r.token = token;
            r.assistToken = assistToken;
            r.ident = 0;
            r.intent = intent;
            r.state = state;
            r.parent = parent;
            r.embeddedID = id;
            r.activityInfo = activityInfo;
            r.lastNonConfigurationInstances = lastNonConfigurationInstances;
        if (localLOGV) {
            ComponentName compname = intent.getComponent();
            String name;
            if (compname != null) {
                name = compname.toShortString();
            } else {
                name = "(Intent " + intent + ").getComponent() returned null";
            }
            Slog.v(TAG, "Performing launch: action=" + intent.getAction()
                    + ", comp=" + name
                    + ", token=" + token);
        }
        
        return performLaunchActivity(r, null /* customIntent */);
    }

可以看到 ActivityThread 中创建了 ActivityClientRecord,并将 token 赋值给了该 ActivityClientRecord.

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 activity = null;
        try {
            ......
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ......
        } catch (Exception e) {
            ......
        }

   if (activity != null) {
         ......
         activity.attach(appContext, this, getInstrumentation(), r.token,
             r.ident, app, r.intent, r.activityInfo, title, r.parent,
             r.embeddedID, r.lastNonConfigurationInstances, config,
             r.referrer, r.voiceInteractor, window, r.configCallback);
         ......
    }
}

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

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, ......) {
        attachBaseContext(context);
        ......
        mToken = token;
        ......
}

Activity.attach() 中将 ActivityClientRecord.token 赋值给了Activity.mToken。至此,ActivityRecord、ActivityClientRecord、Activity 三者的一一对应关系就建立完毕了。

总结一下,ActivityRecord 和 ActivityClientRecord 都是保存 Activity 信息的对象。只不过,ActivityRecord 归系统进程使用,ActivityClientRecord 归应用进程使用。他们之间通过 token 建立同步关系。

三  数据结构的生成

当我们启动一个新的 Activity 的时候,首先会为这个 Activity 创建一个 ActivityRecord,然后再为这个 Activty 分配所属的 TaskRecord,这个 TaskRecord 又要分配到对应的 ActivityStack 中,而 ActivityStack 也要分配到所属的 ActivityDisplay。

这一整套关系确立之后,我们才能够真正启动这个 Activity。为了便于理解我们后面对 Activity 启动流程的分析,这里我们就先把这些数据结构的创建过程单独拎出来分析。

3.1 ProcessRecord的创建

每个进程在AMS侧对应有一个ProcessRecord。这些ProcessRecord是通过ProcessMap来管理的。

进程的启动的入口函数是AMS.startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags,String hostingType, ComponentName hostingName, boolean allowWhileBooting,boolean isolated, boolean keepIfLarge)。当启动Activity、Service、getContentProvider时如果目标进程未启动,那么就需要调用startProcessLocked()来启动目标进程。

ATMS.startProcessAsync

    void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
            String hostingType) {
        try {
            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
                        + activity.processName);
            }
            // Post message to start process to avoid possible deadlock of calling into AMS with the
            // ATMS lock held.
            final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
                    mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
                    isTop, hostingType, activity.intent.getComponent());
            mH.sendMessage(m);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

AMS.startProcess 

       @Override
        public void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,
                boolean isTop, String hostingType, ComponentName hostingName) {
            try {
                if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"
                            + processName);
                }
                synchronized (ActivityManagerService.this) {
                    // If the process is known as top app, set a hint so when the process is
                    // started, the top priority can be applied immediately to avoid cpu being
                    // preempted by other processes before attaching the process of top app.
                    startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
                            new HostingRecord(hostingType, hostingName, isTop),
                            ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
                            false /* isolated */, true /* keepIfLarge */);
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
        }

AMS.startProcessLocked()

    @GuardedBy("this")
    final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
            boolean isolated, boolean keepIfLarge) {
        return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
                hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
                keepIfLarge, null /* ABI override */, null /* entryPoint */,
                null /* entryPointArgs */, null /* crashHandler */);
    }

ProcessList.startProcessLocked

// com.android.server.am.ProcessList.java  
final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            String hostingType, ComponentName hostingName, boolean allowWhileBooting,
            boolean isolated, boolean keepIfLarge) {
        ProcessRecord app;
        if (!isolated) {
            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);    //①获取进程记录块
        } else {
            // If this is an isolated process, it can't re-use an existing process.
            app = null;
        }
        …………
        if (app == null) {
            app = newProcessRecordLocked(info, processName, isolated);      //②创建一个ProcessRecord进程记录块,并保存在mProcessNames中
            if (app == null) {
                Slog.w(TAG, "Failed making new process record for "
                        + processName + "/" + info.uid + " isolated=" + isolated);
                return null;
            }
            mProcessNames.put(processName, app.uid, app);
            if (isolated) {
                mIsolatedProcesses.put(app.uid, app);
            }
        } else {
            // If this is a new package in the process, add the package to the list
            app.addPackage(info.packageName, mProcessStats);
        }
        ……………
        startProcessLocked(app, hostingType, hostingNameStr);       //③真正进程启动函数
        return (app.pid != 0) ? app : null;
    }

3.2 ActivityRecord 的创建

ActivityRecord 的创建比较简单,它是在 Activity 启动过程中的 startActivity 函数中被创建的

frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

private int startActivity(IApplicationThread caller, Intent intent,
      Intent ephemeralIntent, String resolvedType, ......) {
        ......
        ActivityRecord r = new ActivityRecord(mService, callerApp,
        callingPid, callingUid, callingPackage, intent, resolvedType, 
        aInfo, mService.getGlobalConfiguration(), resultRecord, 
        resultWho, requestCode, componentSpecified, 
        voiceSession != null, mSupervisor, options, sourceRecord);
        ......
}

3.3 ActivityDisplay,ActivityStack,TaskRecord 的创建

我们主要看一下 ActivityDisplay,ActivityStack 和 TaskRecord 的创建和联系过程,这个过程相对繁琐,我们先把时序图列出来,然后跟着时序图来分析代码。

frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

private int startActivityUnchecked(final ActivityRecord r, 
    ActivityRecord sourceRecord, ......) {
        ......
        final TaskRecord taskToAffiliate = 
            (mLaunchTaskBehind && mSourceRecord != null)
                ? mSourceRecord.getTask() : null;
        // Should this be considered a new task?
        int result = START_SUCCESS;
        if (mStartActivity.resultTo == null && mInTask == null &&
         !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
           ......
            // 开始为 ActivityRecord 配置对应的 TaskRecord 和 ActivityStack
            result = setTaskFromReuseOrCreateNewTask(
                    taskToAffiliate, preferredLaunchStackId, topStack);
        } else {
            ......
        }
        ......
}

接下来我们看下 TaskRecord 和 ActivityStack 的创建和关联过程,其实整个 Activity 这些重要结构体的创建以及对应关系的建立比较复杂,它有很多情况,对应不同的路径,我们这里只是选择了一种情况来进行分析。

private ActivityStack mTargetStack;

private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate,
        int preferredLaunchStackId, ActivityStack topStack) {
        // 1.创建获取 ActivityStack
        mTargetStack = computeStackFocus(
                mStartActivity, true, mLaunchBounds, mLaunchFlags, mOptions);
        // Do no move the target stack to front yet, as we might bail if
        // isLockTaskModeViolation fails below.
        if (mReuseTask == null) {
            // 2.创建 TaskRecord
            final TaskRecord task = mTargetStack.createTaskRecord(
                mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
                mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                ........);
            // 3.将 ActivityRecord 添加到 TaskRecord 中
            addOrReparentStartingActivity(task, 
                    "setTaskFromReuseOrCreateNewTask - mReuseTask");
            if (mLaunchBounds != null) {
                ......
            }
            ......
        } else {
            addOrReparentStartingActivity(mReuseTask, 
                    "setTaskFromReuseOrCreateNewTask");
        }
        ......
        if (mDoResume) {
            // 4.将当前要显示的 ActivityStack 移到栈顶
            mTargetStack.moveToFront("reuseOrNewTask");
        }
        return START_SUCCESS;
}

我们可以看到 setTaskFromReuseOrCreateNewTask 中主要做了以下 4 件事情:

  • 为已创建的 ActivityRecord 寻找创建 ActivityStack
  • 创建 TaskRecord
  • 将 ActivityRecord 添加到 TaskRecord 中
  • 将 ActivityStack 移到栈顶准备显示

这样,这几个重要的数据结构,就像我们上面列出的它们之间的关系图一样,建立起来了层层包含的关系。下面我们来具体分析它们。

3.3.1 ActivityStack 创建

frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask,
    Rect bounds, int launchFlags, ActivityOptions aOptions) {
        final TaskRecord task = r.getTask();
        ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
        if (stack != null) {
            return stack;
        }
        ......
}

这里调用了 RootActivityContainer 的 getLaunchStack 函数

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

<T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
     @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask,
     boolean onTop, @Nullable LaunchParamsController.LaunchParams launchParams, 
     int realCallingPid, int realCallingUid) {
     
        int taskId = INVALID_TASK_ID;
        int displayId = INVALID_DISPLAY;
        // Rect bounds = null;
        // We give preference to the launch preference in activity options.
        if (options != null) {
            taskId = options.getLaunchTaskId();
            displayId = options.getLaunchDisplayId();
        }
        ......
        // Checking if the activity's launch caller, or the realCallerId of
        // the activity from start request (i.e. PendingIntent caller)
        // is allowed to launch on the display.
        if (displayId != INVALID_DISPLAY && (canLaunchOnDisplay(r, displayId)
                || canLaunchOnDisplayFromStartRequest)) {
            if (r != null) {
                // get 合适的 Activitystack,如果存在直接返回
                stack = (T) getValidLaunchStackOnDisplay(displayId, r,
                candidateTask, options, launchParams);
                if (stack != null) {
                    return stack;
                }
            }
            // 根据 displayId 获取 ActivityDisplay 对象
            final ActivityDisplay display = 
                getActivityDisplayOrCreate(displayId);
            if (display != null) {
                // 根据 ActivityDisplay 创建 ActivityStack
                stack = display.getOrCreateStack(r, options, 
                candidateTask, activityType, onTop);
                if (stack != null) {
                    return stack;
                }
            }
        }
        ......
}

根据 displayId 获取对应的 ActivityDisplay 对象,然后通过这个 ActivityDisplay 对象来创建 ActivityStack。

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

<T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r,
     @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask,
     int activityType, boolean onTop) {
        // First preference is the windowing mode in the 
        // activity options if set.
        int windowingMode = (options != null)
                ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
        
        windowingMode = validateWindowingMode(windowingMode, r, 
                candidateTask, activityType);
        return getOrCreateStack(windowingMode, activityType, onTop);
    }
    
<T extends ActivityStack> T getOrCreateStack(int windowingMode, 
        int activityType, boolean onTop) {
        if (!alwaysCreateStack(windowingMode, activityType)) {
            T stack = getStack(windowingMode, activityType);
            if (stack != null) {
                return stack;
            }
        }
        return createStack(windowingMode, activityType, onTop);
    }

终于看到了 createStack 函数,看名字感觉离最终 ActivityStack 的创建已经不远。

<T extends ActivityStack> T createStack(int windowingMode, int activityType,
        boolean onTop) {
        ......
        final int stackId = getNextStackId();
        return createStackUnchecked(windowingMode, activityType, 
                stackId, onTop);
    }

获取下一个 stackId,继续调用 createStackUnchecked 完成 ActivityStack 的创建

<T extends ActivityStack> T createStackUnchecked(int windowingMode,
    int activityType, int stackId, boolean onTop) {
        if (windowingMode == WINDOWING_MODE_PINNED && activityType !=
             ACTIVITY_TYPE_STANDARD) {
    throw new IllegalArgumentException("Stack with windowing mode  "
                    + "cannot with non standard activity type.");
        }
        return (T) new ActivityStack(this, stackId,
           mRootActivityContainer.mStackSupervisor, windowingMode,
           activityType, onTop);
    }

终于完成了 ActivityStack 的创建。而且我们会发现 ActivityStack 是由 ActivityDisplay 创建的,至此 ActivityRecord 和 ActivityStack 都已经创建完成,我们接着回到 setTaskFromReuseOrCreateNewTask 函数去看下 TaskRecord 的创建

3.3.2 TaskRecord创建

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

TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
     IVoiceInteractionSession voiceSession, ......) {
        return createTaskRecord(taskId, info, intent, voiceSession,
           voiceInteractor, toTop, null /*activity*/, null /*source*/, null);
}

TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
            IVoiceInteractionSession voiceSession, ......) {
        // 创建 TaskRecord
        final TaskRecord task = TaskRecord.create(
                mService, taskId, info, intent, voiceSession, voiceInteractor);
        // add the task to stack first, mTaskPositioner might need 
        // the stack association
        // 新建的 TaskRecord 插入到了 ActivityStack 中 mTaskHistory 中,
        // 将 TaskRecord 和 ActivityStack 关联在了一起。
        addTask(task, toTop, "createTaskRecord");
        final int displayId = mDisplayId != 
            INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY;
        ......
        task.createTask(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
        return task;
    }

到这我们完成了 TaskRecord 的创建,并且将 TaskRecord 与 ActivityStack 建立起了联系。我们继续回到 setTaskFromReuseOrCreateNewTask 接着执行第 3 步操作。

3.3.3 ActivityRecord 与 TaskRecord 关联
private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
        if (mStartActivity.getTaskRecord() == null || 
              mStartActivity.getTaskRecord() == parent) {
        // 将当前 Activity 插入到 TaskRecord 中的 mActivities 的顶部,
        // 建立 ActivityRecord 与 TaskRecord 的联系
            parent.addActivityToTop(mStartActivity);
        } else {
            mStartActivity.reparent(parent, 
                  parent.mActivities.size() /* top */, reason);
        }
} 

最后调用 moveToFront,将 stack 移到栈的顶部。

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
欢迎使用 PHP MYSQL数据库管理系统-AMS-易用安全高效的MYSQL管理系统。 MYSQL管理系统(AMS) 1.5.0107 更新内容: 1、增加MySQL用户权限管理插件。 (Loc用户反馈: jumpsky) 2、提高数据查询响应速度。(CSDN用户反馈: xiachao2008) 3、进一步增强密码验证安全。 4、增加SQL记录、数据结构改进。 (PPC用户反馈: 冯.于安) 5、增加左栏数据库列表显示/关闭插件。 6、增加SQL查询日志。 (用户反馈: Winter) 7、增加数据页码下拉选择列表。 8、增加数据页码直接跳转。 9、增加数据键盘左右按键翻页。 10、增加数据索引字段排序。 11、增加点击查阅全部数据。 12、增加数据列表内容显示长度截取设置选项。 13、增加数据计数缓存设置选项。 14、增加计数精确查询设置选项。 15、增加Sql查询记录行数设置选项。 16、数据库列表增加数据表数量与字符集显示。 17、多字段索引设置问题。 18、导航宽度适应改进。 19、登录超时时间单位语言文字。 20、字符集校对排序调整、AI使用全称 (CSDN用户反馈:helloyou0) 21、增加登录出错报错信息。 22、改进数据表格闪动问题。 23、Loading提示丢失问题。 Amysql - AMS简介 01) 高效: 应用AMP-PHP框架与AMF-JS后台框架开发,支持同时运行多个数据库、数据表窗口、支持自定义扩展插件等。 02) 强大: 强大的多行SQL执行能力,支持一次执行多行SQL并多窗口完美表现各行SQL的运行结果。 03) 高亮: 支持SQL高亮显示,有效防止书写SQL时错写库表名、字段名与及MYSQL相关关键字。 04) 扩展: 实现版块以JS扩展形式预载,数据库列表、数据请求发送等以JSON格式传输,无刷新快速呈现。 05) 易用: 支持版块右键菜单操作与快捷键响应,支持鼠标划拉选择或Shift按键选择多行数据,使用简单、操作快速方便。 06) 友好: 友好的数据分页展现,解决PHPMYADMIN SQL LIMIT 10000或关联查询数据量过大加载显示不佳问题。 07) 其它: 支持用户扩展开发、良好架构,简单安全易用,等……
欢迎使用 PHP MYSQL数据库管理系统 - AMS AMS-易用安全高效的MYSQL管理系统。 Amysql - AMS简介 01) 高效: 应用AMP-PHP框架与AMF-JS后台框架开发,支持同时运行多个数据库、数据表窗口、支持自定义扩展插件等。 02) 强大: 强大的多行SQL执行能力,支持一次执行多行SQL并多窗口完美表现各行SQL的运行结果。 03) 高亮: 支持SQL高亮显示,有效防止书写SQL时错写库表名、字段名与及MYSQL相关关键字。 04) 扩展: 实现版块以JS扩展形式预载,数据库列表、数据请求发送等以JSON格式传输,无刷新快速呈现。 05) 易用: 支持版块右键菜单操作与快捷键响应,支持鼠标划拉选择或Shift按键选择多行数据,使用简单、操作快速方便。 06) 友好: 友好的数据分页展现,解决PHPMYADMIN SQL LIMIT 10000或关联查询数据量过大加载显示不佳问题。 07) 其它: 支持用户扩展开发、良好架构,简单安全易用,等   AMS 1.5.0107更新: 1、增加MySQL用户权限管理插件。 (Loc用户反馈: jumpsky) 2、提高数据查询响应速度。(CSDN用户反馈: xiachao2008) 3、进一步增强密码验证安全。 4、增加SQL记录、数据结构改进。 (PPC用户反馈: 冯.于安) 5、增加左栏数据库列表显示/关闭插件。 6、增加SQL查询日志。 (用户反馈: Winter) 7、增加数据页码下拉选择列表。 8、增加数据页码直接跳转。 9、增加数据键盘左右按键翻页。 10、增加数据索引字段排序。 11、增加点击查阅全部数据。 12、增加数据列表内容显示长度截取设置选项。 13、增加数据计数缓存设置选项。 14、增加计数精确查询设置选项。 15、增加Sql查询记录行数设置选项。 16、数据库列表增加数据表数量与字符集显示。 17、多字段索引设置问题。 18、导航宽度适应改进。 19、登录超时时间单位语言文字。 20、字符集校对排序调整、AI使用全称 (CSDN用户反馈:helloyou0) 21、增加登录出错报错信息。 22、改进数据表格闪动问题。 23、Loading提示丢失问题。
一、系统需求 1、登陆 功能描述 此模块提供用户登录 主要功能点 登陆系统 名称 用户登陆 操作角色 普通员工,资产管理员,财务人员,人事管理员 功能描述 更具不同的登录账号,判断人员身份,进入对应的功能页面 操作 1.输入帐号和密码,点击登陆 2.登陆成功进入用户身份类型对应的页面 输出 与身份对应的功能主页面 2、修改登陆密码 功能描述 此模块提供用户在登陆系统之后,可以修改自己的登陆密码. 主要功能点 修改登陆密码 名称 修改密码 操作角色 普通员工,资产管理员,财务人员,人事管理员 功能描述 修改网站登陆密码 操作 1.输入原始密码 2.输入新密码 3.输入确认密码 4.点击修改密码 输出 修改成功或者失败均需要提示 3、修改信息 功能描述 此模块提供用户在登陆系统之后,可以修改自己的个人信息 主要功能点 修改个人信息- 名称 修改密码 操作角色 普通员工,资产管理员,财务人员,人事管理员 功能描述 对个人信息进行更新 操作 1.输入修改信息 2.点击确认 输出 修改成功或者失败的提示 4、管理普通用户 功能描述 此模块提供人事管理员对普通用户的管理 主要功能点 修改个人信息- 名称 用户管理 操作角色 人事管理员 功能描述 对普通用户进行信息的修改,以及普通用户添加和删除 操作 1.输出要操作的用户的信息 2.点击查询显示结果(默认显示所有用户) a)查询结果显示用户的所有个人信息i b)要求对用户的管理操作有修改信息,和删除用户 c)在每条信息后显示对应的操作按钮,以便直接进行管理 输出 5、采购入库 功能描述 此模块提供资产管理人员添加采购单,校对采购单并入库的操作 需要记录信息 采购信息:采购单编号 产品序列号 地区 县/市 资产类别 资产名称 规格型号 制造商 供应商 保修年限 使用年限 单位 数量 单价 保管人 是否处理 备注 条形码 显示信息:所有已经提交的采购单信息,入库信息 主要功能点 名称 采购入库 操作角色 资产管理员 功能描述 添加采购单,校对入库 操作 1.添加采购单 A)输入采购信息 B)点击提交 C)显示已添加的采购单 2.校对入库 A)校对采购单 B)点击入库 C)显示校对信息 输出 采购单是否添加成功,校对是否完成 6、财务入账 功能描述 此模块提供财务管理人员对财务信息进行导入 需要提供的信息:记录财务信息的EXCEL表格 主要功能点 名称 财务入账 操作角色 财务管理人员 功能描述 将财务信息从EXCEL表格中导入系统 操作 1.进入财务管理页面,点击导入按钮 2.选择要导入的EXCEL表格 3.点击确定导入,保存旅客订票信息,并提示订票成功或者失败 4.显示导入的信息 输出 导入成功与否的提示信息 7、资产卡片 功能描述 此模块提供资产管理人员查看资产的详细信息 主要功能点 名称 显示资产卡片 操作角色 资产管理人员 功能描述 资产管理人员查看资产的所有信息 操作 1.进入资产信息页面 2.点击资产卡片 3.显示资产所有详细的信息 输出 导入成功与否的提示信息 8、资产领用 功能描述 此模块提供对于闲置的设备用户可以根据业务的需要进行领用 主要功能点 名称 资产领用 操作角色 普通员工,资产管理员,财务人员 功能描述 提供对于闲置的设备用户可以根据业务的需要进行领用 操作 1.点击进入资产领用页面 2.显示所有可领用信息 3.点击选择需要领用的资源 4.填写领用信息 5.提示领用是否成功 输出 领用是否成功的提示 9、资产归还 功能描述 此模块提供用户可以根据业务需要可以归还设备。 主要功能点 名称 资产归还 操作角色 普通员工,资产管理员,财务人员 功能描述 归还领用设备 操作 1.点击进入资产归还页面 2.显示所有目前已领用的信息 3.点击选择需要归还的资源 5.提示归还是否成功 输出 归还是否成功的提示 10、资产调拨 功能描述 此模块提供资产管理员和资产保管员对于闲置的资产可以进行调配调拨。 主要功能点 名称 资产调拨 操作角色 资产管理员,普通员工 功能描述 资产管理员发布调拨信息,普通员工调出自己所需的资产 操作 资产管理员 1.进入资产调拨页面 2.点击发布调拨信息 3.填写调拨信息 4.提交信息 普通用户 1. 进入资产调拨页面 2. 显示发布的调拨信息 3. 点击需要调入的资产 4. 提示是否调入成功 输出 资产调出是否成功的提示 二、数据结构与程序的关系 采购单: Orders 名称 字段名 数据类型 备注 采购单编号 oid int(11) 主键 产品序列号 pno, varchar(255) Default not NULL: 地区 region varchar(255) Default NULL 县/市 city varc

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值