一句话说透Android里面的代理模式

206 阅读2分钟

一句话总结:
代理模式就像「跨国代购」—— 你找代购下单(调用代理),代购联系海外仓库(真实对象)发货,Android 里跨进程通信全靠这套机制!


一、代理模式在 Android 的核心应用

核心作用控制访问,替身(Proxy)拦截请求并转发给真实对象,常见于:

  1. 跨进程通信(如调用系统服务)
  2. 延迟加载(如大图占位代理)
  3. 权限校验(如检查权限后执行实际逻辑)

二、源码级拆解: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 驱动交互原理

  1. 代理对象(Proxy) :运行在客户端进程(如 App 进程)
  2. Binder 驱动:内核态的中转站(/dev/binder
  3. 真实对象(Stub) :运行在服务端进程(如 system_server)

数据流

App 进程 → ProxyBinder 驱动 → system_server 进程 → StubActivityManagerService  
  • 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 生成静态类,动态代理反射秀
权限校验延迟载,源码处处显身手

注意事项

  1. 跨进程代理注意线程安全(Binder 调用默认非主线程)
  2. 动态代理性能敏感场景慎用(如列表滚动时频繁调用)
  3. 系统服务代理需要权限(如 BIND_ACCESSIBILITY_SERVICE