ActivityManagerService

ActivityManagerService

八归少年 55 2024-12-12

ActivityManagerService简称AMS,它是Android最核心的系统服务之一,AMS 是引导服务,AMS是Android进程管理和调度中心,负责应用进程的启动、切换和调度,以及四大组件的启动和管理,组件的状态管理和查询。可参考四大组件工作过程。AMS逻辑复杂,有一些类帮助它完成相关逻辑,它们统称为AMS家族。

AMS启动过程

SystemServer 进程启动过程中,我们通过分析发现AMS是被SystemServer进程启动的。

public final class SystemServer implements Dumpable {
    private SystemServiceManager mSystemServiceManager;
    public static void main(String[] args) {
        new SystemServer().run();
    }
   private void run() {
    ...
    try {
            t.traceBegin("StartServices");
            //启动引导服务
            startBootstrapServices(t);
            //启动核心服务
            startCoreServices(t);
            //启动其它服务
            startOtherServices(t);
            //启动Apex服务
            startApexServices(t);
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            t.traceEnd(); // StartServices
        }
}
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
        // Activity manager runs the show.
        t.traceBegin("StartActivityManager");
        // TODO: Might need to move after migration to WM.
        ActivityTaskManagerService atm = mSystemServiceManager.startService(
                ActivityTaskManagerService.Lifecycle.class).getService();
        mActivityManagerService = ActivityManagerService.Lifecycle.startService(
                mSystemServiceManager, atm);
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
        mWindowManagerGlobalLock = atm.getGlobalLock();
        t.traceEnd();    
}
}

ActivityTaskManagerService

ActivityTaskManagerService是用于管理Activity及其容器(任务、显示等)的系统服务。

SystemServiceManager的startService方法会调用Lifecycle的onStart方法来启动ActivityTaskManagerService.

//frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
     final ActivityTaskManagerInternal mInternal;
    public static final class Lifecycle extends SystemService {
        private final ActivityTaskManagerService mService;
        public Lifecycle(Context context) {
            super(context);
            mService = new ActivityTaskManagerService(context);
        }
        @Override
        public void onStart() {
            publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
            mService.start();
        }
        public ActivityTaskManagerService getService() {
            return mService;
        }
    }
    private void start() {
        LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
    }
}

ActivityManagerService

调用静态内部类LifeCycle的startService方法,接着调用SystemServiceManager类的startService方法

public static final class Lifecycle extends SystemService {
        private final ActivityManagerService mService;
        private static ActivityTaskManagerService sAtm;
        public Lifecycle(Context context) {
            super(context);
            mService = new ActivityManagerService(context, sAtm);
        }

        public static ActivityManagerService startService(
                SystemServiceManager ssm, ActivityTaskManagerService atm) {
            sAtm = atm;
            return ssm.startService(ActivityManagerService.Lifecycle.class).getService();
        }
        @Override
        public void onStart() {
            mService.start();
        }
        @Override
        public void onBootPhase(int phase) {
            mService.mBootPhase = phase;
            if (phase == PHASE_SYSTEM_SERVICES_READY) {
                mService.mBatteryStatsService.systemServicesReady();
                mService.mServices.systemServicesReady();
            } else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
                mService.startBroadcastObservers();
            } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                mService.mPackageWatchdog.onPackagesReady();
            }
        }
        @Override
        public void onUserStopped(@NonNull TargetUser user) {
            mService.mBatteryStatsService.onCleanupUser(user.getUserIdentifier());
        }
        public ActivityManagerService getService() {
            return mService;
        }
    }
 private void start() {
        mBatteryStatsService.publish();
        mAppOpsService.publish();
        mProcessStats.publish();
        Slog.d("AppOps", "AppOpsService published");
        LocalServices.addService(ActivityManagerInternal.class, mInternal);
        LocalManagerRegistry.addManager(ActivityManagerLocal.class,
                (ActivityManagerLocal) mInternal);
        mActivityTaskManager.onActivityManagerInternalAdded();
        mPendingIntentController.onActivityManagerInternalAdded();
        mAppProfiler.onActivityManagerInternalAdded();
        CriticalEventLog.init();
    }

startService方法中创建AMS,mServices中添加AMS,最后又回到ActivityManagerService启动AMS。

//frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
            Slog.i(TAG, "Starting " + name);
            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);

            // Create the service.
            if (!SystemService.class.isAssignableFrom(serviceClass)) {
                throw new RuntimeException("Failed to create " + name
                        + ": service must extend " + SystemService.class.getName());
            }
            final T service;
            try {
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            } catch (InstantiationException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service could not be instantiated", ex);
            } catch (IllegalAccessException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service must have a public constructor with a Context argument", ex);
            } catch (NoSuchMethodException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service must have a public constructor with a Context argument", ex);
            } catch (InvocationTargetException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service constructor threw an exception", ex);
            }

            startService(service);
            return service;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }
public void startService(@NonNull final SystemService service) {
        // Check if already started
        String className = service.getClass().getName();
        if (mServiceClassnames.contains(className)) {
            Slog.i(TAG, "Not starting an already started service " + className);
            return;
        }
        mServiceClassnames.add(className);

        // Register it.
        mServices.add(service);

        // Start it.
        long time = SystemClock.elapsedRealtime();
        try {
            service.onStart();
        } catch (RuntimeException ex) {
            throw new RuntimeException("Failed to start service " + service.getClass().getName()
                    + ": onStart threw an exception", ex);
        }
        warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
    }

数据结构

ActivityRecord

它内部记录了Activity的所有信息,被用来描述一个Activity,它是在启动Activity时被创建的,具体是在ActivityStarter的executeRequest方法中创建的。ActivityStartController是ActivityStarter的控制类,在activity启动过程文章中也提到了这部分,入口是ActivityTaskManagerService的startActivityAsUser方法。

//frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
private int executeRequest(Request request) {
    final ActivityRecord r = new ActivityRecord.Builder(mService)
                .setCaller(callerApp)
                .setLaunchedFromPid(callingPid)
                .setLaunchedFromUid(callingUid)
                .setLaunchedFromPackage(callingPackage)
                .setLaunchedFromFeature(callingFeatureId)
                .setIntent(intent)
                .setResolvedType(resolvedType)
                .setActivityInfo(aInfo)
                .setConfiguration(mService.getGlobalConfiguration())
                .setResultTo(resultRecord)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setComponentSpecified(request.componentSpecified)
                .setRootVoiceInteraction(voiceSession != null)
                .setActivityOptions(checkedOptions)
                .setSourceRecord(sourceRecord)
                .build();
}
//frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java
ActivityStartController(ActivityTaskManagerService service) {
        this(service, service.mTaskSupervisor,
             //工厂模式
                new DefaultFactory(service, service.mTaskSupervisor,
                    new ActivityStartInterceptor(service, service.mTaskSupervisor)));
    }
@VisibleForTesting
ActivityStartController(ActivityTaskManagerService service, ActivityTaskSupervisor supervisor,
            Factory factory) {
        mService = service;
        mSupervisor = supervisor;
        mFactory = factory;
        mFactory.setController(this);
        mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service.mGlobalLock,
                service.mH);
        mBalController = new BackgroundActivityStartController(mService, mSupervisor);
    }
/**
 * @return A starter to configure and execute starting an activity. It is valid until after
 *         {@link ActivityStarter#execute} is invoked. At that point, the starter should be
 *         considered invalid and no longer modified or used.
 */
ActivityStarter obtainStarter(Intent intent, String reason) {
        return mFactory.obtain().setIntent(intent).setReason(reason);
    }

从参数可以可以看出,内部存储了ATMS的引用,AndroidManifest的节点信息、Activity资源信息和进程相关信息等。

final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {
    //构造者模式
    static class Builder {
        private final ActivityTaskManagerService mAtmService;
        private WindowProcessController mCallerApp;
        private int mLaunchedFromPid;
        private int mLaunchedFromUid;
        private String mLaunchedFromPackage;
        private String mLaunchedFromFeature;
        private Intent mIntent;
        private String mResolvedType;
        //代码中和AndroidManifest设置的节点信息
        private ActivityInfo mActivityInfo;
        private Configuration mConfiguration;
        private ActivityRecord mResultTo;
        private String mResultWho;
        private int mRequestCode;
        private boolean mComponentSpecified;
        private boolean mRootVoiceInteraction;
        private ActivityOptions mOptions;
        private ActivityRecord mSourceRecord;
        private PersistableBundle mPersistentState;
        private TaskDescription mTaskDescription;
        private long mCreateTime;
        ActivityRecord build() {
            if (mConfiguration == null) {
                mConfiguration = mAtmService.getConfiguration();
            }
            return new ActivityRecord(mAtmService, mCallerApp, mLaunchedFromPid,
                    mLaunchedFromUid, mLaunchedFromPackage, mLaunchedFromFeature, mIntent,
                    mResolvedType, mActivityInfo, mConfiguration, mResultTo, mResultWho,
                    mRequestCode, mComponentSpecified, mRootVoiceInteraction,
                    mAtmService.mTaskSupervisor, mOptions, mSourceRecord, mPersistentState,
                    mTaskDescription, mCreateTime);
        }
    }
}

TaskRecord

它用来描述一个Activity任务栈,

ActivityStack

ActivityStack是一个管理类,用来管理所有Activity,其内部维护了Activity的所有状态、特殊状态的Activity以及Activity相关的列表等数据。ActivityStack是由ActivityStackSupervisor来进行管理,

Task

Task中可以包含多个ActivityRecord,并且调整ActivityRecord的显示位置。

Task 是一个 TaskFragment,可以包含一组执行特定任务的Activity。具有相同任务亲和力的Activity通常分组在同一个 Task 中。Task 也可以是显示在“最近”屏幕中与用户交互的任务的实体。Task 还可以包含其他 Task。

//frameworks/base/services/core/java/com/android/server/wm/Task.java
/**
 * {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job.
 * Activities of the same task affinities usually group in the same {@link Task}. A {@link Task}
 * can also be an entity that showing in the Recents Screen for a job that user interacted with.
 * A {@link Task} can also contain other {@link Task}s.
 */
class Task extends TaskFragment {
}

TaskFragment

一个基本容器,可用于包含Activity或其他 TaskFragment,它还能够管理Activity生命周期并更新其中Activity的可见性。

//frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java
/**
 * A basic container that can be used to contain activities or other {@link TaskFragment}, which
 * also able to manage the activity lifecycle and updates the visibilities of the activities in it.
 */
class TaskFragment extends WindowContainer<WindowContainer> {    
}

TaskDisplayArea

TaskDisplayArea对应一个DisplayContent,负责管理屏幕里面的各种Task栈,包括调整各个Task的显示的位置。

DisplayArea 表示包含应用窗口容器的屏幕部分。子项可以是 Task 或 TaskDisplayArea。

//frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java
/**
 * {@link DisplayArea} that represents a section of a screen that contains app window containers.
 *
 * The children can be either {@link Task} or {@link TaskDisplayArea}.
 */
final class TaskDisplayArea extends DisplayArea<WindowContainer> {
    DisplayContent mDisplayContent; //对应屏幕
     privates Task mRootHomeTak;//Home栈
     private Task mRootPinnedTask;//画中画栈
     Task mLastFocusedRootTask;//最近获得焦点的RootTask
    private RootWindowContainer mRootWindowContainer;//屏幕管理
}

DisplayContent

DisplayContent 代表的是一块屏幕,其被RootWindowContainer所管理,该显示屏上的窗口以栈的形式管理,DisplayContent有四种形式的container,分别用来存储不同类型的窗口:应用程序窗口,系统类型窗口,输入法窗口和壁纸窗口,在addWindowToken中决定添加到那个Container中。

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
/**
 * Utility class for keeping track of the WindowStates and other pertinent contents of a
 * particular Display.
 */
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {
     final int mDisplayId;//屏幕ID
    final Display mDisplay;//对应物理屏幕
    private final DisplayInfo mDisplayInfo = new DisplayInfo();//屏幕信息
    private final DisplayRotation mDisplayRotation;//屏幕转向控制
}

RootWindowContainer

它是窗口容器的根容器,子容器是DisplayContent。RootWindowContainer的初始化是在WindowManagerService中完成的。主要用来管理显示屏幕,其关联一组DisplayContent。

//frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
/** Root {@link WindowContainer} for the device. */
class RootWindowContainer extends WindowContainer<DisplayContent>
        implements DisplayManager.DisplayListener {}

组件管理

Activity管理

Activity状态
//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
public final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {
    enum State {
        INITIALIZING,//正在初始化
        STARTED,//启动状态
        RESUMED,//恢复状态
        PAUSING,//正在暂停
        PAUSED,//暂停状态
        STOPPING,//正在停止
        STOPPED,//停止状态
        FINISHING,//正在结束
        DESTROYING,//正在销毁
        DESTROYED,//销毁状态
        RESTARTING_PROCESS//正在重启
    }
}
Activity类型
//frameworks/base/core/java/android/app/WindowConfiguration.java
public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> {
    private @ActivityType int mActivityType;

    /** Activity type is currently not defined. */
    public static final int ACTIVITY_TYPE_UNDEFINED = 0;
    /** Standard activity type. Nothing special about the activity... */
    public static final int ACTIVITY_TYPE_STANDARD = 1;
    /** Home/Launcher activity type. */
    public static final int ACTIVITY_TYPE_HOME = 2;
    /** Recents/Overview activity type. There is only one activity with this type in the system. */
    public static final int ACTIVITY_TYPE_RECENTS = 3;
    /** Assistant activity type. */
    public static final int ACTIVITY_TYPE_ASSISTANT = 4;
    /** Dream activity type. */
    public static final int ACTIVITY_TYPE_DREAM = 5;

    /** @hide */
    @IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
            ACTIVITY_TYPE_UNDEFINED,//未定义类型
            ACTIVITY_TYPE_STANDARD,//标准类型
            ACTIVITY_TYPE_HOME,//Home类型
            ACTIVITY_TYPE_RECENTS,//最近认为类型
            ACTIVITY_TYPE_ASSISTANT,//助手类型
            ACTIVITY_TYPE_DREAM,//VR类型
    })
    public @interface ActivityType {}
} 

AMS可能管理多个屏幕设备,由RootWindowContainer管理。每个屏幕在AMS中对应一个DisplayContent的数据结构。一个屏幕由一个TaskDisplayArea管理里面的Task,一个Task里可能会有多个ActivityRecord。

Service管理

Service工作流程文章之前有提到过,ServiceRecord记录了AMS中某一个运行的Service的信息,是AMS管理Service的基本单位,一个Service可以被其它进程用不同的Intent来绑定,使用同一个Intent绑定的Service可能是不同的进程,所以会有多个Connection连接。

//frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java
final class ServiceRecord extends Binder implements ComponentName.WithComponentName {
    final ServiceInfo serviceInfo; //manifest中Service的信息
    boolean isForeground;//当前service是否是一个前台进程
     boolean startRequested;//service是否由startService启动
}

ActiveServices是AMS中负责管理Service的类,mServiceMap是ServiceMap类型,ServiceMap中保存了运行的Service信息ServiceRecord,有两种保存形式,一种是CompomentName,一种是Intent。

public final class ActiveServices {
    final SparseArray<ServiceMap> mServiceMap = new SparseArray<>();
}

Broadcast管理

在AMS中,BroadcastReceiver的过滤条件由BroadcastFilter表示,该类从IntentFilter派生,由于一个BroadcastReceiver可以设置多个过滤条件,因此AMS使用ReceiverList来表达一对多的关系。

AMS提供mRegisteredReceivers用于保存IIntentReceiver和对应ReceiverList的关系。mReceiverResolver存储所有动态注册的BroadcastReceiver设置的过滤条件。

//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
	final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
	final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
                = new IntentResolver<BroadcastFilter, BroadcastFilter>() {
        @Override
        protected boolean allowFilterResult(
                BroadcastFilter filter, List<BroadcastFilter> dest) {
            IBinder target = filter.receiverList.receiver.asBinder();
            for (int i = dest.size() - 1; i >= 0; i--) {
                if (dest.get(i).receiverList.receiver.asBinder() == target) {
                    return false;
                }
            }
            return true;
        }
        @Override
        protected BroadcastFilter newResult(@NonNull Computer computer, BroadcastFilter filter,
                int match, int userId, long customFlags) {
            if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
                    || userId == filter.owningUserId) {
                return super.newResult(computer, filter, match, userId, customFlags);
            }
            return null;
        }
        @Override
        protected IntentFilter getIntentFilter(@NonNull BroadcastFilter input) {
            return input;
        }
        @Override
        protected BroadcastFilter[] newArray(int size) {
            return new BroadcastFilter[size];
        }
        @Override
        protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
            return packageName.equals(filter.packageName);
        }
    };
    
}

进程管理

进程分类

Android系统中,进程主要分为以下几类:

  1. 前台进程(Foreground process)

    • 页面正在显示的Activity
    • 包含一个Service,并且该Service和一个显示的Activity绑定
    • 包含调用了startForeground的Service,或该进程Service正在调用其生命周期方法
    • 该进程有BroadcastReceiver的实例正在执行onReceive方法
  2. 可见进程(Visible process)

    进程拥有一个可见的但非前台的Activity(例如Activity处于onPause状态,但是仍然对用户可见,如弹出对话框)。

    当系统内存不足,且需要回收资源以供前台进程使用时,可能会杀死这些进程。

  3. 服务进程(Service process)

    进程中运行着已使用startService()方法启动的服务。

    尽管这些进程对用户不是直接可见的,但它们通常在执行一些用户关心的操作(如后台播放音乐或下载数据)。

  4. 后台进程(Background process)

    进程包含不可见的Activity(已调用onStop())。

    系统可能随时终止这些进程以回收内存,并且当内存充足时,通常保留它们的进程状态和内存数据,以便用户返回时能够快速恢复

  5. 空进程(Empty process)
    这类进程不包含任何活跃的应用组件。
    系统保留这类进程的目的是为了缓存,以便下次可以更快地启动应用。

系统经常终止这些进程以保持系统的整体性能。Android系统会根据进程中运行的组件及其状态来调整进程的优先级,当系统内存不足时,会按照上述分类从低优先级到高优先级依次清理进程。

管理

AMS是管理进程的核心模块,在AMS中,每个进程以一个记录的形式存在,即ProcessRecord

四大组件定义在AndroidManifest文件中,每一个都可以使用android:process=""来指定进程,同一个APP可以运行在多个进程,多个APP也可以共享一个进程。android:priority=""设置进程优先级,数值越大,进程优先级越高。

进程的创建在Activity启动过程中已经说明过,Zygote进程会fork应用进程。至于进程的退出,AMS会完成进程退出的善后工作,清理进程和进程组。

class ProcessRecord implements WindowProcessListener {
    //退出进程
     void killLocked(String reason, String description, @Reason int reasonCode,
            @SubReason int subReason, boolean noisy, boolean asyncKPG) {
        if (!mKilledByAm) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
            if (reasonCode == ApplicationExitInfo.REASON_ANR
                    && mErrorState.getAnrAnnotation() != null) {
                description = description + ": " + mErrorState.getAnrAnnotation();
            }
            if (mService != null && (noisy || info.uid == mService.mCurOomAdjUid)) {
                mService.reportUidInfoMessageLocked(TAG,
                        "Killing " + toShortString() + " (adj " + mState.getSetAdj()
                        + "): " + reason, info.uid);
            }
            // Since the process is getting killed, reset the freezable related state.
            mOptRecord.setPendingFreeze(false);
            mOptRecord.setFrozen(false);
            if (mPid > 0) {
                mService.mProcessList.noteAppKill(this, reasonCode, subReason, description);
                EventLog.writeEvent(EventLogTags.AM_KILL,
                        userId, mPid, processName, mState.getSetAdj(), reason, getRss(mPid));
                Process.killProcessQuiet(mPid);
                killProcessGroupIfNecessaryLocked(asyncKPG);
            } else {
                mPendingStart = false;
            }
            if (!mPersistent) {
                synchronized (mProcLock) {
                    mKilled = true;
                    mKilledByAm = true;
                    mKillTime = SystemClock.uptimeMillis();
                }
            }
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }
}

内存管理

Android操作系统的内存回收分为两部分,默认内存回收和内核级内存回收,在Activity生命周期切换时,会触发AMS的回收机制。

AMS中内存回收触发的几种场景如下:

  1. 调用startActivity,一般情况下,页面跳转时,会暂停当前Activity,在Activity启动过程中,我们知道会调用resumeTopActivity方法,期间会调用ActivityRecord的addToStopping释放资源。
//frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java
class TaskFragment extends WindowContainer<WindowContainer> {
   final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
            boolean skipPause) {
       ...
        boolean pausing = !skipPause && taskDisplayArea.pauseBackTasks(next);
        if (mResumedActivity != null) {
            ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity);
            pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,
                    next, "resumeTopActivity");
        }
   } 
    boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
            String reason) {
        if (mPausingActivity != null) {
            Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
                    + " state=" + mPausingActivity.getState());
            if (!shouldSleepActivities()) {
                // Avoid recursion among check for sleep and complete pause during sleeping.
                // Because activity will be paused immediately after resume, just let pause
                // be completed by the order of activity paused from clients.
                completePause(false, resuming);
            }
            ...
    }
     void completePause(boolean resumeNext, ActivityRecord resuming) {
         ActivityRecord prev = mPausingActivity;
           if (prev != null) {
            prev.setWillCloseOrEnterPip(false);
            final boolean wasStopping = prev.isState(STOPPING);
            prev.setState(PAUSED, "completePausedLocked");
            if (prev.finishing) {
                ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
                prev = prev.completeFinishing(false /* updateVisibility */,
                        "completePausedLocked");
            } else if (prev.attachedToProcess()) {
                ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
                                + "wasStopping=%b visibleRequested=%b",  prev,  wasStopping,
                        prev.isVisibleRequested());
                if (wasStopping) {
                    prev.setState(STOPPING, "completePausedLocked");
                } else if (!prev.isVisibleRequested() || shouldSleepOrShutDownActivities()) {
                    prev.setDeferHidingClient(false);
                    // If we were visible then resumeTopActivities will release resources before
                    // stopping.
                    prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
                            "completePauseLocked");
                }
            } else {
                ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
                prev = null;
            }
     }    
}

mStoppingActivities是保存pause的Activity列表,同时判断mStoppingActivities的Activity数量是否超过3个,超过则会清除这些Activity。调用ActivityTaskSupervisor类的scheduleIdle方法。

//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {
    final ActivityTaskSupervisor mTaskSupervisor;
    private static final int MAX_STOPPING_TO_FORCE = 3;
    void addToStopping(boolean scheduleIdle, boolean idleDelayed, String reason) {
         void addToStopping(boolean scheduleIdle, boolean idleDelayed, String reason) {
        if (!mTaskSupervisor.mStoppingActivities.contains(this)) {
            EventLogTags.writeWmAddToStopping(mUserId, System.identityHashCode(this),
                    shortComponentName, reason);
            mTaskSupervisor.mStoppingActivities.add(this);
        }
        final Task rootTask = getRootTask();
        // If we already have a few activities waiting to stop, then give up on things going idle
        // and start clearing them out. Or if r is the last of activity of the last task the root
        // task will be empty and must be cleared immediately.
        boolean forceIdle = mTaskSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
                || (isRootOfTask() && rootTask.getChildCount() <= 1);
        if (scheduleIdle || forceIdle) {
            ProtoLog.v(WM_DEBUG_STATES,
                    "Scheduling idle now: forceIdle=%b immediate=%b", forceIdle, !idleDelayed);

            if (!idleDelayed) {
                mTaskSupervisor.scheduleIdle();
            } else {
                mTaskSupervisor.scheduleIdleTimeout(this);
            }
        } else {
            rootTask.checkReadyForSleep();
        }
    }
}

ActivityTaskSupervisor类中发送IDLE_NOW_MSG消息处理内存,最终调用activityIdleInternal方法,第三种场景会继续分析。

//frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
    //规定10s启动Activity,否则释放相关资源
    private static final int IDLE_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
     void scheduleIdleTimeout(ActivityRecord next) {
        if (DEBUG_IDLE) Slog.d(TAG_IDLE, "scheduleIdleTimeout: Callers=" + Debug.getCallers(4));
        Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
        mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
    }

    final void scheduleIdle() {
        if (!mHandler.hasMessages(IDLE_NOW_MSG)) {
            if (DEBUG_IDLE) Slog.d(TAG_IDLE, "scheduleIdle: Callers=" + Debug.getCallers(4));
            mHandler.sendEmptyMessage(IDLE_NOW_MSG);
        }
    }
     private boolean handleMessageInner(Message msg) {
          switch (msg.what) {
                  case IDLE_TIMEOUT_MSG: {
                    if (DEBUG_IDLE) Slog.d(TAG_IDLE,
                            "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
                    // We don't at this point know if the activity is fullscreen, so we need to be
                    // conservative and assume it isn't.
                    activityIdleFromMessage((ActivityRecord) msg.obj, true /* fromTimeout */);
                } break;
                   case IDLE_NOW_MSG: {
                    if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
                    activityIdleFromMessage((ActivityRecord) msg.obj, false /* fromTimeout */);
                } break;
          }
     }
    private void activityIdleFromMessage(ActivityRecord idleActivity, boolean fromTimeout) {
            activityIdleInternal(idleActivity, fromTimeout,
                    fromTimeout /* processPausingActivities */, null /* config */);
        }
}
  1. 用户按back按键,退出应用程序。
  2. 当Activity启动后,进入onResume生命周期时,会向AMS发送一个Idle信息,这会触发AMS执行ActivityClient类的activityIdle方法,源码如下:
//frameworks/base/core/java/android/app/ActivityThread.java
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
    @Override
    public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
            boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
        ...
        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
        Looper.myQueue().addIdleHandler(new Idler());
    }
      private class Idler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            boolean stopProfiling = false;
            if (mBoundApplication != null && mProfiler.profileFd != null
                    && mProfiler.autoStopProfiler) {
                stopProfiling = true;
            }
            final ActivityClient ac = ActivityClient.getInstance();
            while (mNewActivities.size() > 0) {
                final ActivityClientRecord a = mNewActivities.remove(0);
                if (localLOGV) {
                    Slog.v(TAG, "Reporting idle of " + a + " finished="
                            + (a.activity != null && a.activity.mFinished));
                }
                if (a.activity != null && !a.activity.mFinished) {
                    ac.activityIdle(a.token, a.createdConfig, stopProfiling);
                    a.createdConfig = null;
                }
            }
            if (stopProfiling) {
                mProfiler.stopProfiling();
            }
            return false;
        }
}
//frameworks/base/core/java/android/app/ActivityClient.java
public class ActivityClient {
     public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        try {
            getActivityClientController().activityIdle(token, config, stopProfiling);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }
     private static IActivityClientController getActivityClientController() {
        final IActivityClientController controller = INTERFACE_SINGLETON.mKnownInstance;
        return controller != null ? controller : INTERFACE_SINGLETON.get();
    }
}

它的实现在ActivityClientController中,最终还是调用ActivityTaskSupervisor类的activityIdleInternal方法,和第一种场景一致。

//frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java
class ActivityClientController extends IActivityClientController.Stub {
    private final ActivityTaskSupervisor mTaskSupervisor;
   @Override
    public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
                if (r == null) {
                    return;
                }
                mTaskSupervisor.activityIdleInternal(r, false /* fromTimeout */,
                        false /* processPausingActivities */, config);
                if (stopProfiling && r.hasProcess()) {
                    r.app.clearProfilerIfNeeded();
                }
            }
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            Binder.restoreCallingIdentity(origId);
        } 
}

首先调用ATMS的scheduleAppGcsLocked方法通知所有需要回收内存的进程进行内存回收。processStoppingAndFinishingActivities方法主要是取出mStoppingActivities的Activity,并存放到readyToStopActivities列表中,清空了mStoppingActivities列表,然后判断readyToStopActivities中是否在堆栈中,然后在判断是否销毁,是则执行destory方法,否则执行stop方法进行停止。最后ATMS的的handler发送了一条信息。

//frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
    final ActivityTaskManagerService mService;
     void activityIdleInternal(ActivityRecord r, boolean fromTimeout,
            boolean processPausingActivities, Configuration config) {
         ...
         if (mRootWindowContainer.allResumedActivitiesIdle()) {
            if (r != null) {
                mService.scheduleAppGcsLocked();
                mRecentTasks.onActivityIdle(r);
            }
             ...
        }
          // Atomically retrieve all of the other things to do.
        processStoppingAndFinishingActivities(r, processPausingActivities, "idle");
        ...
         mService.mH.post(() -> mService.mAmInternal.trimApplications());
     }
     void processStoppingAndFinishingActivities(ActivityRecord launchedActivity,
            boolean processPausingActivities, String reason) {
          for (int i = 0; i < mStoppingActivities.size(); i++) {
            final ActivityRecord s = mStoppingActivities.get(i);
              ...
             if (readyToStopActivities == null) {
                    readyToStopActivities = new ArrayList<>();
                }
                readyToStopActivities.add(s);
                mStoppingActivities.remove(i);
                i--;
          }
         ...
         final int numReadyStops = readyToStopActivities == null ? 0 : readyToStopActivities.size();
        for (int i = 0; i < numReadyStops; i++) {
            final ActivityRecord r = readyToStopActivities.get(i);
            if (r.isInHistory()) {
                if (r.finishing) {
                    // TODO(b/137329632): Wait for idle of the right activity, not just any.
                    r.destroyIfPossible(reason);
                } else {
                    r.stopIfPossible();
                }
            }
        }
     }
}

ActivityManagerInternal是一个抽象类,它的实现在AMS中,trimApplicationsLocked方法中主要删除mRemovedProcesses中的进程,主要包含以下几种场景:

  • crash进程
  • 5S没有响应并被用户选择关闭的进程
  • 调用了killBackgroundProcesses想要杀死的进程
  • 系统启动时,在AMSSystemReady方法中,非Persistent的应用进程

接着调用updateOomAdjLocked方法更新进程的优先级并通知Linux内核(LowMemoryKiller),内核根据系统内存的使用情况以及adj的值动态管理进程资源,adj作为Linux内核的参考依据。进程的优先级即进程的oom_adj值,值越小优先级越高,最后被杀死。

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
     OomAdjuster mOomAdjuster;
    public final class LocalService extends ActivityManagerInternal
            implements ActivityManagerLocal {
        @Override
        public void trimApplications() {
            ActivityManagerService.this.trimApplications(true, OOM_ADJ_REASON_ACTIVITY);
        }
    }
    private void trimApplications(boolean forceFullOomAdj, @OomAdjReason int oomAdjReason) {
        synchronized (this) {
            trimApplicationsLocked(forceFullOomAdj, oomAdjReason);
        }
    }
     private void trimApplicationsLocked(boolean forceFullOomAdj, @OomAdjReason int oomAdjReason) {
         for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) {
            final ProcessRecord app = mProcessList.mRemovedProcesses.get(i);
            if (!app.hasActivitiesOrRecentTasks()
                    && app.mReceivers.numberOfCurReceivers() == 0
                    && app.mServices.numberOfRunningServices() == 0) {
                final IApplicationThread thread = app.getThread();
                Slog.i(TAG, "Exiting empty application process "
                        + app.toShortString() + " ("
                        + (thread != null ? thread.asBinder() : null)
                        + ")\n");
                final int pid = app.getPid();
                if (pid > 0 && pid != MY_PID) {
                    app.killLocked("empty",
                            ApplicationExitInfo.REASON_OTHER,
                            ApplicationExitInfo.SUBREASON_TRIM_EMPTY,
                            false);
                } else if (thread != null) {
                    try {
                        thread.scheduleExit();
                    } catch (Exception e) {
                        // Ignore exceptions.
                    }
                }
                didSomething = true;
                cleanUpApplicationRecordLocked(app, pid, false, true, -1, false /*replacingPid*/,
                        false /* fromBinderDied */);
                mProcessList.mRemovedProcesses.remove(i);

                if (app.isPersistent()) {
                    addAppLocked(app.info, null, false, null /* ABI override */,
                            ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
                    app.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_PERSISTENT);
                }
            }
        }
        // Now update the oom adj for all processes. Don't skip this, since other callers
        // might be depending on it.
        if (didSomething || forceFullOomAdj) {
            updateOomAdjLocked(oomAdjReason);
        } else {
            // Process any pending oomAdj targets, it'll be a no-op if nothing is pending.
            updateOomAdjPendingTargetsLocked(oomAdjReason);
        }
     }
     final void updateOomAdjLocked(@OomAdjReason int oomAdjReason) {
        mOomAdjuster.updateOomAdjLocked(oomAdjReason);
    }
}

OomAdjuster类的updateOomAdjLocked方法中,最终调用到updateOomAdjInnerLSP方法中,通过computeOomAdjLSP方法计算进程的oom_adj值。assignCachedAdjIfNecessary方法将未分配Adj值的进程根据进程状态分为后台缓存进程和empty进程.updateAndTrimProcessLSP方法会遍历mLruProcesses列表,调用applyOomAdjLSP方法,向/proc/进程号/oom_score_adj文件写入计算出来的oom_adj值,然后杀掉超过限制的后台缓存进程和empty进程,默认限制是后台缓存进程和empty进程各16个,isolated进程不包含服务直接回收,最后调用updateLowMemStateLSP方法进行内存调整。

public class OomAdjuster {
    void updateOomAdjLocked(@OomAdjReason int oomAdjReason) {
        synchronized (mProcLock) {
            updateOomAdjLSP(oomAdjReason);
        }
    }
     private void updateOomAdjLSP(@OomAdjReason int oomAdjReason) {
        if (checkAndEnqueueOomAdjTargetLocked(null)) {
            // Simply return as there is an oomAdjUpdate ongoing
            return;
        }
        try {
            mOomAdjUpdateOngoing = true;
            performUpdateOomAdjLSP(oomAdjReason);
        } finally {
            // Kick off the handling of any pending targets enqueued during the above update
            mOomAdjUpdateOngoing = false;
            updateOomAdjPendingTargetsLocked(oomAdjReason);
        }
    }
     private void performUpdateOomAdjLSP(@OomAdjReason int oomAdjReason) {
        final ProcessRecord topApp = mService.getTopApp();
        // Clear any pending ones because we are doing a full update now.
        mPendingProcessSet.clear();
        mService.mAppProfiler.mHasPreviousProcess = mService.mAppProfiler.mHasHomeProcess = false;
        updateOomAdjInnerLSP(oomAdjReason, topApp , null, null, true, true);
    }
    protected void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
            ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
            boolean startProfiling) {
         ArrayList<ProcessRecord> lruList = mProcessList.getLruProcessesLOSP();
        final int numLru = lruList.size();
         for (int i = numProc - 1; i >= 0; i--) {
            ProcessRecord app = activeProcesses.get(i);
            final ProcessStateRecord state = app.mState;
            if (!app.isKilledByAm() && app.getThread() != null) {
                state.setProcStateChanged(false);
                app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
                // It won't enter cycle if not computing clients.
                computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, fullUpdate, now, false,
                        computeClients, oomAdjReason, true);
                // if any app encountered a cycle, we need to perform an additional loop later
                retryCycles |= state.containsCycle();
                // Keep the completedAdjSeq to up to date.
                state.setCompletedAdjSeq(mAdjSeq);
            }
        }
        ...
        assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
         postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime);
        if (startProfiling) {
            mService.mOomAdjProfiler.oomAdjEnded();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }
     protected void postUpdateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, ActiveUids activeUids,
            long now, long nowElapsed, long oldTime) {
         final boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids,
                oomAdjReason);
         ...
     }
    private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed,
            final long oldTime, final ActiveUids activeUids, @OomAdjReason int oomAdjReason) {
         for (int i = numLru - 1; i >= 0; i--) {
              if (!app.isKilledByAm() && app.getThread() != null) {
                // We don't need to apply the update for the process which didn't get computed
                if (state.getCompletedAdjSeq() == mAdjSeq) {
                    applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason);
                }
                final ProcessServiceRecord psr = app.mServices;
                // Count the number of process types.
                switch (state.getCurProcState()) {
                    case PROCESS_STATE_CACHED_ACTIVITY:
                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                        mNumCachedHiddenProcs++;
                        numCached++;
                        final int connectionGroup = psr.getConnectionGroup();
                        if (connectionGroup != 0) {
                            if (lastCachedGroupUid == app.info.uid
                                    && lastCachedGroup == connectionGroup) {
                                numCachedExtraGroup++;
                            } else {
                                lastCachedGroupUid = app.info.uid;
                                lastCachedGroup = connectionGroup;
                            }
                        } else {
                            lastCachedGroupUid = lastCachedGroup = 0;
                        }
                        if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
                            app.killLocked("cached #" + numCached,
                                    "too many cached",
                                    ApplicationExitInfo.REASON_OTHER,
                                    ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,
                                    true);
                        } else if (proactiveKillsEnabled) {
                            lruCachedApp = app;
                        }
                        break;
                    case PROCESS_STATE_CACHED_EMPTY:
                        if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
                                && app.getLastActivityTime() < oldTime) {
                            app.killLocked("empty for " + ((now
                                    - app.getLastActivityTime()) / 1000) + "s",
                                    "empty for too long",
                                    ApplicationExitInfo.REASON_OTHER,
                                    ApplicationExitInfo.SUBREASON_TRIM_EMPTY,
                                    true);
                        } else {
                            numEmpty++;
                            if (numEmpty > emptyProcessLimit) {
                                app.killLocked("empty #" + numEmpty,
                                        "too many empty",
                                        ApplicationExitInfo.REASON_OTHER,
                                        ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY,
                                        true);
                            } else if (proactiveKillsEnabled) {
                                lruCachedApp = app;
                            }
                        }
                        break;
                    default:
                        mNumNonCachedProcs++;
                        break;
                }

                // TODO: b/319163103 - limit isolated/sandbox trimming to just the processes
                //  evaluated in the current update.
                if (app.isolated && psr.numberOfRunningServices() <= 0
                        && app.getIsolatedEntryPoint() == null) {
                    app.killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
                            ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
                } else if (app.isSdkSandbox && psr.numberOfRunningServices() <= 0
                        && app.getActiveInstrumentation() == null) {
                    app.killLocked("sandbox not needed", ApplicationExitInfo.REASON_OTHER,
                            ApplicationExitInfo.SUBREASON_SDK_SANDBOX_NOT_NEEDED, true);
                } else {
                    // Keeping this process, update its uid.
                    updateAppUidRecLSP(app);
                }

                if (state.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
                        && !app.isKilledByAm()) {
                    numTrimming++;
                }
            }
        }
        if (proactiveKillsEnabled                               // Proactive kills enabled?
                && doKillExcessiveProcesses                     // Should kill excessive processes?
                && freeSwapPercent < lowSwapThresholdPercent    // Swap below threshold?
                && lruCachedApp != null                         // If no cached app, let LMKD decide
                // If swap is non-decreasing, give reclaim a chance to catch up
                && freeSwapPercent < mLastFreeSwapPercent) {
            lruCachedApp.killLocked("swap low and too many cached",
                    ApplicationExitInfo.REASON_OTHER,
                    ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,
                    true);
        }
        mLastFreeSwapPercent = freeSwapPercent;
        return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming, now);
         }
    }
}

OOM ADJ的定义在ProcessList类中。

//无法确定的adj,要缓存的进程或空进程
public static final int UNKNOWN_ADJ = 1001;
//不可见进程的Adj最大值
public static final int CACHED_APP_MAX_ADJ = 999;
//不可见进程的Adj最小值
public static final int CACHED_APP_MIN_ADJ = 900;
//允许终止的oom_adj值
public static final int CACHED_APP_LMK_FIRST_ADJ = 950;
//B List的Service,和A List相比,对用户的黏合度小些
public static final int SERVICE_B_ADJ = 800;
//用户前一次交互的进程
public static final int PREVIOUS_APP_ADJ = 700;
//Launcher进程Adj
public static final int HOME_APP_ADJ = 600;
//应用服务进程
public static final int SERVICE_ADJ = 500;
//后台的重量级进程
public static final int HEAVY_WEIGHT_APP_ADJ = 400;
//承载backup相关操作的进程
public static final int BACKUP_APP_ADJ = 300;
//低感知进程
public static final int PERCEPTIBLE_LOW_APP_ADJ = 250;
//可见APP adj
public static final int VISIBLE_APP_ADJ = 100;
//前台APP adj
public static final int FOREGROUND_APP_ADJ = 0;
//系统进程
public static final int SYSTEM_ADJ = -900;
//Native 进程,不被系统管理
public static final int NATIVE_ADJ = -1000;
.....

LowMemoryKiller是Android基于Linux的OOM Killer定制的进程管理功能,通过对进程的资源管理来保证Android系统可以流畅运行,避免内存不足造成系统异常。

AMS实时更新oom_adj的值,并将此值传递到Linux Kernel中,Kernel有低内存回收机制,在内存达到一定阈值后触发kill oom_adj高的进程。可以通过cat /proc/pid 号/oom_score_adj查看每个进程的oom_adj值。

总结

AMS担任的角色实在是太多了,一两篇文章不可能将它的所有"魅力"展示出来。本文主要针对AMS的启动、相关数据结构、组件管理、进程管理和内存管理进行分析,本文结合四大组件的工作过程文章阅读效果更好,后续会继续补充AMS中的内容。


Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.yanghujun.com/archives/activitymanagerservice