代理模式——基础的设计

作者: 彭旭锐 | 来源:发表于2019-07-07 16:40 被阅读289次

      代理模式(Proxy Pattern)也称委托模式(Delegate Pattern),是一种结构型设计模式,也是一项基础设计技巧。在日常应用中,代理模式的使用频率非常高,许多其他的设计模式本质上是在特定的上下文中,对代理模式进行的针对性优化,由此可见其重要性。
      本文的第一节阐述了代理模式的定义,如果你已经对代理模式(静态代理)比较熟悉了,可以跳过这个部分;第二节讨论了动态代理及其实现原理;第三个节提出了一些扩展思路的要点,其中包含了多数人(和一部分书籍)容易犯的思维桎梏,请不要错过它。第四节列举了其他一些常见的设计模式,你能理解它们和代理模式的微妙关系吗?最后的部分是参考的文献资料。

    1. 代理的定义

      代理模式用于解决两种问题:

    • 控制对基础对象的访问

    • 在访问时增加额外的功能

      这是两种非常朴素的场景,正因如此,才会常常从其他设计模式中发现代理模式的影子,参考下面的UML类图和时序图:

    代理模式UML 类图 代理模式UML 时序图

      从类图看,代理对象(Proxy)通过组合的方式持有基础对象(RealSubject)的引用,并且实现了Subject接口,从客户端的角度,Proxy代理对象可以作为Subject接口的替代品。
      从时序图看,客户端依靠代理对象(Proxy)工作,代理对象将请求转发给基础对象(RealSubject),在这个例子中,代理对象在转发之前先进行了访问控制。到这里,我们可以把代理模式解释为:由代理对象组合基础对象,控制对基础对象的访问,扩展新的功能。
      从OOP的原则看,代理模式主要体现了:

    • 单一指责原则

      基础类只需要关注业务逻辑本身,保证了业务类的可重用性。

    • 里氏替换原则

      引用基类的地方必须能透明地使用子类对象。Proxy类实现了Subject接口,因此客户端中引用Subject的地方都可以透明地替换为Proxy对象。

    • 开闭原则

      对修改是封闭的,对扩展是开放的。RealSubject类中的实现只应该因错误而修改,扩展的特性通过新建Proxy类实现,而Proxy类通过组合的方式重用了RealSubject类中的实现。(继承也是扩展代码的方式)

    • 依赖倒置原则

      高层次的模块不依赖于低层次模块的实现细节。因为客户端(高层模块)依赖于Subject接口,并不依赖于具体的RealSubject对象或Proxy对象。

    继承和组合的区别

    继承和组合是实现代码复用和扩展的两种方式,两者的差别体现在在灵活性和封装性上。
    灵活性上,继承在编译期静态地定义了类的层次结构,直白明了,易于使用,但是反过来看,继承不能在运行期改变复用的代码;相比之下,组合在运行期可以通过替换引用的对象的方式来修改复用的对象。
    封装性上,继承(白盒复用)有侵入性,子类必须全盘接收父类的(非私有)内部实现,一定程度上破坏了封装;使用组合(黑盒复用)时对象间通过接口相互作用,对象的封装性得到保护。

    2. 动态代理

    2.1 动态代理的定义

      Java中有两种代理模式:静态代理和动态代理,上一节提到的实现方式属于静态代理。
      静态代理由程序员或代码生成工具生成代理类,编译之后生成Class文件,代理关系在编译期就已经绑定,一个代理关系是一个代理类对应一个基础接口。
      假设项目中需要给很多类扩展相同的功能,比如所有和网络有关的业务类需要打印请求日志。使用静态代理时,为了给每个业务类添加代理,做法是为每个业务类抽象一个接口,对应地新建一个代理类,并在代理类中实现日志功能。例如:

    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) {
            // 扩展的功能
            Log.i("http-statistic", url);
            // 访问基础对象
            return target.get(url);
        }
    }
    

      假设有一个OtherHttpApi接口,可以选择新建一个OtherProxy代理类,退而求其次也可以选择将代码写在Proxy类中,让Proxy类继续代理OtherHttpApi。无论哪种选择,都无法规避静态代理的两个缺点:一是重复性,在程序规模稍大时,需要代理的方法越多,重复的模板代码就越多;二是脆弱性,基础接口一旦改动,除了所有业务类需要改动外,代理类也必须改动。

    Kotlin提供的by操作符可以在实现代理时减少冗余的模板代码,但是很遗憾,在需要装饰方法时收效甚微。

      有没有办法规避这些缺点呢?
      我们知道,JVM在真正开始引用一个类之前,需要先经过加载、连接、初始化,将描述类信息的Class文件转换为内存中Class对象,这就是JVM的类加载机制。(一般地)Class文件在编译后生成,类加载的工作是在运行时完成,虽然会稍微增加一些性能开销,但是却给Java带来的动态扩展的特性,提供了高度的灵活性。Class文件(而不是源代码)是JVM的语言,Java、Groovy、Kotlin等其他语言都可以把代码编译称Class文件。既然如此,对于JVM来说,代理类的源代码并不是必须的,只要有办法得到代理类的Class文件,就可以执行类加载。

    Class文件是一个通俗的说法,表示存储程序的二进制字节码(ByteCode),但不一定以磁盘文件的形式存在,可以来自网络上的二进制流,甚至在运行时计算生成。不论哪种来源,Class文件总在类加载的加载阶段转变为内存中byte[]的形式,经过连接和(可选的)初始化后,一个类才算加载完成。

      动态代理(Dymanic Proxy API)是JDK1.3中引入的特性,核心API是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。它利用反射机制在运行时生成代理类的字节码,为Java平台在带来了运行时动态扩展对象行为的能力。回到前面例子:

    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;
            }
            // method底层的方法无参数时,args为空或者长度为0
            @Override
            public Object invoke(Object proxy, Method method, @Nullable Object[] args)       
                   throws Throwable {
                // 扩展的功能
                Log.i("http-statistic", (String) args[0]);
                // 访问基础对象
                return method.invoke(target, args);
            }
        }
    }
    

      如果需要兼容多个业务接口,可以在生成代理的接口处使用泛型:

    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 {
            ...
        }
    }
    
    // 客户端调用:
    HttpAPi proxy = ProxyFactory.getProxy<HttpApi>(target);
    OtherHttpApi proxy = ProxyFactory.getProxy<OtherHttpApi>(otherTarget);
    

      通过泛型参数传递不同的类型,客户端可以按需实例化不同类型的代理对象。基础接口的所有方法都统一到InvocationHandler#invoke() 处理,即使有多个基础业务需要代理,也不需要编写过多重复的模板代码;当基础接口变更时,同步改动代理并不是必须的,从而规避了重复性和脆弱性。
      回顾下静态代理和动态代理:

    • 共同点:两种代理模式实现都在不改动基础对象的前提下,对基础对象进行访问控制和扩展,符合开闭原则。
    • 不同点:静态代理在程序规模稍大时,重复性和脆弱性的缺点凸显;动态代理(搭配泛型参数)实现了一个代理同时处理N多个基础接口,本质上是代理类和基础接口的解耦,一定程度上规避了静态代理的缺点。从原理上讲,静态代理的代理类Class文件在编译期生成,而动态代理的代理类Class文件在运行时生成,代理类在coding阶段并不存在,代理关系直到运行时才确定。

    2.2 动态代理的源码分析

      这一节,我们将分析java.lang.reflect.Proxy源码,理解代理类Class文件是如何生成的,以及它如何将方法调用统一到java.lang.reflect.InvocationHandler接口中处理。
      先列出Proxy类的public方法:

    Proxy类的public方法

    为什么需要指定ClassLoader对象?

    ClassLoader相当于类的命名空间,类的唯一性由它本身和加载它的ClassLoader确定。一个Class文件如果由两个ClassLoader加载,结果是两个独立的类。

    Proxy.java:
    public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces){
        final Class<?>[] intfs = interfaces.clone();
        ...
        // 重点:获得代理类Class对象
        return getProxyClass0(loader, intfs);
    }
    ​
    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
        ...
        final Class<?>[] intfs = interfaces.clone();
        // 重点:获得代理类Class对象
        Class<?> cl = getProxyClass0(loader, intfs);
        ...
        // 获得代理类构造器
        // private static final Class<?>[] constructorParams = { InvocationHandler.class };
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        ...
        // 创建实例
        return newInstance(cons, ih);
    }
    ​
    public static boolean isProxyClass(Class<?> cl) {
        // 检查时Proxy的子类,并且proxyClassCache中有缓存
        return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
    }
    ​
    public static InvocationHandler getInvocationHandler(Object proxy){
         // 检查是代理对象
        if (!isProxyClass(proxy.getClass())) {
            throw new IllegalArgumentException("not a proxy instance");
        }
        final Proxy p = (Proxy) proxy;
        final InvocationHandler ih = p.h;
        ...
        // 返回InvocationHandler对象
        return ih;
    }
    

      可以看到,Proxy#getProxyClass() 和 Proxy#newProxyInstance() 都调用了 Proxy#getProxyClass0() 获得代理类Class对象,后者还获取了一个以InvocationHandler为参数的构造器,最终创建并返回了一个代理对象实例。再看Proxy#getProxyClass0() 源码:

    Proxy.java:
    private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
        ...
        // 从缓存中获取代理类,如果缓存未命中,则通过ProxyClassFactory生成代理类
        return proxyClassCache.get(loader, interfaces);
    }
    private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
        // 代理类命名前缀
        private static final String proxyClassNamePrefix = "$Proxy";
        // 代理类命名后缀,从0递增(原子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) {
                // 验证参数interfaces和ClassLoder中加载的是同一个类
                // 验证参数interfaces是接口类型
                // 验证参数interfaces中没有重复项
                // 否则抛出IllegalArgumentException
            }
            // 验证所有non-public接口来自同一个包
    
            //(一般地)代理类包名
            // public static final String PROXY_PACKAGE = "com.sun.proxy";
            String proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    
            // 代理类的全限定名
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
            // 重点:生成字节码数据
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
            // 重点:从字节码生成Class对象
            return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length); 
        }
    }
    

      ProxyClassFactory#defineClass0()是native方法(类似地,ClassLoader#defineClass()最终委派的方法也是native方法),从字节码到Class对象的这部分类加载过程由JVM继续进行。重点看ProxyGenerator#generateProxyClass()如何生成代理类字节码数据:

    Tip:Android系统中生成字节码和从字节码生成Class对象的步骤都在native实现:
    private static native Class<?> generateProxy(…)
    对应的native方法: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);
        // 是否保存至磁盘文件 
        final byte[] var3 = var2.generateClassFile();
        if (saveGeneratedFiles) {
            ...
             return var3;
        }
    }
    private byte[] generateClassFile() {
        // 只代理Object的hashCode、equals和toString
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
    
        // 代理接口的每个方法
        ...
        for(var1 = 0; var1 < this.interfaces.length; ++var1) {
            ...
        }
        // 重点:添加带有InvocationHandler参数的构造器
        this.methods.add(this.generateConstructor());
        var7 = this.proxyMethods.values().iterator();
        while(var7.hasNext()) {
            ...
            // 重点:在每个代理的方法中调用InvocationHandler#invoke()
        }
        ByteArrayOutputStream var9 = new ByteArrayOutputStream();
        DataOutputStream var10 = new DataOutputStream(var9);
        ...
        return var9.toByteArray();
    }
    

      可以看到ProxyGenerator#generateProxyClass() 是一个静态public方法,我们直接调用,并将代理类Class文件写入磁盘文件,使用IntelliJ IDEA的反编译功能查看源代码:

    // Client:
    byte[] classFile = ProxyGenerator.generateProxyClass("$proxy0",new Class[]{HttpApi.class});
    // 直接写入项目路径下,方便使用IntelliJ IDEA的反编译功能
    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 {
        //反射的元数据Method存储起来,避免重复创建
        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()
         */
    
        // 实现了HttpApi接口
        public final String get() throws  {
            try {
                //转发到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());
            }
        }
    }
    

      到这里就走完生成代理类Class文件的流程,可以得出:
      动态生成的代理类命名为com.sun.proxy$Proxy[从0开始的数字](例如:com.sun.proxy​$Proxy0),继承了java.lang.reflect.Proxy类,有一个参数为InvocationHandler接口的构造器,同时实现了指定的基础接口,并将方法调用转发给InvocationHandler#invoke()。参考UML类图,仔细体会图中红色的箭头,代理类(Prox$0)和HttpApi接口的代理关系是在运行时才确定的:

    动态代理UML 类图

    3. 扩展思路的要点

    3.1 面向切面编程

      OOP(Object Oriented Progarmming,面向对象编程)是一种使用封装、继承、多态将业务处理过程抽象为一个个清晰的层级结构的编程思想。
      AOP(Aspect Oriented Programming,面向切面编程)是一种通过分离横切关注点(cross-cutting concerns)来维持程序模块化的编程思想。
      AOP是OOP的补充和完善,AOP更关注业务处理中的横切步骤或阶段,将影响多个步骤的横切步骤从核心业务模块单元单独提炼出来,命名为切面(Aspect),降低了横切步骤的侵入性。比如将日志、安全控制、异常处理、拦截点作为横切关注点单独提出,既有利于单独维护,又不会改动原有的业务模块。静态代理和动态代理都是实现AOP编程的方法。

    3.2 视图的概念

      回顾静态代理和动态代理的UML类图,我们有RealModule类实现了HttpApi接口。但是,必须有这层实现关系吗,如果没有,还算是代理模式吗?

    动态代理UML 类图

      如果有人指着你的程序说:“不是代理模式!这里少了一个接口”,你会乖乖点头吗?提醒一下,设计模式本身并不是目的,解决问题才是。不要为了设计而设计!

    人生有时候,乖乖的只是呆子,不乖的却是才子啊! —— 《蔡康永的说话之道2》

      很多人(和一部分书籍)存在两个误解:

    • 动态代理的缺点是接口代理,不能实现类代理(X)

      动态代理是接口代理,这句话本身没有错误。的确,Proxy#getProxyClass() 和Proxy#newProxyInstance() 的参数只能接收接口的Class对象,如果传入类的Class对象,会抛出IllegalArgumentException。那么作者(Peter Jones)为什么这么设计?

      从语义上,类代理是为类的每个方法提供代理,这就包括类中声明的方法(Class#getDeclaredMethods())和父类中声明的方法。结果是,客户端将面对一个可能庞大而臃肿的接口,但是客户端可能并不对每个方法都感兴趣。即使暂不考虑为冗余的方法生成代理带来的性能损失(一定的CPU计算时间和对象内存占用),暴露过多接口其实违背了最小接口原则:类间的依赖关系应该建立在最小接口上。为了降低耦合,实例化代理对象后依然需要将对象映射为一个更合适的接口,既然如此,为什么不一开始就根据这个合适的接口来生成代理类呢?

    • 业务对象必须实现基础接口,否则不能使用动态代理(X)

      这个想法可能来自于一些没有实现任何接口的类,因此就没有办法得到接口的Class对象作为Proxy#newProxyInstance() 的参数,这确实会带来一些麻烦,举个例子:

    package com.domain;
    public interface HttpApi {
        String get();
    }
    
    // 另一个包的non-public接口
    package com.domain.inner;
    /**non-public**/interface OtherHttpApi{
        String get();
    }
    
    package com.domain.inner;
    // OtherHttpApiImpl类没有实现HttpApi接口或者没有实现任何接口
    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:扩展的新功能
            // IllegalArgumentException: object is not an instance of declaring class
            return method.invoke(impl,args);
        }
    });
    api.get();
    

      这个例子里,OtherHttpApiImpl类因为历史原因没有实现HttpApi接口,虽然方法签名与HttpApi接口的方法签名完全相同,但是遗憾,参数method来自于HttpApi接口,而不是OtherHttpApiImpl类。也有补救的办法,找到HttpApi接口中签名相同的Mthod,使用这个Method来转发调用:

    HttpApi api = (HttpApi) Proxy.newProxyInstance(...}, new InvocationHandler() {
        OtherHttpApiImpl impl = new OtherHttpApiImpl();
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // TODO:扩展的新功能
            if (method.getDeclaringClass() != impl.getClass()) {
                // 找到相同签名的方法
                Method realMethod = impl.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
                return realMethod.invoke(impl, args);
            }else{
                return method.invoke(impl,args);
            }
        }
    });
    

      其实没有人规定一定要调用Method#invoke(...)转发调用到业务对象,直接调用和反射调用都可以访问基础对象!再次回顾代理模式的定义:由代理对象组合基础对象,控制对基础对象的访问,扩展新的功能。
      这里提到“视图”的概念,即允许将任何对象视为任何接口,当基础对象和接口不匹配时,内部使用一些映射。也许你会理解为适配器模式和外观模式的思想,exactly!只是后两者通常是一种静态转换,动态代理的威力在于,如前所述:一个代理同时处理N多个基础接口,代理关系在运行时确定。试想一下,一个(或少数)基础对象可以代理整个应用的接口,或者数以万计的应用的接口。What a magic! 有名Retrofit框架就是这样的例子。

    4. 相关的设计模式

      文章开头提到,许多其他设计模式本质上是在特定的上下文中,对代理模式进行的针对性优化。设计模式之间确实有相似的地方,关键的区别在于它们关注/强调的目的不同。再提醒一次,设计模式是指导解决软件问题的思想,而不是约束。

    4.1 装饰模式

      装饰模式(Decorate Pattern)指动态地给基础对象添加额外的功能,相对于代理模式,更多强调的是增强所装饰对象的功能。

    4.2 外观模式

      外观模式(Facade Pattern),封装子系统间的逻辑和交互,将行为开放给高层接口,强调的是最小接口原则,降低了高层模块对于子系统的耦合,对外观接口的封装也使得接口更易用。外观模式在开发过程中运用频率非常高,比如很多框架为了降低用户的使用成本,会提供一个统一的高层接口。
      Android中的Context类就是外观模式的典型例子,Context是一个抽象类,通常翻译为上下文,指程序运行环境的基本信息。Context类有两个直接子类,ContextImpl类和ContextWrapper类,参考如下UML类图:

    Context类UML 类图

      子类ContextImpl类就是外观类,真正的方法实现并不在ContextImpl类中,而在其内部封装的多个子系统中,比如AMS管理Activity(及其他组件)状态,PMS管理应用包相关的信息,Resource管理资源系统。另一个子类ContextWrapper有三个直接子类:Service、ContextThemeWrapper、Application,分别提供了不同的扩展,参考以下源码:

    • Application
    ActivityThread.java:
    private void handleBindApplication(AppBindData data){
        ...
        // 创建Application实例
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;
        ...
        // 重点:回调onCreate()
        mInstrumentation.callApplicationOnCreate(app);
        ...
    }
    
    public Application makeApplication(boolean forceDefaultAppClass,
                Instrumentation instrumentation) {
        // 避免重复构造
        if (mApplication != null) {
            return mApplication;
        }
        ...
        // 创建基础对象ContextImpl
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        // 重点:创建Application实例
        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);
        // 重点:设置基础对象
        app.attach(context);
        return app;
    }
    
    • Activity
    ActivityThread.java:
    /**Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // 获取ActivityInfo信息
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
        // 获取Component信息
        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }
        // 重点:创建基础对象ContextImpl
        ContextImpl appContext = createBaseContextForActivity(r);
        // 创建Activity实例
        java.lang.ClassLoader cl = appContext.getClassLoader();
        Activity activity = mInstrumentation.newActivity(
                        cl, component.getClassName(), r.intent);
        ...
        // 获取Application对象
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);  
        appContext.setOuterContext(activity);
        // 重点:设置基础对象
        activity.attach(appContext, this, app, ...);
        // 设置Intent
        if (customIntent != null) {
        activity.mIntent = customIntent;
        }
        ...
        // 设置主题
        int theme = r.activityInfo.getThemeResource();
        if (theme != 0) {
            activity.setTheme(theme);
        }
        // 重点:回调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) {
        ...
        // 重点:创建Service实例
        Service service = packageInfo.getAppFactory()
                        .instantiateService(cl, data.info.name, data.intent);
        ...
        // 创建基础对象ContextImpl
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service);
        // 获取Application实例
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        // 重点:设置基础对象
        service.attach(context, this, data.info.name, data.token, app,
                        ActivityManager.getService());
        // 重点:回调onCreate()
        service.onCreate();
        ...
    }
    

      分析Application、Service、Activity的初始化源码,可以看到它们处理基础对象ContextImpl的流程是非常相似的:创建基础对象ContextImpl,并调用attach(),内部调用ContextWrapper#attachBaseContext() ,最终建立起与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. 参考

    相关文章

      网友评论

        本文标题:代理模式——基础的设计

        本文链接:https://www.haomeiwen.com/subject/ddnbhctx.html