一、什么是静态代理

静态代理是指在编译期就编写好代理类的代码,代理类与目标类一一对应。代理类持有目标对象的引用,在调用目标方法前后插入增强逻辑。
核心结构:

  • 抽象接口:定义目标对象和代理对象共同的行为
  • 目标对象:真正的业务实现
  • 代理对象:持有目标对象引用,对外暴露相同接口,内部转发调用

二、Android 中的代码示例

2.1 基础实现

假设有一个图片加载接口:

// 1. 抽象接口
public interface ImageLoader {
    void loadImage(String url, ImageView imageView);
}

// 2. 目标对象:Glide 封装
public class GlideImageLoader implements ImageLoader {
    @Override
    public void loadImage(String url, ImageView imageView) {
        Glide.with(imageView.getContext())
             .load(url)
             .into(imageView);
    }
}

// 3. 代理对象:添加缓存和日志
public class ImageLoaderProxy implements ImageLoader {
    private ImageLoader realLoader;  // 持有目标对象
    
    public ImageLoaderProxy(ImageLoader realLoader) {
        this.realLoader = realLoader;
    }
    
    @Override
    public void loadImage(String url, ImageView imageView) {
        // 前置增强:日志记录
        Log.d("ImageProxy", "开始加载图片: " + url);
        long startTime = System.currentTimeMillis();
        
        // 调用目标对象
        realLoader.loadImage(url, imageView);
        
        // 后置增强:统计耗时
        long cost = System.currentTimeMillis() - startTime;
        Log.d("ImageProxy", "图片加载完成,耗时: " + cost + "ms");
    }
}

// 4. 使用
ImageLoader realLoader = new GlideImageLoader();
ImageLoader proxy = new ImageLoaderProxy(realLoader);
proxy.loadImage("https://example.com/pic.jpg", imageView);

2.2 带缓存逻辑的代理

public class CacheImageLoaderProxy implements ImageLoader {
    private ImageLoader realLoader;
    private LruCache<String, Bitmap> memoryCache;
    
    public CacheImageLoaderProxy(ImageLoader realLoader) {
        this.realLoader = realLoader;
        // 初始化内存缓存
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        memoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount() / 1024;
            }
        };
    }
    
    @Override
    public void loadImage(String url, ImageView imageView) {
        Bitmap cached = memoryCache.get(url);
        if (cached != null) {
            imageView.setImageBitmap(cached);
            return;
        }
        // 缓存未命中,走真实加载
        realLoader.loadImage(url, imageView);
    }
}

三、Android 系统源码中的静态代理(最经典)

Android 的 Binder IPC 机制 是静态代理最庞大、最精密的应用。以 ActivityManager 为例:

3.1 AIDL 生成的静态代理

当你定义一个 AIDL 接口:

// IBookManager.aidl
interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

编译后自动生成两个关键类:

// 1. Stub:服务端实现
public static abstract class Stub extends Binder implements IBookManager {
    // ...
}

// 2. Proxy:客户端代理(静态代理)
public static class Proxy implements IBookManager {
    private IBinder mRemote;  // 持有远端 Binder 引用
    
    Proxy(IBinder remote) {
        mRemote = remote;
    }
    
    @Override
    public List<Book> getBookList() throws RemoteException {
        Parcel _data = Parcel.obtain();
        Parcel _reply = Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
            _reply.readException();
            return _reply.createTypedArrayList(Book.CREATOR);
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }
}

这就是典型的静态代理

  • Proxy 和 Stub 都实现了 IBookManager 接口
  • Proxy 持有 IBinder mRemote(目标对象)
  • 调用 getBookList() 时,Proxy 将方法调用封装为 Parcel 数据,通过 Binder 驱动发送到服务端
  • 对客户端代码来说,完全感知不到这是跨进程调用

3.2 系统服务中的代理

// 获取 ActivityManager,实际返回的是代理对象
IActivityManager am = ActivityManagerNative.getDefault();

// 调用 startActivity,实际上是通过代理对象发送 Binder 事务
am.startActivity(...);

系统源码中大量存在这种 XXXProxy 命名类:

  • ActivityManagerProxy
  • WindowManagerProxy
  • PackageManagerProxy
  • LocationManagerProxy

它们都是编译期确定的静态代理类,负责将本地方法调用转换为跨进程通信。

四、静态代理 vs 动态代理

对比项 静态代理 动态代理
代理类生成时机 编译期手写/生成 运行时动态生成
代码可见性 有实体 .java 文件 运行时内存中,无实体文件
灵活性 一个代理类对应一个目标类 一个 InvocationHandler 可代理多个目标
代码量 目标类多时代码膨胀 代码简洁,复用性强
性能 直接方法调用,无反射开销 反射调用,有一定性能损耗
Android 典型应用 Binder AIDL 代理、封装层 Retrofit、Mockito
是否需要接口 需要 JDK 代理需要,CGLIB 不需要

五、静态代理的优缺点

✅ 优点

  1. 性能更好:直接方法调用,没有反射开销,在 Android 这种资源受限环境尤为重要
  2. 编译期检查:代理类代码在编译期就确定,IDE 可检查、可跳转、可调试
  3. 结构清晰:代理逻辑一目了然,适合复杂业务场景
  4. 类型安全:强类型,无需强制转换

❌ 缺点

  1. 代码冗余:每增加一个目标类,可能需要新增一个代理类
  2. 维护成本高:接口方法变更时,代理类也要同步修改
  3. 不够灵活:无法像动态代理那样"一个处理器代理所有接口"

六、Android 中的实际应用场景

场景 说明
Binder IPC AIDL 生成的 Stub.Proxy,Android 系统最核心的代理应用
网络层封装 对 OkHttp/Retrofit 做一层静态代理,统一添加 Header、签名、重试逻辑
图片加载封装 对 Glide/Fresco 做代理,添加缓存策略、加载统计、占位图逻辑
数据库封装 对 Room 或 SQLite 操作做代理,添加日志、事务管理
权限代理 代理敏感操作,在调用前统一检查权限
日志/埋点代理 代理某些工具类方法,自动上报使用统计

七、总结

静态代理在 Android 中最大的价值是 Binder IPC:AIDL 编译生成的 Proxy 类,让跨进程调用对开发者完全透明。而在应用层开发中,静态代理适合结构固定、性能敏感的封装场景;如果接口多且变化频繁,则更适合用动态代理(如 Retrofit)来减少样板代码。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐