一句话总结:
代理模式就像「跨国代购」—— 你找代购下单(调用代理),代购联系海外仓库(真实对象)发货,Android 里跨进程通信全靠这套机制!
一、代理模式在 Android 的核心应用
核心作用:控制访问,替身(Proxy)拦截请求并转发给真实对象,常见于:
- 跨进程通信(如调用系统服务)
- 延迟加载(如大图占位代理)
- 权限校验(如检查权限后执行实际逻辑)
二、源码级拆解:AIDL 生成的 Proxy 类
以启动 Activity 为例,分析 ActivityManagerProxy 如何代理 ActivityManagerService:
1. 定义接口(IActivityManager)
// frameworks/base/core/java/android/app/IActivityManager.aidl
interface IActivityManager {
int startActivity(in Intent intent, ...);
// 其他系统服务方法...
}
2. 生成代理类(ActivityManagerProxy)
编译后自动生成:
// out/.../android/app/IActivityManager.java
public static class Proxy implements IActivityManager {
private android.os.IBinder mRemote; // Binder 代理对象
public Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public int startActivity(Intent intent, ...) throws RemoteException {
// 1. 打包数据
Parcel data = Parcel.obtain();
data.writeInterfaceToken(DESCRIPTOR);
data.writeTypedObject(intent, 0);
// 2. 通过 Binder 发送数据
mRemote.transact(Stub.TRANSACTION_startActivity, data, reply, 0);
// 3. 读取结果
int result = reply.readInt();
return result;
}
}
3. 客户端调用流程
// 开发者调用
context.startActivity(intent);
// 实际调用链
Activity → ContextImpl → Instrumentation → ActivityManagerProxy → Binder驱动 → ActivityManagerService
三、底层 Binder 驱动交互原理
- 代理对象(Proxy) :运行在客户端进程(如 App 进程)
- Binder 驱动:内核态的中转站(
/dev/binder) - 真实对象(Stub) :运行在服务端进程(如 system_server)
数据流:
App 进程 → Proxy → Binder 驱动 → system_server 进程 → Stub → ActivityManagerService
- Proxy 将方法调用转换为
Binder.transact() - Stub 在服务端解析数据并调用实际方法
四、动态代理 vs 静态代理
1. 静态代理(AIDL 生成)
- 手动编写接口和代理类
- 优点:性能高(直接调用)
- 缺点:每个接口都要写代理类
2. 动态代理(Retrofit 使用)
// 动态生成接口实现类
public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] { service },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 根据方法注解生成 HTTP 请求
return parseMethod(method, args);
}
});
}
- 运行时生成代理类(如
$Proxy0) - 优点:灵活,减少模板代码
- 缺点:反射调用有性能损耗
五、代理模式的实际应用场景
1. 权限检查代理
// 代理类控制真实对象的访问
public class PermissionProxy implements Downloader {
private RealDownloader realDownloader;
@Override
public void download(String url) {
if (checkPermission()) {
realDownloader.download(url);
} else {
throw new SecurityException("Need storage permission!");
}
}
}
2. 图片加载占位代理
// 先显示占位图,实际图片加载完成再替换
public class ImageProxy extends ImageView {
private RealImage realImage;
public void setImageUrl(String url) {
showPlaceholder(); // 显示占位图
new Thread(() -> {
realImage = loadFromNetwork(url);
runOnUiThread(() -> setImage(realImage));
}).start();
}
}
六、源码中的代理模式彩蛋
1. InputMethodManager 的窗口会话代理
// frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java
final IInputMethodSession mImeSession;
// 输入法会话的实际代理
private static final class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
// 代理输入法会话的输入操作
}
2. Hook 系统服务的动态代理
// 使用动态代理拦截系统服务调用(需反射)
IBinder clipboardBinder = ServiceManager.getService("clipboard");
Class<?> stubClass = Class.forName("android.content.IClipboard$Stub");
Object clipboard = stubClass.getMethod("asInterface", IBinder.class).invoke(null, clipboardBinder);
// 创建代理对象
Object proxy = Proxy.newProxyInstance(
clipboard.getClass().getClassLoader(),
clipboard.getClass().getInterfaces(),
new ClipboardHookHandler(clipboard));
// 替换系统服务
ServiceManager.class.getMethod("addService", String.class, IBinder.class)
.invoke(null, "clipboard", ((IInterface) proxy).asBinder());
七、总结口诀
「代理模式像代购,跨进程通信它最牛
AIDL 生成静态类,动态代理反射秀
权限校验延迟载,源码处处显身手」
注意事项:
- 跨进程代理注意线程安全(Binder 调用默认非主线程)
- 动态代理性能敏感场景慎用(如列表滚动时频繁调用)
- 系统服务代理需要权限(如
BIND_ACCESSIBILITY_SERVICE)