About the author

Guo Xiaoxing, programmer and guitarist, is mainly engaged in the infrastructure of Android platform. Welcome to exchange technical questions. You can go to my Github to raise an issue or send an email to [email protected] to communicate with me.

For the first time in this series, see the introduction and the table of contents for more articles.

The article directories

  • A component manager ActivityManagerService
    • 1.1 ActivityManagerService Startup process
    • 1.1 ActivityManagerService workflow
  • Apply the main thread ActivityThread
    • 2.1 ActivityThread Startup process
    • 2.2 ActivityThread work

ActivityManagerService is a core service that runs through Android components. It is created when ServiceServer executes the run() method and runs in a separate thread. Responsible for the start, switch and scheduling of Activity, Service and BroadcastReceiver, as well as the management and scheduling of application process.

The startup, switch, and scheduling of activities, services, and BroadcastReceiver all have similar processes. Let’s take a look.

The startup flow chart of the Activity is as follows:

The main roles are:

  • Instrumentation: Monitor the interactions between applications and the system.
  • AMS: Component management scheduling center, does nothing, but manages everything.
  • ActivityStarter: Handles when an Activity starts and how it starts. It handles intents and flags.
  • The activitystackcontainer is designed to handle stacks and tasks, as you can see from its name.
  • ActivityStack: Used to manage activities in the stack.
  • ActivityThread: This class is the inner class of activityThreads, where activities, services, and BroadcastReceivers are started, switched, and scheduled.

The startup flowchart of Service is as follows:

The main roles are:

  • AMS: Component management scheduling center, does nothing, but manages everything.
  • ApplicationThread: The inner class of an ActivityThread that starts, switches, and schedules activities, services, and BroadcastReceivers.
  • ActiveServices: Manages services. Internally maintains three lists of services to start, services to restart, and services to destroy.

The startup flowchart of the BroadcastReceiver is as follows:

The main roles are:

  • AMS: Component management scheduling center, does nothing, but manages everything.
  • BroadcastQueue: BroadcastQueue that manages broadcasts according to their priorities.
  • ApplicationThread: The inner class of an ActivityThread that starts, switches, and schedules activities, services, and BroadcastReceivers.
  • ReceiverDispatcher: a broadcast dispatch center that gets an instance of the BroadcastReceiver reflectfully and calls its onReceive() method.

You can see that, in addition to some helper classes, the most important components are AMS and ActivityThread, the main application thread. This article focuses on the implementation of these two classes, while the other classes will be examined in articles on the Activity, Service, and BroadcastReceiver startup processes.

Through the above analysis, the whole AMS scheduling process is very clear.

The ActivityManager is the receptionist who communicates the needs of clients to the ActivityMangerService, but the ActivityManager doesn’t do the work himself, he hires a lot of young people, His trusty buddy, ActivityThread, does the actual startup, switching, and exit operations for him, leaving the rest to ActivityStack, ActivityStarter, and others.

A component manager ActivityManagerService

1.1 ActivityManagerService Startup process

We know that all system services are started in SystemServer’s run() method. SystemServer classifies system services into three categories:

  • Guide services
  • Core services
  • Other services

ActivityManagerService is a boot service created in the startBootstrapServices() method, as shown below:

mActivityManagerService = mSystemServiceManager.startService(
        ActivityManagerService.Lifecycle.class).getService();
Copy the code

SystemServiceManager’s startService() method uses reflection to create objects. Lifecycle is a static inner class in ActivityManagerService that inherits from SystemService. In its constructor it calls the Constructor of ActivityManagerService to create an ActivityManagerService object.

public static final class Lifecycle extends SystemService {
    private final ActivityManagerService mService;

    public Lifecycle(Context context) {
        super(context);
        mService = new ActivityManagerService(context);
    }

    @Override
    public void onStart(a) {
        mService.start();
    }

    public ActivityManagerService getService(a) {
        returnmService; }}Copy the code

ActivityManagerService is constructed as follows:

public ActivityManagerService(Context systemContext) {
    mContext = systemContext;
    mFactoryTest = FactoryTest.getMode();
    mSystemThread = ActivityThread.currentActivityThread();

    Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());

    // Create and start system threads and associated handlers
    mHandlerThread = new ServiceThread(TAG,
            android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
    mHandlerThread.start();
    mHandler = new MainHandler(mHandlerThread.getLooper());
    mUiHandler = new UiHandler();
    /* static; one-time init here */
    if (sKillHandler == null) {
        sKillThread = new ServiceThread(TAG + ":kill",
                android.os.Process.THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
        sKillThread.start();
        sKillHandler = new KillHandler(sKillThread.getLooper());
    }

    // Create a data structure for storing various component activities and Broadcast
    mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "foreground", BROADCAST_FG_TIMEOUT, false);
    mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "background", BROADCAST_BG_TIMEOUT, true);
    mBroadcastQueues[0] = mFgBroadcastQueue;
    mBroadcastQueues[1] = mBgBroadcastQueue;

    mServices = new ActiveServices(this);
    mProviderMap = new ProviderMap(this);
    mAppErrors = new AppErrors(mContext, this);

    // Create various folders, such as system, to record some system events.// Initialize some recording tools. }Copy the code

You can see that the constructor for ActivityManagerService does two main things:

  • Create and start system threads and associated handlers.
  • Create data structures for storing various component activities and Broadcast.

There’s a problem here. There are two hanlders created: MainHandler and UiHandler. What’s the difference between them? 🤔

We know that the Handler is used to send messages to the thread in which it is located. That is, it is the Looper in its constructor that determines the location of the Handler.

The Looper in MainHandler comes from the thread ServiceThread, whose thread name is “ActivityManagerService”. This Handler is used to handle component scheduling operations.

mHandlerThread = new ServiceThread(TAG,
        android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
mHandler = new MainHandler(mHandlerThread.getLooper());
Copy the code

The Looper in UiHandler comes from the thread UiThread (inherited from ServiceThread), whose thread name is “Android. UI”. This Handler is used to handle UI-related operations.


private UiThread(a) {
    super("android.ui", Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
    // Make sure UiThread is in the fg stune boost group
    Process.setThreadGroup(Process.myTid(), Process.THREAD_GROUP_TOP_APP);
}

public UiHandler(a) {
    super(com.android.server.UiThread.get().getLooper(), null.true);
}
Copy the code

This is an easy way to start ActivityManagerService.

1.2 ActivityManagerService workflow

ActivityManagerService is the core class of the ActivityManager family. All four components are started, switched, and scheduled in ActivityManagerService.

The ActivityManagerService class diagram looks like this:

  • ActivityManager: The interface that AMS calls to clients.
  • ActivityManagerNative: Binder is the parent of ActivityManagerService. Binder is responsible for communicating with processes and receiving messages from ActivityManager. Allows ActivityManagerService to focus on component scheduling, reducing the size of the class.
  • ActivityManagerProxy: This class is defined inside ActivityManagerNative and, as its name suggests, is a proxy class for ActivityManagerService.

About the ActivityManager

ActivityManager provides the interface for the client to call. In daily development, we can use ActivityManager to obtain the information of the running components (activities, services), processes, tasks and other information in the system. ActivityManager defines methods to retrieve and manipulate this information.

ActivityManager defines a number of static inner classes to describe this information, specifically:

  • Activitymanager. StackId: describes the component StackId
  • ActivityManager. StackInfo: describe the component stack information, can use the StackInfo to retrieve a stack in the system.
  • ActivityManager. MemoryInfo: system memory available information
  • ActivityManager. RecentTaskInfo: recent task information
  • ActivityManager. RunningAppProcessInfo: running process information
  • ActivityManager. RunningServiceInfo: running the service information
  • ActivityManager. RunningTaskInfo: information of the running task
  • Activitymanager. AppTask: Describes application task information

At this point, it’s important to distinguish a few concepts to avoid future confusion.

  • Processes: The basic unit of resource scheduling and allocation in the Android system. It is important to note that activities on the same stack can run in different processes.
  • Task: A Task is a collection of activities grouped together in a stack, which is a Task.

The ActivityManager class is usually not used directly in daily development, but only in special development scenarios.

  • IsLowRamDevice () : Determines whether the application is running on a low-memory Android device.
  • ClearApplicationUserData () : Resets user data in the app.
  • ActivityManager. AppTask/ActivityManager. RecentTaskInfo: how do we need to manipulate the Activity of the stack information that can be done through the ActivityManager.

About ActivityManagerNative and ActivityManagerProxy

These two classes are related to Android’s Binder communication principles, and there will be a special article on Binder implementation.

Apply the main thread ActivityThread

Activitythreads manage the main thread in application processes, and are responsible for starting, switching, and destroying activities, services, and BroadcastReceivers.

2.1 ActivityThread Startup process

Let’s talk about ActivityThread. 😎 is the entry to our app. For those of you who have written Java programs, Java entry classes always have a main() method, and ActivityThread is the same. Its main() method is called when a new application process is created. Let’s see what this main() method does.

public final class ActivityThread {
    
     public static void main(String[] args) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
         SamplingProfilerIntegration.start();
 
         // CloseGuard defaults to true and can be quite spammy. We
         // disable it here, but selectively enable it later (via
         // StrictMode) on debug builds, but using DropBox, not logs.
         CloseGuard.setEnabled(false);
 
         Environment.initForCurrentUser();
 
         // Set the reporter for event logging in libcore
         EventLogger.setReporter(new EventLoggingReporter());
 
         // Make sure TrustedCertificateStore looks in the right place for CA certificates
         final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
         TrustedCertificateStore.setDefaultUserDirectory(configDir);
 
         Process.setArgV0("<pre-initialized>");
         // Looper for the main thread
         Looper.prepareMainLooper();
         // Create ActivityThread instance
         ActivityThread thread = new ActivityThread();
         // Call attach() to attach the ApplicationThread object to AMS so that AMS can call the methods in ApplicationThread. This is also an IPC process.
         thread.attach(false);
 
         // Handler for the main thread
         if (sMainThreadHandler == null) {
             sMainThreadHandler = thread.getHandler();
         }
 
         if (false) {
             Looper.myLooper().setMessageLogging(new
                     LogPrinter(Log.DEBUG, "ActivityThread"));
         }
 
         // End of event ActivityThreadMain.
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         // Start the message loop
         Looper.loop();
 
         throw new RuntimeException("Main thread loop unexpectedly exited"); }}Copy the code

There’s also the key attach() method, so let’s take a look.

public final class ActivityThread {
    
   private void attach(boolean system) {
        sCurrentActivityThread = this;
        // Check whether it is a system process. False indicates that it is not a system process
        mSystemThread = system;
        // Application process processing flow
        if(! system) { ViewRootImpl.addFirstDrawHandler(new Runnable() {
                @Override
                public void run(a) { ensureJitEnabled(); }}); android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                // Attach the ApplicationThread object to AMS so that AMS can call the methods in ApplicationThread
                // This is also an IPC process.
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            // Watch for getting close to heap limit.
            BinderInternal.addGcWatcher(new Runnable() {
                @Override public void run(a) {
                    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 {
                            mgr.releaseSomeActivities(mAppThread);
                        } catch (RemoteException e) {
                            throwe.rethrowFromSystemServer(); }}}}); }// System process processing flow
        else {
            // Initialize system components, such as Instrumentation, ContextImpl, and Application
            // The system process name is system_process
            android.ddm.DdmHandleAppName.setAppName("system_process",
                    UserHandle.myUserId());
            try {
                // Create Instrumentation objects
                mInstrumentation = new Instrumentation();
                // Create ContextImpl object
                ContextImpl context = ContextImpl.createAppContext(
                        this, getSystemContext().mPackageInfo);
                // Create an Application object
                mInitialApplication = context.mPackageInfo.makeApplication(true.null);
                // Call the application.oncreate () method, which is familiar enough to do some library initialization work here.
                mInitialApplication.onCreate();
            } catch (Exception e) {
                throw new RuntimeException(
                        "Unable to instantiate Application():"+ e.toString(), e); }}// add dropbox logging to libcore
        DropBox.setReporter(new DropBoxReporter());
        
        // Registers the callback notification after Configuration changes. When the system Configuration changes, for example, a language switch, the callback is triggered.
        ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
            // The configuration has changed
            @Override
            public void onConfigurationChanged(Configuration newConfig) {
                synchronized (mResourcesManager) {
                    // We need to apply this change to the resources
                    // immediately, because upon returning the view
                    // hierarchy will be informed about it.
                    if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
                        updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
                                mResourcesManager.getConfiguration().getLocales());

                        // This actually changed the resources! Tell
                        // everyone about it.
                        if (mPendingConfiguration == null|| mPendingConfiguration.isOtherSeqNewer(newConfig)) { mPendingConfiguration = newConfig; sendMessage(H.CONFIGURATION_CHANGED, newConfig); }}}}/ / low memory
            @Override
            public void onLowMemory(a) {}@Override
            public void onTrimMemory(int level) {}}); }}Copy the code

From the above two methods we can see that ActivityThread does two main things:

  • Create and start a message loop for the main thread.
  • Attaching ApplicationThread objects (Binder objects) to AMS so that AMS can call methods in ApplicationThread is also an IPC process.

2.2 ActivityThread workflow

ActivityThread workflow flowchart is shown below:

From the previous analysis, the overall workflow of ActivityThread is clear. ActivityThread has a Binder object called ApplicationThread. AMS can call the methods in the ApplicationThread. The methods in ApplicationThread use mH (Handlers) to send messages to the message queue in the ActivityThread, which in turn processes the messages to perform various operations, such as starting an Activity.

So far we have sorted out the main framework of the ActivityManager family. This article does not have a large length to analyze the source code. Our focus is on sorting out the overall framework, so that you have an overall understanding of the specific details, you can according to your own needs targeted to study. This is the same approach we advocate for reading Android source code: don’t obsess over the details, but be aware of the big picture.

After understanding the content of AMS, we will analyze the startup, switching and destruction processes of Activity, Service and BroadcastReceiver. In the process of analysis, we will also combine some problems frequently encountered in daily development. With these problems, we will look at the source code. Why do these problems arise? How to solve it.