可以说代理模式是 23 种设计模式中最重要的设计模式之一了,代理模式也是非常常用的一种设计模式了,有句很有名的话:软件开发中遇到的所有问题,都可以通过增加一层抽象而得以解决,如果不行就再加一层(开个玩笑)。在 Android 开发中我们都会使用到图片加载框架,图片加载框架也是层出不穷,比较有名的有: UniversalImageLoader 、 Picasso 、 Glide 、 Fresco 等。这么多图片加载框架,我们到底应该用那个呢,这不是我们要讨论的问题。现在假设我们选择了 UniversalImageLoader 作为我们的图片加载库,但是过了一段时间我们发现 UniversalImageLoader 不能满足我们的需求,我们需要切换为 Glide 了,如果我们之前所有用到图片加载的地方都是直接调用 UniversalImageLoader 的 API 的,那么可想而知,这个切换图片加载库的工作量是非常大的,这里就是我们代理模式的用武之地了,我们可以在图片加载库可我们的业务逻辑代码层之间封装一层,也就是所谓的代理层,如果有了代理层,我们再切换图片加载库就只需要修改代理层的代码,这个工作量就会小很多。可以看到代理模式使得客户端和被代理对象之间耦合性降低,程序的扩展性更强了。
代理模式的定义和使用场景
为其他对象提供一种代理以控制对这个对象的访问。
当不能或不想直接访问某个对象,或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象和代理对象要实现相同的接口。
代理模式 UML 和示例代码
下面是代理模式的通用模板代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34abstract class Subject
{
public abstract void Request();
}
class RealSubject extends Subject
{
public override void Request()
{
//业务方法具体实现代码
}
}
class Proxy extends Subject
{
private RealSubject realSubject = new RealSubject(); //维持一个对真实主题对象的引用
public void PreRequest()
{
…...
}
public void Request()
{
PreRequest();
realSubject.Request(); //调用真实主题对象的方法
PostRequest();
}
public void PostRequest()
{
……
}
}
静态代理和动态代理
静态代理
代理模式的定义和模板代码在上面已经介绍过了,这个小节介绍的是静态代理和动态代理。静态代理就是由程序员编写代理类的源码,再编译代理类,也就是说在程序运行前代理类的字节码文件已经存在了,代理类和委托类的关系在运行前就已经确定了。我们上面章节介绍的就是静态代理,所以在这里就不再介绍静态代理了。下面我们介绍动态代理。
动态代理
动态代理类的源码是在程序运行期间由 JVM 根据反射机制动态生成的,所以不存在代理类的字节码文件,代理类和委托类的关系是在程序运行时确定的。
与动态代理类有关的 Java API 有: Proxy 类和 InvocationHandler 类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//Proxy类的静态方法
// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)
// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)
// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
//InvocationHandler的核心方法
// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
Object invoke(Object proxy, Method method, Object[] args)
下面是动态代理的一个示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53public interface IUser {
/**
* 判断用户的权限
*
* @param uid 用户的UID
* @return 是否认证通过
*/
boolean isAuthUser(int uid);
}
public class UserImpl implements IUser {
public boolean isAuthUser(int uid) {
//做一些权限验证的工作
System.out.println(uid);
//....
return false;
}
}
public class UserJDKProxy implements InvocationHandler {
// 需要代理的类
private Object target;
/**
* 绑定委托对象并返回一个代理类 ClassLoader loader:类加载器 Class<?>[] interfaces:得到全部的接口 InvocationHandler
* h:得到InvocationHandler接口的子类实例
*
* @param target
* @return
*/
public Object initUserJDKProxy(Object target) {
this.target = target;
// 可以看出这里的第二个参数是获取接口,那么也就是说我们实现代理,需要类去实现接口,在有的时候,类是没有接口的,所以这里是一个缺陷
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy:这里拦截处理一下事情....,如监控参数、插入日志....");
//传入处理对象和参数
Object object = method.invoke(target, args);
System.out.println("proxy:这里做一些收尾工作....");
return object;
}
}
//客户端调用
private void authUser() {
UserJDKProxy userJDKProxy = new UserJDKProxy();
IUser iUser = (IUser) userJDKProxy.initUserJDKProxy(new UserImpl());
iUser.isAuthUser(19);
}
可以看到动态代理类通过一个代理类来代理多个委托类,接口中声明的所有方法都被转移到了 InvocationHandler 的 invoke 方法中去处理了,这样可以在 invoke 方法中统一做一些事情,例如 Spring 的 AOP 实现原理就是动态代理。但是动态代理也有美中不足的缺点,那就是动态代理只能代理接口,这是因为动态生成的代理类已经默认继承了 Proxy 类,所以不能代理 class 了。
Android 源码中的代理模式
代理模式在 Android 源码中最典型的应用当属 IPC 中的 AIDL 了,对于 AIDL 不熟悉的同学可以点击查看Android Studio AIDL 实战 。 Android 源码中有很多 ManagerService 和很多 Manager ,其中它们之间的通信就是 Binder ,当然也会涉及到代理模式。例如 ActivityManagerNative 、 ActivityManagerService 和 ActivityManagerProxy 。下面我们就缕一缕这三者之间的关系。
首先是 ActivityManagerNative 类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
//...
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case START_ACTIVITY_TRANSACTION:
{
//....
return true;
}
case START_ACTIVITY_AS_USER_TRANSACTION:
{
//.....
return true;
}
}
public IBinder asBinder() {
return this;
}
class ActivityManagerProxy implements IActivityManager{
public ActivityManagerProxy(IBinder remote)
{
mRemote = remote;
}
public IBinder asBinder()
{
return mRemote;
}
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
//...
return result;
}
public int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options,
int userId) throws RemoteException {
//....
return result;
}
}
}
如果看过我这篇文章Android Studio AIDL 实战 或者对于 AIDL 比较熟悉的话大家应该能感觉到 ActivityManagerNative 的结构和 通过 AIDL 生成的类几乎是一模一样,这也说明了 ActivityManagerService 和 ActivityManager 的通信就是采用了 Binder 。和 AIDL 生成的类一样, ActivityManagerProxy 就是 ActivityManagerNative 的内部类。 ActivityManagerNative 是抽象类,而它的实现类正是 ActivityManagerService 。它们三者的关系如下图所示:
通过上图可以看到 ActivityManagerProxy 和 ActivityManagerNative 都实现了 IActivityManager 接口,严格来说 ActivityManagerProxy 就是代理, ActivityManagerNative 就是委托,但是由于 ActivityManagerNative 是个抽象类,它的具体实现是 ActivityManagerService ,所以说 ActivityManagerProxy 代理的是 ActivityManagerService。
总结
代理模式应用广泛,通过使用代理模式可以极大的提高代码的扩展性,降低耦合度。但是若大量使用代理模式则会生成数量众多的代理类,为了减少代理类的数量可以使用动态代理。