点赞关注,不再迷路,你的支持对我意义重大!
🔥 Hi,我是丑丑。本文 「Java 路线」| 导读 —— 梦开始的地方 已收录,这里有 Android 进阶成长路线笔记 & 博客,欢迎跟着彭丑丑一起成长。(联系方式在 GitHub)
前言
- 代理模式(Proxy Pattern)也称委托模式(Delegate Pattern),是一种结构型设计模式,也是一项基础设计技巧。在日常应用中,代理模式的使用频率非常高,许多其他的设计模式本质上是在特定的上下文中,对代理模式进行的针对性优化,由此可见其重要性。
- 本文的第一节阐述了代理模式的定义,如果你已经对代理模式(静态代理)比较熟悉了,可以跳过这个部分;第二节讨论了动态代理及其实现原理;第三个节提出了一些扩展思路的要点,其中包含了多数人(和一部分书籍)容易犯的思维桎梏,请不要错过它。第四节列举了其他一些常见的设计模式,你能理解它们和代理模式的微妙关系吗?最后的部分是参考的文献资料。
1. 代理的定义
代理模式用于解决两种问题:
- 控制对基础对象的访问
- 在访问时增加额外的功能
这是两种非常朴素的场景,正因如此,才会常常从其他设计模式中发现代理模式的影子,参考下面的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方法:
为什么需要指定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接口的代理关系是在运行时才确定的:
3. 扩展思路的要点
3.1 面向切面编程
OOP(Object Oriented Progarmming,面向对象编程)是一种使用封装、继承、多态将业务处理过程抽象为一个个清晰的层级结构的编程思想。
AOP(Aspect Oriented Programming,面向切面编程)是一种通过分离横切关注点(cross-cutting concerns)来维持程序模块化的编程思想。
AOP是OOP的补充和完善,AOP更关注业务处理中的横切步骤或阶段,将影响多个步骤的横切步骤从核心业务模块单元单独提炼出来,命名为切面(Aspect),降低了横切步骤的侵入性。比如将日志、安全控制、异常处理、拦截点作为横切关注点单独提出,既有利于单独维护,又不会改动原有的业务模块。静态代理和动态代理都是实现AOP编程的方法。
3.2 视图的概念
回顾静态代理和动态代理的UML类图,我们有RealModule类实现了HttpApi接口。但是,必须有这层实现关系吗,如果没有,还算是代理模式吗?
如果有人指着你的程序说:“不是代理模式!这里少了一个接口”,你会乖乖点头吗?提醒一下,设计模式本身并不是目的,解决问题才是。不要为了设计而设计!
人生有时候,乖乖的只是呆子,不乖的却是才子啊! —— 《蔡康永的说话之道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类图:
子类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;
}
}
推荐阅读
- Java | ThreadLocal 线程本地存储
- Android | 文件存储
- Android | 再按一次返回键退出
- Android | InputManagerService 与输入事件采集
- Cocos Creator | 封装音频池工具
- 笔记 | Android Studio 极速编译
- 笔记 | 使用 Keytool 管理密钥和证书
参考
- 《Android源码设计模式解析与实战》何红辉,关爱民 著
- 《修炼Java开发技术:在架构中体验设计模式和算法之美》于广 著
- 《深入理解Android内核设计思想》林学森 著
- Wikipedia:Aspect-oriented programming
- JavaWorld:Explore the Dynamic Proxy API
- JavaWorld:Generically chain dynamic proxies
创作不易,你的「三连」是丑丑最大的动力,我们下次见!