Design Patterns | Dynamic and Static Agents

Posted by samudasu on Thu, 25 Jul 2019 10:40:50 +0200

Proxy Pattern, also known as Delegate Pattern, is a structural design pattern and a basic design skill. In daily applications, the use of proxy mode is very high. Many other design modes are essentially targeted optimization of proxy mode in a specific context, which shows the importance of proxy mode.
_The first section of this paper elaborates on the definition of agent mode. If you are familiar with the agent mode (static agent), you can skip this part. The second section discusses the dynamic agent and its implementation principle. The third section puts forward some key points of expanding ideas, including most people (and some books). ) Easy thinking shackles, please don't miss it. Section 4 lists some other common design patterns. Can you understand the subtle relationship between them and the agent pattern? The last part is the reference literature.

1. Definition of agent

_proxy model is used to solve two kinds of problems:

  • Control access to underlying objects

  • Additional functionality for access

These are two very simple scenarios, which is why modern processing patterns are often reflected in other design patterns. Refer to the following UML class diagrams and sequence diagrams:

Agent schema UML class diagram
Proxy schema UML sequence diagram

From the view of class diagram, Proxy holds the reference of RealSubject through combination, and implements Subject interface. From the perspective of client, Proxy proxy object can be used as a substitute for Subject interface.
From the time sequence diagram, the client relies on Proxy, which forwards requests to Real Subject. In this case, the proxy object first controls access before forwarding. At this point, we can interpret the proxy pattern as: the proxy object combines the basic object, controls the access to the basic object, and expands the new function.
From the perspective of OOP principle, the agency model mainly embodies:

  • Single accusation principle

    The basic class only needs to pay attention to the business logic itself to ensure the reusability of the business class.

  • Richter's replacement principle

    Subclass objects must be used transparently where base classes are referenced. The Proxy class implements the Subject interface, so that all references to Subject in the client can be transparently replaced by Proxy objects.

  • Open and Close Principle

    Modifications are closed and extensions are open. The implementations in RealSubject classes should be modified only because of errors. Extended features are implemented by creating new Proxy classes, while the Proxy classes reuse the implementations in RealSubject classes through combinations. (Inheritance is also the way to extend code)

  • Dependence Inversion Principle

    High-level modules do not depend on the implementation details of low-level modules. Because the client (high-level module) relies on the Subject interface and does not depend on specific RealSubject objects or Proxy objects.

Differences between inheritance and composition

Inheritance and composition are two ways to realize code reuse and expansion. The difference between them lies in flexibility and encapsulation.
In flexibility, inheritance statically defines the hierarchy of classes at compile time, which is straightforward and easy to use, but in turn, inheritance cannot change reused code at run time; in contrast, composition can modify reused objects at run time by replacing referenced objects.
In terms of encapsulation, inheritance (white box reuse) is intrusive, and subclasses must receive the parent's (non-private) implementations wholly, which destroys the encapsulation to a certain extent. When using combination (black box reuse), objects interact with each other through interfaces, and the encapsulation of objects is protected.

2. Dynamic proxy

2.1 Definition of Dynamic Agent

There are two proxy modes in Java: static proxy and dynamic proxy. The implementation method mentioned in the previous section belongs to static proxy.
Static proxy generates proxy classes by programmers or code generation tools. Class files are generated after compilation. The proxy relationship is bound at compilation time. A proxy relationship is a proxy class corresponding to a basic interface.
Let's assume that many classes need to be extended to the same functionality in the project, such as all network-related business classes that need to print request logs. When using static proxy, in order to add proxy to each business class, the method is to abstract an interface for each business class, create a new proxy class correspondingly, and implement logging function in the proxy class. For example:

public interface HttpApi {
    String get(String url);
}
​
public class RealModule implements HttpApi {
     @Override
     public String get(String url) {
         return "result";
     }
}
​
public class Proxy implements HttpApi {
    private HttpApi target;

    Proxy(HttpApi target) {
        this.target = target;
    }

    @Override
    public String get(String url) {
        // Extended functions
        Log.i("http-statistic", url);
        // Accessing Basic Objects
        return target.get(url);
    }
}

Suppose there is an Other HttpApi interface, you can choose to create a new Other Proxy proxy class, and then you can choose to write code in the Proxy class, so that the Proxy class can continue to proxy Other HttpApi. Either way, there are two shortcomings of static proxy: one is repeatability. When the program is larger, the more methods need proxy, the more repetitive template code. The other is fragility. Once the basic interface is changed, the proxy class must be changed besides all business classes.

The by operator provided by Kotlin can reduce redundant template code when implementing proxies, but unfortunately, it has little effect when decorating methods are needed.

Is there any way to avoid these shortcomings?
_We know that JVM needs to load, connect and initialize the Class file describing class information into the Class object in memory before it really starts to refer to a class. This is the class loading mechanism of JVM. Class files are generated after compilation. Class loading is done at runtime. Although it will increase some performance overhead slightly, it provides a high degree of flexibility for the dynamic extensibility of Java. Class files (rather than source code) are the language of JVM. Java, Groovy, Kotlin and other languages can compile code into Class files. In this case, for JVM, the source code of the proxy class is not necessary. As long as there is a way to get the Class file of the proxy class, class loading can be performed.

Class file is a popular term, representing the stored program's ByteCode, but not necessarily in the form of disk files, can come from the network's binary stream, or even generated at runtime. Regardless of the source, Class files are always converted to byte [] in memory during the loading stage of class loading. After connection and (optional) initialization, a class is loaded.

Dynamic Proxy API is a feature introduced in JDK 1.3. The core APIs are java.lang.reflect.Proxy class and java.lang.reflect.InvocationHandler interface. It uses reflection mechanism to generate bytecode of proxy class at runtime, which brings Java platform the ability to dynamically extend object behavior at runtime. Back to the previous example:

public class ProxyFactory {
    public static HttpApi getProxy(HttpApi target) {
        return (HttpApi) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new LogHandler(target));
    }

    private static class LogHandler implements InvocationHandler {
        private HttpApi target;

        LogHandler(HttpApi target) {
            this.target = target;
        }
        // When the underlying method has no parameters, args is empty or the length is 0
        @Override
        public Object invoke(Object proxy, Method method, @Nullable Object[] args)       
               throws Throwable {
            // Extended functions
            Log.i("http-statistic", (String) args[0]);
            // Accessing Basic Objects
            return method.invoke(target, args);
        }
    }
}

If you need to be compatible with multiple business interfaces, you can use generics at the interface where the agent is generated:

public class ProxyFactory {
    @SuppressWarnings("unchecked")
    public static <T> T getProxy(T target) {
        return (T) Proxy.newProxyInstance(
        target.getClass().getClassLoader(),
        target.getClass().getInterfaces(),
        new LogHandler(target));
    }

    private static class LogHandler<T> implements InvocationHandler {
        ...
    }
}
// Client call:
HttpAPi proxy = ProxyFactory.getProxy<HttpApi>(target);
OtherHttpApi proxy = ProxyFactory.getProxy<OtherHttpApi>(otherTarget);

_Pass different types through generic parameters, and clients can instantiate different types of proxy objects on demand. All methods of the basic interface are unified to the InvocationHandler invoke () processing, even if there are multiple basic businesses that need proxies, there is no need to write too much duplicate template code; when the basic interface changes, synchronous change of the proxy is not necessary, thus avoiding duplication and vulnerability.
_Review static and dynamic agents:

  • Common point: Both proxy mode implementations are based on access control and expansion of the basic object without changing the basic object, which conforms to the open-close principle.
  • Different points: Static proxy has obvious shortcomings of repeatability and vulnerability when the program size is a little larger; Dynamic proxy (with generic parameters) realizes a proxy to handle N basic interfaces simultaneously, which is essentially decoupling between proxy Class and basic interface, and avoids the shortcomings of static proxy to a certain extent. In principle, the proxy Class file of static proxy is generated at compile time, while the proxy Class file of dynamic proxy is generated at run time. The proxy Class does not exist at coding stage, and the proxy relationship is not determined until run time.

2.2 Source Code Analysis of Dynamic Agent

In this section, we will analyze the java.lang.reflect.Proxy source code, understand how the proxy class Class file is generated, and how it unifies method calls into the java.lang.reflect.InvocationHandler interface.
_Lists the public methods of the Proxy class:

public Method of Proxy Class

Why do you need to specify a ClassLoader object?

ClassLoader is equivalent to the namespace of a class, and the uniqueness of a class is determined by itself and the ClassLoader that loads it. If a Class file is loaded by two ClassLoaders, the result is two separate classes.

Proxy.java:
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces){
    final Class<?>[] intfs = interfaces.clone();
    ...
    // Focus: Get the proxy class Class object
    return getProxyClass0(loader, intfs);
}
​
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
    ...
    final Class<?>[] intfs = interfaces.clone();
    // Focus: Get the proxy class Class object
    Class<?> cl = getProxyClass0(loader, intfs);
    ...
    // Get the proxy class constructor
    // private static final Class<?>[] constructorParams = { InvocationHandler.class };
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    final InvocationHandler ih = h;
    ...
    // Create an instance
    return newInstance(cons, ih);
}
​
public static boolean isProxyClass(Class<?> cl) {
    // Proxy subclasses are checked, and the proxyClassCache has caches
    return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
}
​
public static InvocationHandler getInvocationHandler(Object proxy){
     // Checks are proxy objects
    if (!isProxyClass(proxy.getClass())) {
        throw new IllegalArgumentException("not a proxy instance");
    }
    final Proxy p = (Proxy) proxy;
    final InvocationHandler ih = p.h;
    ...
    // Returns the InvocationHandler object
    return ih;
}

As you can see, both Proxy getProxyClass () and Proxy new ProxyInstance () call Proxy getProxyClass0 () to get the proxy class Class object. The latter also obtains a constructor with Invocation Handler as a parameter, and finally creates and returns an instance of the proxy object. Look at the Proxy getProxyClass0 () source code again:

Proxy.java:
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
    ...
    // Get the proxy class from the cache, and if the cache misses, generate the proxy class through ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
    // Naming prefix of proxy class
    private static final String proxyClassNamePrefix = "$Proxy";
    // Proxy class named suffix, incremental from 0 (atomic Long)
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            // Verify that the same class is loaded in the parameters interfaces and lassLoder
            // Verification parameter interface is the interface type
            // There are no duplicates in the validation parameter interface
            // Otherwise throw Illegal ArgumentException
        }
        // Verify that all non-public interfaces come from the same package

        //(Generally) Proxy class package name
        // public static final String PROXY_PACKAGE = "com.sun.proxy";
        String proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";

        // Fully qualified name of proxy class
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        // Focus: Generating bytecode data
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
        // Emphasis: Generating Class objects from bytecodes
        return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length); 
    }
}

_ProxyClassFactory_DefneClass0 () is a native method (similarly, the final assignment method of ClassLoader_DefneClass () is also a native method), and the classification loading process from bytecode to Class object is continued by JVM. Focus on how ProxyGenerator generateProxyClass () generates proxy class bytecode data:

Tip: The steps of generating bytecode and generating Class object from bytecode in Android system are implemented native ly.
private static native Class<?> generateProxy(...)
The corresponding native method: dalvik/vm/native/java_lang_reflect_Proxy.cpp

ProxyGenerator.java:
public static byte[] generateProxyClass(final String var0, Class[] var1) {
    ProxyGenerator var2 = new ProxyGenerator(var0, var1);
    // Whether to save to disk file 
    final byte[] var3 = var2.generateClassFile();
    if (saveGeneratedFiles) {
        ...
         return var3;
    }
}
private byte[] generateClassFile() {
    // Proxy Object-only hashCode, equals, and toString
    this.addProxyMethod(hashCodeMethod, Object.class);
    this.addProxyMethod(equalsMethod, Object.class);
    this.addProxyMethod(toStringMethod, Object.class);

    // Each method of proxy interface
    ...
    for(var1 = 0; var1 < this.interfaces.length; ++var1) {
        ...
    }
    // Important: Add a constructor with InvocationHandler parameters
    this.methods.add(this.generateConstructor());
    var7 = this.proxyMethods.values().iterator();
    while(var7.hasNext()) {
        ...
        // Emphasis: InvocationHandler#invoke() is called in each proxy's method
    }
    ByteArrayOutputStream var9 = new ByteArrayOutputStream();
    DataOutputStream var10 = new DataOutputStream(var9);
    ...
    return var9.toByteArray();
}

We can see that ProxyGenerator generateProxyClass () is a static public method. We call it directly and write the proxy Class class file to the disk file. We use the decompilation function of IntelliJ IDEA to view the source code:

// Client:
byte[] classFile = ProxyGenerator.generateProxyClass("$proxy0",new Class[]{HttpApi.class});
// Write directly to the project path to facilitate the use of IntelliJ IDEA decompilation function
String path = "/Users/pengxurui/IdeaProjects/untitled/src/proxy/HttpApi.class";
try(FileOutputStream fos = new FileOutputStream(path)){
    fos.write(classFile);
    fos.flush();
    System.out.println("success");
} catch (Exception e){
    e.printStackTrace();
    System.out.println("fail");
}
public final class $proxy0 extends Proxy implements HttpApi {
    //Reflected metadata methods are stored to avoid duplicate creation
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    /**
     * Object#hashCode()
     * Object#equals(Object)
     * Object#toString()
     */

    // Implementing HttpApi interface
    public final String get() throws  {
        try {
            //Forward to Invocation#invoke()
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            //Object#hashCode()
            //Object#equals(Object)
            //Object#toString()
            m3 = Class.forName("HttpApi").getMethod("get");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

_At this point, we have finished the process of generating the proxy class Class file, which can be concluded as follows:
_The dynamically generated proxy class is named com.sun.proxy$Proxy [the number starting from zero] (for example, com.sun.proxy$Proxy 0), inherits the java.lang.reflect.Proxy class, has a constructor parameter for the InvocationHandler interface, implements the specified basic interface, and forwards method calls to InvocationHandler8560;i#i Nvoke (). Referring to the UML class diagram, carefully understand the red arrows in the diagram, the proxy relationship between the proxy class (Prox(Prox $0)) and the HttpApi interface is determined at run time:

Dynamic Agent UML Class Diagram

3. Key Points of Expanding Thoughts

3.1 Face-Oriented Programming

OOP (Object Oriented Progarming) is a programming idea that abstracts business process into a clear hierarchical structure using encapsulation, inheritance and polymorphism.
AOP (Aspect Oriented Programming) is a programming idea that maintains program modularity by separating cross-cutting concerns.
AOP is the complement and perfection of OOP. AOP pays more attention to the cross-cutting steps or stages in business processing. It extracts the cross-cutting steps affecting multiple steps from the core business module unit separately, named Aspect, which reduces the intrusiveness of cross-cutting steps. For example, the logs, security control, exception handling and interception points are proposed separately as cross-cutting concerns, which is conducive to separate maintenance and will not change the original business module. Both static proxy and dynamic proxy are methods to implement AOP programming.

3.2 View Concept

Looking back at the UML class diagrams of static and dynamic agents, we have RealModule classes that implement the HttpApi interface. But does it have to be an implementation relationship? If not, is it still a proxy model?

Dynamic Agent UML Class Diagram

If someone points to your program and says, "It's not the agency model! There's a missing interface here. Would you nod your head obediently? Remind me that design patterns are not an end in themselves. Solving problems is. Don't design for design!

Sometimes in life, the obedient are just fools, but the non-obedient are talents. —— Cai Kangyong's Way of Speaking 2

Many people (and some books) have two misunderstandings:

  • The disadvantage of dynamic proxy is that interface proxy can not implement class proxy (X)

    The dynamic proxy is the interface proxy, and there is no error in this statement. Indeed, the parameters of Proxy getProxyClass () and Proxy newProxyInstance () can only receive the Class object of the interface, and if the Class object of the class is passed in, Illegal ArgumentException will be thrown. So why did the author (Peter Jones) design this way?

    Semantically, class proxies provide proxies for each method of a class, including methods declared in the class (Class# getDeclared Methods ()) and methods declared in the parent class. As a result, the client will face a potentially large and bloated interface, but the client may not be interested in every method. Even if we do not consider the performance loss caused by generating agents for redundant methods (a certain amount of CPU computing time and object memory occupancy), exposing too many interfaces violates the minimum interface principle: dependencies between classes should be based on the minimum interface. In order to reduce coupling, it is still necessary to map the object to a more appropriate interface after instantiating the proxy object. In this case, why not generate the proxy class from the beginning according to the appropriate interface?

  • Business objects must implement basic interfaces, otherwise they cannot use dynamic proxy (X)

    The idea may come from classes that don't implement any interfaces, so there's no way to get the Class object of the interface as a parameter to Proxy#newProxyInstance(), which does cause some trouble, for example:

package com.domain;
public interface HttpApi {
    String get();
}

// Another package's non-public interface
package com.domain.inner;
/**non-public**/interface OtherHttpApi{
    String get();
}

package com.domain.inner;
// Other HttpApiImpl class does not implement the HttpApi interface or does not implement any interfaces
public class OtherHttpApiImpl  /**extends OtherHttpApi**/{
    public String get() {
        return "result";
    }
}

// Client:
 HttpApi api = (HttpApi) Proxy.newProxyInstance(...}, new InvocationHandler() {
    OtherHttpApiImpl impl = new OtherHttpApiImpl();
  
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO: Extended New Functions
        // IllegalArgumentException: object is not an instance of declaring class
        return method.invoke(impl,args);
    }
});
api.get();

In this example, the other HttpApiImpl class has not implemented the HttpApi interface for historical reasons. Although the method signature is identical to the method signature of the HttpApi interface, unfortunately, the parameter method comes from the HttpApi interface, not the other HttpApiImpl class. There is also a remedy to find the Mthod with the same signature in the HttpApi interface and use this method to forward the call:

HttpApi api = (HttpApi) Proxy.newProxyInstance(...}, new InvocationHandler() {
    OtherHttpApiImpl impl = new OtherHttpApiImpl();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO: Extended New Functions
        if (method.getDeclaringClass() != impl.getClass()) {
            // How to Find the Same Signature
            Method realMethod = impl.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
            return realMethod.invoke(impl, args);
        }else{
            return method.invoke(impl,args);
        }
    }
});

In fact, no one stipulates that the method invoke (...) must be invoked to forward the call to the business object. Both direct and reflection calls can access the basic object! Reviewing the definition of proxy pattern again: the proxy object combines the basic object, controls the access to the basic object, and expands the new function.
_The concept of "view" is mentioned here, which allows any object to be regarded as any interface, and some mappings are used internally when the underlying object and interface do not match. Perhaps you will understand the idea of adapter mode and appearance mode, exactly! But the latter two are usually static transformations. The power of dynamic agents lies in that, as mentioned earlier, one agent handles N basic interfaces at the same time, and the agent relationship is determined at runtime. Imagine that one (or a few) base objects can proxy the entire application's interface, or tens of thousands of application's interfaces. What a magic! The famous Retrofit framework is an example of this.

4. Relevant design patterns

At the beginning of the article, it is mentioned that many other design patterns are essentially targeted optimization of proxy patterns in specific contexts. There are indeed similarities between design patterns, and the key difference is that they focus on/emphasize different purposes. Again, design patterns are ideas that guide the solution of software problems, not constraints.

4.1 Decoration Mode

Decorate Pattern refers to adding additional functions to the base object dynamically. Compared with proxy pattern, it emphasizes more on enhancing the function of the decorated object.

4.2 Appearance Model

Facade Pattern encapsulates logic and interaction between subsystems, opens behavior to high-level interfaces, emphasizes the principle of minimum interface, reduces the coupling of high-level modules to subsystems, and makes interfaces easier to use by encapsulating external interfaces. Appearance patterns are frequently used in the development process. For example, many frameworks provide a unified high-level interface in order to reduce user costs.
The Context class in Android is a typical example of the appearance pattern. Context is an abstract class, which is usually translated into context, referring to the basic information of the running environment of the program. The Context class has two direct subclasses, the ContextImpl class and the ContextWrapper class. Refer to the following UML class diagram:

Context class UML class diagram

_Subclass ContextImpl class is the appearance class, the real method implementation is not in the ContextImpl class, but in many subsystems encapsulated internally, such as AMS Management Activity (and other components) state, PMS Management Application Package related information, Resource Management Resource System. Another subclass, ContextWrapper, has three direct subclasses: Service, ContextThemeWrapper and Application, which provide different extensions, referring to the following source code:

  • Application
ActivityThread.java:
private void handleBindApplication(AppBindData data){
    ...
    // Create an Application instance
    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    mInitialApplication = app;
    ...
    // Focus: Callback onCreate()
    mInstrumentation.callApplicationOnCreate(app);
    ...
}

public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
    // Avoid duplication
    if (mApplication != null) {
        return mApplication;
    }
    ...
    // Create the underlying object ContextImpl
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    // Focus: Creating Application Instances
    app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
    appContext.setOuterContext(app);
    ...
    return app;
}

Instrumentation.java:
public Application newApplication(ClassLoader cl, String className, Context context){
    Application app = getFactory(context.getPackageName())
                .instantiateApplication(cl, className);
    // Focus: Setting up basic objects
    app.attach(context);
    return app;
}
  • Activity
ActivityThread.java:
/**Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // Get ActivityInfo information
    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                Context.CONTEXT_INCLUDE_CODE);
    }
    // Getting Component Information
    ComponentName component = r.intent.getComponent();
    if (component == null) {
        component = r.intent.resolveActivity(mInitialApplication.getPackageManager());
        r.intent.setComponent(component);
    }
    // Focus: Create the underlying object ContextImpl
    ContextImpl appContext = createBaseContextForActivity(r);
    // Create Activity instances
    java.lang.ClassLoader cl = appContext.getClassLoader();
    Activity activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
    ...
    // Getting Application Objects
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);  
    appContext.setOuterContext(activity);
    // Focus: Setting up basic objects
    activity.attach(appContext, this, app, ...);
    // Setting up Intent
    if (customIntent != null) {
    activity.mIntent = customIntent;
    }
    ...
    // set up themes
    int theme = r.activityInfo.getThemeResource();
    if (theme != 0) {
        activity.setTheme(theme);
    }
    // Focus: Callback onCreate()
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    ...
}   
  • Service
ActivityThread.java:
private void handleCreateService(CreateServiceData data) {
    ...
    // Focus: Creating Service Instances
    Service service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
    ...
    // Create the underlying object ContextImpl
    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
    context.setOuterContext(service);
    // Get an Application instance
    Application app = packageInfo.makeApplication(false, mInstrumentation);
    // Focus: Setting up basic objects
    service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
    // Focus: Callback onCreate()
    service.onCreate();
    ...
}

_Analyzing the initial source code of Application, Service and Activity, we can see that their processes of dealing with the basic object ContextImpl are very similar: creating the basic object ContextImpl, calling attach(), calling ContextWrapper attachBaseContext () internally, and finally establishing the proxy relationship with ContextImpl.

Activity.java:
public class Activity extends ContextWrapper{
    final void attach(Context context,ActivityThread aThread,Application application,...){
         ...
        attachBaseContext(context);
        mMainThread = aThread;
        mApplication = application;c
    }
}

ContextWrapper.java:
public class ContextWrapper extends Context{
    private Context mBase;
        protected void attachBaseContext(Context base){
            ...
            mBase = base;
        }
}

5. Reference

Recommended reading

Thank you for enjoying it! Your praise is my greatest encouragement! If you have any questions, you can leave a message in the comments section below.

Topics: Java Android Programming jvm