Android随笔-静态代理
·
一、什么是静态代理
静态代理是指在编译期就编写好代理类的代码,代理类与目标类一一对应。代理类持有目标对象的引用,在调用目标方法前后插入增强逻辑。
核心结构:
- 抽象接口:定义目标对象和代理对象共同的行为
- 目标对象:真正的业务实现
- 代理对象:持有目标对象引用,对外暴露相同接口,内部转发调用
二、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 不需要 |
五、静态代理的优缺点
✅ 优点
- 性能更好:直接方法调用,没有反射开销,在 Android 这种资源受限环境尤为重要
- 编译期检查:代理类代码在编译期就确定,IDE 可检查、可跳转、可调试
- 结构清晰:代理逻辑一目了然,适合复杂业务场景
- 类型安全:强类型,无需强制转换
❌ 缺点
- 代码冗余:每增加一个目标类,可能需要新增一个代理类
- 维护成本高:接口方法变更时,代理类也要同步修改
- 不够灵活:无法像动态代理那样"一个处理器代理所有接口"
六、Android 中的实际应用场景
| 场景 | 说明 |
|---|---|
| Binder IPC | AIDL 生成的 Stub.Proxy,Android 系统最核心的代理应用 |
| 网络层封装 | 对 OkHttp/Retrofit 做一层静态代理,统一添加 Header、签名、重试逻辑 |
| 图片加载封装 | 对 Glide/Fresco 做代理,添加缓存策略、加载统计、占位图逻辑 |
| 数据库封装 | 对 Room 或 SQLite 操作做代理,添加日志、事务管理 |
| 权限代理 | 代理敏感操作,在调用前统一检查权限 |
| 日志/埋点代理 | 代理某些工具类方法,自动上报使用统计 |
七、总结
静态代理在 Android 中最大的价值是 Binder IPC:AIDL 编译生成的 Proxy 类,让跨进程调用对开发者完全透明。而在应用层开发中,静态代理适合结构固定、性能敏感的封装场景;如果接口多且变化频繁,则更适合用动态代理(如 Retrofit)来减少样板代码。
更多推荐

所有评论(0)