Introduction:

The point of this chapter is to gain a deeper understanding of how the four components work, and to help you gain a better understanding of Android’s overall architecture. In many cases, an understanding of the Android architecture is necessary to write good code in practice. The reader has a sense of the working process of the four components and can give some guidance to the upper development.

The main content

  • The operating status of the four components
  • The working process of an Activity
  • The working process of a Service
  • The working process of the BroadcastReceiver
  • How ContentProvider works

The specific content

The operating status of the four components

All four components of Android, except for BroadcastReceiver, need to be registered in the AndroidManifest file. BroadcastReceiver can be registered through code. In terms of how to invoke intEnts, all three components except ContentProvider require intEnts.

Activity

Is a display component, used to directly display an interface to the user, and can receive user input information to interact, playing the role of a front interface. An Activity can be started with an intent, either implicitly or explicitly. An Activity can have a specific start mode, and the Finish method ends the Activity.

Service

Is a computational component that performs a series of computational tasks in the background. It still runs in the main thread itself, so the time-consuming logic still needs to be done by a separate thread. An Activity has only one state: the start state. There are two kinds of services: startup state and binding state. When a service is bound, it is easy for outsiders to communicate with the service, whereas when it is started, they cannot. A Service can be stopped. You need to flexibly use stopService and unBindService.

BroadcastReceiver

Is a messaging component used to pass messages between different components and even between different applications.

  • Static registration:

Register a broadcast in the manifest file. This broadcast is resolved by the system when the application is installed. This form of broadcast can be received without the application being started.

  • Dynamic registration:

Need to pass through the Context. RegisterReceiver (), and when not needed by the Context. UnRegisterReceiver () to terminate the radio. This type of broadcast requires application startup to register and receive broadcasts. In actual development, broadcasts are sent through a series of send methods of the Context. The sent broadcasts are sent by the system to interested broadcast receivers. The matching of the sending and receiving processes is described by the broadcast receivers. A low-coupling observer pattern can be achieved without any coupling between the observer and the observed. But broadcasting is not suitable for time-consuming operations.

ContentProvider

It is a data sharing component used to share data with other components and even other applications. It maintains a collection of data that can be implemented by a database or any other type, such as list or map.contentProvider, without requiring any specific implementation of the collection. Take care to handle internal thread synchronization for insert, DELETE, Update, and Query methods, which are called in the Binder thread pool.

The working process of an Activity

  1. All startActivity overload methods for an Activity will eventually call startActivityForResult.
  2. Call mInstrumentation. ExecStartActivity. ExecStartActivity () method.
  3. Code to start the Activity in the real implementation is by ActivityManagerNative getDefault () startActivity () method. ActivityManagerService for short AMS. AMS inherits from ActivityManagerNative(), ActivityManagerNative() inherits from Binder and implements the IActivityManager interface, so AMS is also a Binder and is a concrete implementation of IActivityManager. ActivityManagerNative. GetDefault () is essentially a IActivityManager type of Binder object, so the specific implementation is AMS.
  4. In ActivityManagerNative, AMS, the Binder object, is provided as a Singleton, and Singleton is a Singleton wrapper class. The Binder object AMS is initialized with the create method the first time its GET () method is called and returned in subsequent calls.
  5. The AMS startActivity() procedure:
    1. The checkStartActivityResult () method checks the result of starting the Activity (including whether it is registered in the manifest).
    2. After two transfer Activity in the process of its start, then transferred to mStackSupervisor. StartActivityMayWait () this method, belongs to such as ActivityStackSupervisor. StartActivityLocked () is called inside startActivityMayWait(), which returns the result code that was used for checkStartActivityResult().
    3. Method will call startActivityUncheckedLocked (), then call ActivityStack# resumeTopActivityLocked (). At this point, the startup process has been moved from ActivityStackSupervisor to the ActivityStack class.

  6. . In the final ActivityStackSupervisor realStartActivityLocked (), call the app. The thread. ScheduleLaunchActivity () method. App. thread is an ApplicationThread class that extends from IApplicationThread, a Binder class that provides interfaces for starting and stopping services and activities.
  7. In ApplicationThread, scheduleLaunchActivity() is used to start the Activity, The implementation is to send an Activity message (wrapped from an ActivityClientRecord object) to a Handler for processing. This Handler has a succinct name, H.
  8. In H’s handleMessage() method, The Activity object is created and started using the handleLaunchActivity() method, and the ActivityThread calls the initiated onResume() lifecycle method using the handleResumeActivity() method. PerformLaunchActivity() does a few things:
    1. Gets information about the Activity component to be started from the ActivityClientRecord object.
    2. Use the class loader to create an Activity object through the newActivity method of Instrumentation.
    3. LoadedApk attempts to create an Application object through the makeApplication method, implemented through the classloader (if the Application has already been created, it will not be created again).
    4. Create the ContextImpl object and initialize some important data using the Attach method of the Activity. ContextImpl is an important data structure that is the implementation of the Context, most of which is done by ContentImpl. ContextImpl is attached to the Activity using the attach() method of the Activity. In addition, in attach(), the Activity also completes the creation of the Window and associates itself with the Window. This allows the Window to pass the event to the Activity when it receives an external input event.
    5. Through mInstrumentation. CallActivityOnCreate (activity, r.s Tate) method calls the activity’s onCreate method.

The working process of a Service

  • Start state: Performs background calculations.
  • Binding state: Used by other components to interact with the Service.

The two states can coexist.

Service startup process

  1. Service starts from ContextWrapper’s startService.
  2. In ContextWrapper, most operations are implemented through a ContextImpl object, mBase.
  3. In ContextImpl, mBase.startService() calls the startServiceCommon method, And startServiceCommon method through ActivityManagerNative. GetDefault () (in fact is the AMS) this object to launch a service.
  4. AMS starts a Service using an ActiveService object (a class that helps AMS manage services, including starting, binding, and stopping services) : MServices. StartServiceLocked ().
  5. In mServices. StartServiceLocked () the last invoked startServiceInnerLocked () method: Wrap the Service information into a ServiceRecord object, which is used throughout the Service startup process. With the bringUpServiceLocked() method, bringUpServiceLocked() calls the realStartServiceLocked() method to actually start a Service.
  6. The realStartServiceLocked() method works like this:
    1. App. Thread. ScheduleCreateService () to create the Service and call its onCreate () life cycle method.
    2. SendServiceArgsLocked () calls other lifecycle methods, such as onStartCommand().
    3. The app.thread object is of type IApplicationThread, which is a Binder and is implemented as ApplicationThread inheriting ApplictionThreadNative.
  7. Specific look at Application start-up process of the Service app. Thread. ScheduleCreateService () : Using sendMessage(h.create_service, s), this process is similar to the Activity startup process and is done by sending a message to Handler H.
  8. H accepts the CREATE_SERVICE message and launches the Service through ActivityThread’s handleCreateService().
  9. HandleCreateService () does the following:
    1. Create a Service object by ClassLoader.
    2. Create a Context object inside the Service.
    3. Create the Application and call its onCreate() (only once).
    4. Connect the service to the context using the service.attach() method (similar to an Activity).
    5. The onCreate() lifecycle method of the service is called. At this point, the service is started.
    6. Store the Service object into an ArrayMap of ActivityThread.
Service binding process

Similar to the service startup process:

  1. The binding of Service starts with ContextWrapper’s bindService.
  2. In ContextWrapper, hand the ContextImpl object mBase.bindService().
  3. Finally, ContextImpl’s bindServiceCommon method is called, which does two things:
    1. Will the client ServiceConnection into ServiceDispatcher. InnerConnection object. ServiceDispatcher connects ServiceConnection and InnerConnection. This process is implemented using LoadedApk’s getServiceDispatcher method, which stores the mapping between the client’s ServiceConnection and ServiceDispatcher in an ArrayMap.
    2. Done through AMS Service specific binding process ActivityManagerNative. GetDefault (). The bindService ().
  4. In AMS, the bindService() method calls bindServiceLocked(), bindServiceLocked() calls bringUpServiceLocked(), BringUpServiceLocked () in turn calls realStartServiceLocked().
  5. AMS realStartServiceLocked () will call ActiveServices requrestServiceBindingLocked () method, Finally is to call the ServiceRecord object r app. Thread. ScheduleBindService () method.
  6. ApplicationThread’s set of methods starting with schedule are internally routed through Handler H: scheduleBindService() is also internally routed through sendMessage(H.bin_service, s).
  7. This is handled by ActivityThread’s handleBindService() method when a message such as BIND_SERVICE is received within H:
    1. Fetch the Service object based on Servcie’s token.
    2. Call the onBind() method of the Service. At this point, the Service is in a bound state.
    3. Binder objects are called to call onServiceConnected() methods on the client’s ServiceConnection. This through ActivityManagerNative. GetDefault (.) publishService (). ActivityManagerNative. GetDefault () is the AMS.
  8. The AMS publishService() is handled by the ActivityService object publishServiceLocked(), Connected (R.name,service). Object is of type c ConnectionRecord, Arthur c. onn is ServiceDispatcher. InnerConnection object, service is the service’s onBind () method returns the Binder object.
  9. Connected (r.name,service) internal implementation is handed to mactivityThread. post(new RunnConnection(name,service,0)); The implementation. ServiceDispatcher’s mActivityThread is a Handler, which is the H in ActivityThread. This way RunConnection runs in the main thread via H’s POST method, so the methods in the client ServiceConnection are called back in the main thread.
  10. RunConnection is defined as follows:
    1. Inherits from the Runnable interface, the implementation of the run() method also simply calls the doConnected method of ServiceDispatcher.
    2. Because ServiceDispatcher holds the client-side ServiceConntion object internally, it’s easy to call the onServiceConnected method of the ServiceConntion object.
    3. Once the onServiceConnected method on the client is executed, the Service binding process is complete.
    4. ServiceDispatcher connects the ServiceConnection and InnerConnection by sending a ServiceDispatcher notification to the client following steps 8, 9, and 10. As for stopping and unbinding a Service, the system flow is similar.

The working process of the BroadcastReceiver

To recap how to use broadcasts, first define broadcast receivers, just inherit BroadcastReceiver and rewrite the onReceive() method. Once you have defined the broadcast receiver, you also need to register the broadcast receiver, which can be divided into two types: static registration or dynamic registration. Once the registration is complete, the broadcast can be sent.

The registration process for broadcasts

  1. The dynamic registration process starts with ContextWrapper#registerReceiver(). Same as Activity or Service. Instead of doing the actual work, ContextWrapper hands off the registration process directly to ContextImpl.
  2. The ContextImpl#registerReceiver() method calls the registerReceiverInternal() method of this class.
  3. The system first obtains the IIntentReceiver object from mPackageInfo, and then sends the broadcast registration request to AMS in a cross-process way. The reason why IIntentReceiver is used rather than BroadcastReceiver directly is because the registration process is an interprocess communication process. As a component of Android, BroadcastReceiver cannot be delivered directly across processes. All need to be transferred through the IIntentReceiver.
  4. IIntentReceiver as a Binder interface, its implementation is LoadedApk ReceiverDispatcher. InnerReceiver, The ReceiverDispatcher contains both the BroadcastReceiver and the InnerReceiver so that when a broadcast is received, ReceiverDispatcher is a handy way to call BroadcastReceiver#onReceive(). It has the same class as Service, and the inner class is also a Binder interface.
  5. Since the process of registering broadcasts is actually implemented in AMS, the first step in AMS is to look at the registerReceiver() method, which is only concerned with the core parts. This code will eventually store the remote InnerReceiver and IntentFilter objects, and the entire broadcast will be registered.
The sending and receiving of a broadcast

There are several types of broadcast transmission: normal broadcast, ordered broadcast, and sticky broadcast. Their send/receive processes are similar, so only the normal broadcast implementation is analyzed.

  1. The transmitting and receiving of a broadcast are essentially two stages of a process. The sending of the broadcast still starts with the ContextImpl#sendBroadcase() method, which is not Context because Context#sendBroad() is an abstract method. As with the broadcast registration, ContextWrapper#sendBroadcast() still does nothing, just leaves the matter to ContextImpl to handle.
  2. ContextImpl also does almost nothing, internally making an asynchronous request directly to AMS to send a broadcast.
  3. Call the AMS#broadcastIntent() method, which continues to call broadcastIntentLocked().
  4. Inside broadcastIntentLocked(), matching broadcast receivers are found based on intent-filters and filtered by a series of criteria. The qualified broadcast receivers are eventually added to the BroadcastQueue, which then sends broadcasts to the corresponding broadcast receivers.
  5. BroadcastQueue#scheduleBroadcastsLocked() does not immediately send a broadcast, but a message of type BROADCAST_INTENT_MSG. The BroadcastQueue calls the processNextBroadcast() method when it receives a message.
  6. Disorderly broadcast stored in mParallelBroadcasts, the system will traverse the set and will be one of the radio sent them all the receiver, is through the specific transmission process deliverToRegisteredReceiverLocked () method. DeliverToRegisteredReceiverLocked () is responsible for a broadcast sent to a specific receiver, its internal call performReceiverLocked method to complete the specific transmission process.
  7. PerformReceiverLocked () method calls ApplicationThread# scheduleRegisteredReceiver () implementation is simpler, it done by InnerReceiver radio reception.
  8. ScheduleRegisteredReceiver () method, receiver. PerformReceive IIntentReceiver types corresponding to the receiver () interface. ReceiverDispatcher$InnerReceiver. These two nested inner classes belong to LoadedApk.
  9. The LoadedApk$ReceiverDispatcher#performReceive() method is called. In the performReceiver() method, an Args object is created and the logic in the Args is executed through the POST method of the mActivityThread. And the essential relationship of these classes is:
    1. Args: Implements the Runnable class.
    2. MActivityThread: mActivityThread: mActivityThread: mActivityThread: mActivityThread: mActivityThread: mActivityThread This inner class H was said before.
  10. The BroadcastReceiver#onReceive() method in the Args that implements the Runnable interface is executed, meaning that the application has received the broadcast and the onReceive() method is called from the main thread of the broadcast receiver.

Android 3.1 started with two flags for. FLAG_INCLUDE_STOPPED_PACKAGES, FLAG_EXCLUDE_STOPPED_PACKAGES. Used to control whether the broadcast is intended to work on the stopped application.

  • FLAG_INCLUDE_STOPPED_PACKAGES: Contains stopped applications and broadcasts are sent to stopped applications.
  • FLAG_EXCLUDE_STOPPED_PACKAGES: does not contain stopped applications. Broadcasts are not sent to stopped applications.

Starting with Android 3.1, the FLAG_EXCLUDE_STOPPED_PACKAGES flag has been added by default for all broadcasts. FLAG_INCLUDE_STOPPED_PACKAGES(not the default) is used when these two tags coexist.

Applications in stop fall into two categories:

  • The application is not running after installation.
  • Forcibly stopped by manual or other applications.

The boot broadcast is also affected by this flag bit. Applications that have been stopped since Android 3.1 cannot receive a startup broadcast, and applications that were stopped before Android 3.1 can receive a startup broadcast.

How ContentProvider works

Contentproviders are content-sharing components that provide data to other components and applications through binders. When the process the ContentProvider is in starts, the ContentProvider is started and published to AMS at the same time. Note that the onCreate() method of the ContentProvider is executed before the onCreate() method of the Application, which is rare among the four components.

  1. When an application starts, the entry method is the Main method of ActivityThread, which creates an instance of the ActivityThread and a message queue for the main thread.
  2. The attach method of ActivityThread remotely calls attachApplication of ActivityManagerService and provides the ApplicationThread to AMS. Applicationthreads are used to communicate between ActivityThreads and AMS.
  3. AttachApplication in ActivityManagerService calls the bindApplication method of the ApplicationThread. The bindApplication method is switched to the ActivityThread. That is, call the handleBindApplication method.
  4. The handleBindApplication method creates the Application object and loads the ContentProvider. Note that the ContentProvider is loaded first and then the onCreate method of the Application is called.
  5. After ContentProvider is started, the outside world can operate data sources in ContentProvider through the four interfaces it provides: add, delete, change and check. These four methods are called by Binder, so the outside world cannot directly access ContentProvider. It can only obtain the corresponding Binder interface IContentProvider of ContentProvider according to URI through AMS, and then access the data source in ContentProvider through IContentProvider.

The Android: MultiProcess property of the ContentProvider determines whether it is singleton. The default value is false, which means it is singleton by default. When set to true, there is a ContentProvider object in each caller’s process.

Calling any of the Insert, DELETE, Update, or query methods of a ContentProvider will trigger the creation of the ContentProvider if the ContentProvider process is not started. This is accompanied by the start of the process in which the ContentProvider is located.

Take the query call as an example:

  1. AcquireUnstableProvider () or acquireProvider() directly. The essence is the same. The ContentProvider is ultimately obtained through the acquireProvider method.
  2. ApplicationContentResolver# acquireProvider () method does not handle any logic, it direct call ActivityThread# acquireProvider ().
  3. Check the ActivityThread to see if the ContentProvider already exists and return it if it does. ActivityThread uses mProviderMap to store started ContentProvider objects. This collection is of type ArrayMap mProviderMap. If the ContentProvider is not currently started, an interprocess request is sent to AMS to start the project target ContentProvider, and the reference count is modified through the installProvider() method.
  4. How does AMS start a ContentProvider? The process in which the ContentProvider is located is started, and then the ContentProvider. Starting a process is done by the AMS#startProcessLocked() method. Internally, a new process is started using the Process#start() method, which has an entry method called ActivityThread#main().
  5. ActivityThread#main() is a static method, inside which an ActivityThread instance is created and the attach() method is called for a series of initializations, followed by a message loop. The ActivityThread#attach() method transfers the Application object across processes to AMS through AMS#attachApplication, and AMS completes the creation of the ContentProvider.
  6. The AMS#attachApplication() method calls attachApplication() and then calls ApplicationThread#bindApplication(), The bindApplication() method sends a message of type BIND_APPLICATION to the mH, which is a Handler. When it receives the message, it calls the ActivityThread#handleBindApplication() method.
  7. ActivityThread#handlerBindApplication() completes the creation of the Application and the ContentProvider can be divided into four steps:
    1. Create the ContentProvider and Instrumentation.
    2. Create the Application object.
    3. Start the current process’s ContentProvider and call the onCreate() method. The main internal implementation is that installContentProvider() completes the job of starting contentProviders, first iterating through the list of ProviderInfo for the current process and calling installProvider() to start them one by one. Then publish the launched ContentProviders to AMS, which stores them in the ProviderMap, This allows external callers to retrieve the ContentProvider instance created internally by the classloader directly from AMS and call attachInfo() in the method. Inside this is a call to ContentProvider#onCreate().
    4. Call the application # onCreate ().

After the four steps, the ContentProvider has been successfully started, and the Application of its process has been successfully started, which means that the process has completed the entire startup process. Other applications can then access the ContentProvider through AMS.

Once you have a ContentProvider, you can access it through the interface methods it provides. Note that the ContentProvider is not the original ContentProvider. But the ContentProvider IContentProvider Binder type object, and the implement is ContentProviderNative IContentProvider and ContentProvider. Transport. The latter inherited the former.

If we use the Query method to explain the process, then the Binder object that other applications get ContentProvider from AMS is IContentProvider. And is the actual implementers IContentProvider ContentProvider. Transport. So in fact external application calls when nature will call in the form of interprocess communication ContentProvider. Transport of query () method.