First of all:

The writing of this article refers to a large number of articles, some may be directly copied from the text, I will give links in the article, if there is infringement, please contact me to delete, thank you.

As we know, the Activity can be started by Activity or by Context, there is no big difference between the two kinds of start, are finally called Instrumentation method to start, of course, say so, in fact, there are still differences, The Activity’s startActivity() method can use the default LAUNCH FLAG configuration, while the Context’s startActivity() must contain the FLAG_ACTIVITY_NEW_TASK LAUNCH FLAG, The reason is that the Context may not have an existing task stack for the new Activity to use, and you must explicitly specify to generate a separate task stack of your own.

With Binder, the Activity is eventually started by AMS(ActivityManagerService) in the System_server process. I’m not going to talk about the Activity startup process here, because that’s the way it goes, and too many blog posts have analyzed the process. If you want to see the startup process, check out the following article: StartActivity startup process analysis [Activity to start the process all analytical] (http://blog.csdn.net/zhaokaiqiang1992/article/details/49428287) if the above articles are not satisfied, Or if you still don’t understand the details, try:

StartActivity Startup process analysis

Here are some important classes:

  • ActivityManagerService (AMS for short), a server object in the System_server process, is responsible for the life cycle of all activities in the system
  • ActivityThread, the real entry point to the App. When the App is started, main() is called to start running, opening the message loop queue, which is known as the UI thread or main thread. Work with ActivityManagerService to manage the Activity
  • ApplicationThread, which implements the interaction between ActivityManagerService and ActivityThread. When ActivityManagerService needs to manage the Activity lifecycle of the related Application, it communicates with the ActivityThread through a proxy object for the ApplicationThread. Because App and AMS communicate, App is the client, The ApplicationThread is the server, and the system_server process is the client. The ApplicationThread is the server.
  • ApplicationThreadProxy is the server-side proxy for ApplicationThread, which communicates with the application process’s server object ApplicationThread. It is through this agent that AMS communicates with ActivityThread
  • Instrumentation, each application has only one Instrumentation object, and each Activity has a reference to that object. Instrumentation can be understood as the steward of the application process. When an ActivityThread wants to create or pause an Activity, it needs to perform specific operations through Instrumentation.
  • ActivityStack is the stack management of activities in AMS, which is used to record the status of activities that have been started. Use ActivityStack to determine if a new process needs to be started.
  • ActivityRecord, the managed object of ActivityStack, has an ActivityRecord for each Activity in AMS, which records the status of the Activity and other management information. This is the image of the Activity object on the server side.
  • A TaskRecord is a stack of activityRecords that AMS abstracts from the concept of a “Task”. A “Task” contains several ActivityRecords. AMS uses TaskRecord to ensure the order in which an Activity starts and exits. This concept should be familiar if you are familiar with the four launchmodes for your Activity.

Here are a few questions:

1. Start theActivityWhy is it so complex that it needs to cross processes?

One reason is that android’s four major components are all designed to allow a component to run in a single process. All App processes in Android are forked by Zygote (you don’t want to create your own process, you create a process, it needs some system resources how do you give). If our Activity component is configured with a new process that requires the Zygote process to do its work, this is a cross-process. Here’s how components configure processes. This is typically done through the Android: Process attribute in androidmanifest.xml. When the Android: Process attribute starts with:, it indicates that the process is private and accessible only to the App. When the Android: Process attribute value does not start with “:”, it represents a global process, but in this case it is important to note that the process name must contain at least “. Characters.

Another reason is that the Activity lifecycle is actually managed by ActivityManagerService(AMS) in the system_server process, except for onCreate, which is called by the process when new comes out. Look at the IActivityManager interface.

public interface IActivityManager extends IInterface {
    public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
    public boolean finishActivityAffinity(IBinder token) throws RemoteException;
    public void finishVoiceTask(IVoiceInteractionSession session) throws RemoteException;
    public boolean releaseActivityInstance(IBinder token) throws RemoteException;
    public void releaseSomeActivities(IApplicationThread app) throws RemoteException;
    public boolean willActivityBeVisible(IBinder token) throws RemoteException;
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter,
            String requiredPermission, int userId) throws RemoteException;
    public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException;
    public int broadcastIntent(IApplicationThread caller, Intent intent,
            String resolvedType, IIntentReceiver resultTo, int resultCode,
            String resultData, Bundle map, String[] requiredPermissions,
            int appOp, Bundle options, boolean serialized, boolean sticky, int userId) throws RemoteException;
    public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException;
    public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map,
            boolean abortBroadcast, int flags) throws RemoteException;
    public void attachApplication(IApplicationThread app) throws RemoteException;
    public void activityResumed(IBinder token) throws RemoteException;
    public void activityIdle(IBinder token, Configuration config,
            boolean stopProfiling) throws RemoteException;
    public void activityPaused(IBinder token) throws RemoteException;
    public void activityStopped(IBinder token, Bundle state,
            PersistableBundle persistentState, CharSequence description) throws RemoteException;
    public void activitySlept(IBinder token) throws RemoteException;
    public void activityDestroyed(IBinder token) throws RemoteException;
}
Copy the code

Why do we need system_server to manage the life cycle of an Activity? Is it not my life that I decide? This question can be figured out for A moment. At this time, we switch the process and return to the desktop Launcher process. At this time, we must hope that the video on interface A will stop playing. At this time, if the App manages its own life, the App does not know that it is already on the desktop. So it’s obvious that this simple scenario knows that the Activity lifecycle itself does not have callback management.

#### 2. How does an Activity communicate with ActivityManagerService across processes? Binder is the obvious answer, but it would take more than an article to explain how specific binders communicate. I don’t know what to say here, and my current description and understanding of Binder is not very good, so I only talk about the use of Binder at the Framework layer.

Use process of Binder: ##### 2.1. Protocol Interface Binder is C/S architecture, corresponding to Client and Server. To use Binder, first we need to specify a protocol, which is what the client and server need to do. Corresponding to The Java side, we need to specify a common interface between the client and server. This excuse needs to implement the empty interface IInterface. This interface defines a method to return a Binder object for use with Binder communication.

/**
* Base class for Binder interfaces.  When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
    /**
    * Retrieve the Binder object associated with this interface.
    * You must use this instead of a plain cast, so that proxy objects
    * can return the correct result.
    */
    public IBinder asBinder();
}
Copy the code

For example, IApplicationThread is used by the system_server process to call App methods across processes. AMS is used by the App process to call system_server methods across processes. AIDL is the same.

public interface IApplicationThread extends IInterface {
    void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving,
            int configChanges, boolean dontReport) throws RemoteException;
    void scheduleStopActivity(IBinder token, boolean showWindow,
            int configChanges) throws RemoteException;
    void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
    void scheduleSleeping(IBinder token, boolean sleeping) throws RemoteException;
    void scheduleResumeActivity(IBinder token, int procState, boolean isForward, Bundle resumeArgs)
            throws RemoteException;
}
int SCHEDULE_PAUSE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
int SCHEDULE_STOP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
int SCHEDULE_WINDOW_VISIBILITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
int SCHEDULE_RESUME_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
Copy the code

And I’m going to number each method to identify each method.

##### 2.2. Server side implementation The server side implementation, inheriting Binder classes, implements the public interface IApplicationThread defined above. Then implement the methods inside.

private class ApplicationThread extends ApplicationThreadNative {

    private void updatePendingConfiguration(Configuration config) {
        synchronized (mResourcesManager) {
            if(mPendingConfiguration == null || mPendingConfiguration.isOtherSeqNewer(config)) { mPendingConfiguration = config; } } } public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, boolean dontReport) { sendMessage( finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY, token, (userLeaving ? 1 : 0) | (dontReport ? 2 : 0), configChanges); }}Copy the code

Binder inherits these methods and needs to duplicate another onTransact method because cross-process calls cannot call methods directly. The client and server must use the same identifier as the interface defined above, and then call the corresponding method according to the identifier.

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    switch (code) {
    caseSCHEDULE_PAUSE_ACTIVITY_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); IBinder b = data.readStrongBinder(); boolean finished = data.readInt() ! = 0; boolean userLeaving = data.readInt() ! = 0; int configChanges = data.readInt(); boolean dontReport = data.readInt() ! = 0; schedulePauseActivity(b, finished, userLeaving, configChanges, dontReport);return true;
    }

    caseSCHEDULE_STOP_ACTIVITY_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); IBinder b = data.readStrongBinder(); boolean show = data.readInt() ! = 0; int configChanges = data.readInt(); scheduleStopActivity(b, show, configChanges);return true; }}Copy the code

##### 2.3. Client-side implementation Client-side implementation, which implements the public interface IApplicationThread defined above. Then implement the methods inside.

class ApplicationThreadProxy implements IApplicationThread {
    private final IBinder mRemote;
    
    public ApplicationThreadProxy(IBinder remote) {
        mRemote = remote;
    }
    
    public final IBinder asBinder() {
        returnmRemote; } public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeStrongBinder(token); data.writeInt(finished ? 1:0); data.writeInt(userLeaving ? 1:0); data.writeInt(configChanges); data.writeInt(dontReport ? 1:0); mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); }}Copy the code

The implementation here simply writes the id of the method to be called, the parameters passed, to the Binder driver via mRemote, waits for the remote method to be called, and finally writes the result back to the Binder driver. * The mRemote here actually refers to BinderProxy class, which has native methods to interact with Binder. You should read the article to find out exactly how to know this class.

##### 2.4. Conversion between client and server we can see that ActivityThread. Attach method, here, our App is the server, AMS is the client. The final call is the AMS proxy class ActivityManagerProxy.

public void attachApplication(IApplicationThread app) throws RemoteException
{
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(app.asBinder());
    mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);
    reply.readException();
    data.recycle();
    reply.recycle();
}
Copy the code

Corresponds to ActivityManagerService on the server. First onTransact:

case ATTACH_APPLICATION_TRANSACTION: {
    data.enforceInterface(IActivityManager.descriptor);
    IApplicationThread app = ApplicationThreadNative.asInterface(
            data.readStrongBinder());
    if(app ! = null) { attachApplication(app); } reply.writeNoException();return true;
}
Copy the code

Here data.readStrongBinder() gets the BinderProxy object and gets ApplicationThreadProxy, as well as the intermediate layer transformations that Binder does underneath.

#### 3. How can Activity HOOK? Starting an Activity is very simple, with the startActivity method. However, there is a limitation of Android that an Activity must be declared in the Manifest to be started. Well, the validation is not done locally but in the system_server process where ActivityManagerService is located. So now we’ve derived some solutions. Since the Activity to be launched must be registered in the Manifest, it’s possible to register some activities in advance for use. Well, there are two ways to do this.

3.1. The agentActivitymodel

The main feature of the so-called ProxyActivity mode is that the main project APK registers a ProxyActivity (named ProxyActivity). The ProxyActivity is a normal Activity, but it is an empty shell without any business logic of its own. Every time you open an Activity in the plug-in APK, you start ProxyActivity in the main project in a standard way, and then call the life cycle method of the Activity instance in the plug-in synchronously in the life cycle of the ProxyActivity, so as to execute the business logic of the plug-in APK. The above features are described in the proxy Activity mode

Since the current method of plug-in Activity is the second one in 3.2, and proxy Activity mode is really not very convenient, so it is not the main point. Dynamic load-APk defines the DLPlugin interface for activities. DLPlugin abstracts the key lifecycle methods of activities into DLPlugin interface. ProxyActivity invokes the plug-in Activity lifecycle through the DLPlugin proxy.

When loading a plug-in, you parse the APK file first, and then create the ClassLoader and Resources. These two problems are particularly troublesome, and we will talk about them later, because we will not be able to explain them in a moment. Preparation code :DLPluginManager

Activity

dynamic-load-apk
Activity

3.2. Dynamic creationActivitymodel

In fact, the above code mode Activity has some defects, such as the use of that keyword development, the launch of ProxyActivity, LaunchMode problems, etc. So, as someone continues to work on this, we have the dynamic create Activity mode. Binder mechanisms are required to have some understanding of the Activity startup process. 2. Based on Hook, the dynamic creation of Activity mode is realized by Hook part of the system API, so it needs compatibility; 3. Need to pre-register pit, the previous analysis, to start the Activity must have been registered, so, dynamic creation is no exception to the need to first create some activities in the Manifest for use, you can create some different startup modes, or even different processes to support multiple processes; To Hook an Activity, replace the actual Activity with a registered Activity when it starts, and complete the task in the AMS of the system_server process, that is, some Activity management and some validation work. When you go back to the App process and switch back to the actual Activity, you can think of roughly two options. 1.Hook the startActivity method to replace the actual Activity with the pre-registered Activity. Of course, check some parameters of the launched Activity, such as LaunchMode, to select the best pre-registered Activity. Hook the handleLaunchActivity method when AMS returns to the App process after doing something. Hook startActivity method, a heavier way is Hook AMS, a lighter method can Hook Instrumentation.

The 360 team’s plug-in framework RePlugin does just that, but it’s much more troublesome.

The details are described in the next article.

#### 4.ClassLoader processing? If ClassLoader does not know gaha, it must be checked first.

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // ...
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if(r.state ! = null) { r.state.setClassLoader(cl); } } catch (Exception e) {if(! mInstrumentation.onException(activity, e)) { throw new RuntimeException("Unable to instantiate activity " + component
                + ":"+ e.toString(), e); }} / /...return activity;
}
Copy the code

We know that the last step in starting an Activity is to call ATP(ApplicationThreadProxy) from AMS. Cross-process calls to our App’s AT(ApplicationThread), which then sends a message to Handler Hand calls ActivityThread’s performLaunchActivity method, which is the code I posted above. Since an Activity is also a Java object, it must need a ClassLoader when it is new. Not only an Activity, but all classes that load plug-ins need ClassLoader. If the Activity component exists in a file separate from the host program, how does the system’s ClassLoader know where to load it? Therefore, the Activity object in the plug-in can’t even be created without doing extra processing. How can you start it? There are also two ways to load a ClassLoader for plug-in code: 1. Create your own ClassLoader to load plug-ins, one for each plug-in.

2. Delegate system ClassLoader to load our plugin apK path into the dexElements field of the pathList object DexPathList, and then load it.

In the second scenario, the host and plug-in cannot have the same class between plug-ins. After the plugin is upgraded, it will need to be updated on the next startup. There is a good implementation for this, which can be updated as soon as the plugin is updated. It is by replacing the system ClassLoader, and then also each plug-in corresponding to a ClassLoader, you can see the source ZeusClassLoader.

#### 5. Resource handling? Resource processing, the previous article slightly mentioned, Android resource manager creation process, this is also really troublesome, will write a separate article to explain.

6. Conclusion

Finished? There is no such thing, because the starting point of the Activity involves a lot of things, and there are only 5 problems mentioned here (the fifth problem has not been detailed, skip). In the next article, I will refer to many open source plug-in projects to write a relatively complete plug-in Demo of the Activity. After writing the plug-in Demo of the Activity, BroadcastReceiver, Service, and ContentProvider are also useful.