10.Android 设计模式 核心模式之四动态代理 在商业项目中的落地

摘要:

Android动态代理 利用Java的java.lang.reflect.Proxy在运行时创建接口的代理实例,通过InvocationHandler拦截方法调用。开发者可在方法执行前后注入逻辑(如日志、权限校验),实现无侵入的功能增强。适用于AOP编程、简化回调、网络请求封装(如Retrofit)等场景。其核心价值是解耦核心逻辑与横切关注点,提升代码复用性和灵活性。需注意系统版本兼容性及代理接口的限制。

1.概念

动态代理是一种在运行时动态创建代理对象的设计模式,通过代理类控制对真实对象的访问。核心组件:

动态代理,代理的是什么? 是接口,那么类里面的方法,可以根据名字进行选择!

  • 抽象接口:定义代理对象和真实对象的共同接口
  • 真实对象:实现核心业务逻辑的目标类
  • InvocationHandler:方法调用处理器,在代理对象方法被调用时执行拦截逻辑
  • Proxy:动态生成代理类的工厂

问题:用了Proxy,就算动态代理么?

✅ 核心价值:无侵入式增强功能(日志、权限、监控等),符合开闭原则

2.在Android源码中的应用场景

2.1. Retrofit网络框架

ini 复制代码
java

```
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).build();
ApiService service = retrofit.create(ApiService.class); // 动态代理生成接口实例
```

代理对象将接口方法转换为HTTP请求

2.2. ActivityManager

go 复制代码
java

```
IActivityManager am = ActivityManager.getService();
// 系统通过动态代理处理Binder跨进程通信
```

2.3. AIDL跨进程通信

ini 复制代码
```
private IService mService;
mService = IService.Stub.asInterface(binder); // 动态代理生成远程服务代理
```

3.UML图

4.语音项目的例子和没用设计模式的对比

场景:语音识别SDK需要添加网络监控和日志记录

4.1 未使用设计模式的语音识别 SDK 实现
typescript 复制代码
public class BasicVoiceRecognizer {
    private String currentLanguage = "zh-CN";

    // 核心业务方法 - 被辅助功能代码污染
    public String recognize(String audioData) {
        // 1. 网络检查(重复代码)
        if (!checkNetwork()) {
            throw new RuntimeException("网络不可用");
        }

        // 2. 日志记录(重复代码)
        logAction("recognize", audioData);

        // 3. 性能监控(重复代码)
        long startTime = System.currentTimeMillis();

        // 实际业务逻辑(被非业务代码包围)
        System.out.println("Processing audio data: " + audioData.substring(0, 10) + "...");
        String result = "识别结果: " + audioData.hashCode();

        // 4. 性能日志(重复代码)
        long duration = System.currentTimeMillis() - startTime;
        System.out.println("METHOD PERFORMANCE: recognize executed in " + duration + "ms");

        return result;
    }

    // 另一个方法也需要重复相同的辅助代码
    public void setLanguage(String language) {
        // 网络检查(重复)
        if (!checkNetwork()) {
            throw new RuntimeException("网络不可用");
        }

        // 日志记录(重复)
        logAction("setLanguage", language);

        // 性能监控(重复)
        long startTime = System.currentTimeMillis();

        // 实际业务逻辑
        this.currentLanguage = language;
        System.out.println("Language set to: " + language);

        // 性能日志(重复)
        long duration = System.currentTimeMillis() - startTime;
        System.out.println("METHOD PERFORMANCE: setLanguage executed in " + duration + "ms");
    }

    // 重复的辅助方法 - 网络检查
    private boolean checkNetwork() {
        // 模拟网络检查
        boolean isConnected = Math.random() > 0.2;
        System.out.println("NETWORK CHECK: " + (isConnected ? "Connected ✓" : "Disconnected ✗"));
        return isConnected;
    }

    // 重复的辅助方法 - 日志记录
    private void logAction(String methodName, Object arg) {
        System.out.println("METHOD CALL: " + methodName + " | ARGS: [" + arg + "]");
    }
}
java 复制代码
public class BasicVoiceClient {
    public static void main(String[] args) {
        BasicVoiceRecognizer recognizer = new BasicVoiceRecognizer();

        try {
            // 设置语言
            recognizer.setLanguage("en-US");

            // 模拟多次识别调用
            for (int i = 0; i < 3; i++) {
                String audioData = "audio_sample_" + System.currentTimeMillis();
                String result = recognizer.recognize(audioData);
                System.out.println("Recognition Result: " + result);
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            System.err.println("Recognition failed: " + e.getMessage());
        }
    }
}

问题

  • 违反单一职责原则
  • 修改需侵入业务代码
  • 重复代码遍布各方法
4.2 使用静态代理的语音识别 SDK 实现
java 复制代码
/**
 * @Author pengcaihua
 * @Date 15:07
 * @describe
 */
// 静态代理实现(对比方案)
public class VoiceRecognizerProxy implements IVoiceRecognizer {
    private final GoogleVoiceRecognizer realRecognizer;

    public VoiceRecognizerProxy() {
        this.realRecognizer = new GoogleVoiceRecognizer();
    }

    @Override
    public String recognize(String audioData) {
        if (!NetworkUtils.isConnected()) {
            throw new RuntimeException("Network unavailable");
        }
        System.out.println("METHOD CALL: recognize | ARGS: [" + audioData + "]");
        long start = System.currentTimeMillis();
        String result = realRecognizer.recognize(audioData);
        long duration = System.currentTimeMillis() - start;
        System.out.println("METHOD PERFORMANCE: recognize executed in " + duration + "ms");
        return result;
    }

    @Override
    public void setLanguage(String language) {
        // 需要为每个方法重复代理逻辑
        if (!NetworkUtils.isConnected()) {
            throw new RuntimeException("Network unavailable");
        }
        System.out.println("METHOD CALL: setLanguage | ARGS: [" + language + "]");
        long start = System.currentTimeMillis();
        realRecognizer.setLanguage(language);
        long duration = System.currentTimeMillis() - start;
        System.out.println("METHOD PERFORMANCE: setLanguage executed in " + duration + "ms");
    }
}

静态代理问题

  1. 每个方法都需要重复代理代码
  2. 新增接口方法时必须修改代理类
  3. 无法动态添加/移除代理功能
  4. 代理逻辑与接口耦合度高
4.3 使用动态代理的语音识别 SDK 实现
arduino 复制代码
// 1. 定义核心接口
public interface IVoiceRecognizer {
    String recognize(String audioData);  // 语音识别方法
    void setLanguage(String language);   // 设置识别语言
}
typescript 复制代码
// 2. 真实对象 - 实现核心业务逻辑
public class GoogleVoiceRecognizer implements IVoiceRecognizer {
    private String currentLanguage = "zh-CN";

    @Override
    public String recognize(String audioData) {
        // 模拟实际语音识别处理(核心业务)
        System.out.println("Processing audio data: " + audioData.substring(0, 10) + "...");
        return "识别结果: " + audioData.hashCode();
    }

    @Override
    public void setLanguage(String language) {
        this.currentLanguage = language;
        System.out.println("Language set to: " + language);
    }
}
java 复制代码
public class VoiceProxyHandler implements InvocationHandler {
    private final Object target;
    private long lastNetworkCheck = 0;
    private static final long NETWORK_CHECK_INTERVAL = 5000; // 5秒缓存

    public VoiceProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置处理:网络状态检查(带缓存机制)
        if (System.currentTimeMillis() - lastNetworkCheck > NETWORK_CHECK_INTERVAL) {
            if (!checkNetwork()) {
                throw new RuntimeException("Network unavailable");
            }
            lastNetworkCheck = System.currentTimeMillis();
        }

        // 方法调用日志
        logMethodCall(method, args);

        // 记录方法开始时间
        long startTime = System.currentTimeMillis();

        // 执行原始方法
        Object result = method.invoke(target, args);

        // 后置处理:性能监控
        long duration = System.currentTimeMillis() - startTime;
        System.out.println("METHOD PERFORMANCE: " + method.getName() +
                " executed in " + duration + "ms");

        return result;
    }

    private boolean checkNetwork() {
        // 模拟网络检查(实际项目中替换为真实网络检查)
        boolean isConnected = Math.random() > 0.2; // 80%成功率
        System.out.println("NETWORK CHECK: " + (isConnected ? "Connected ✓" : "Disconnected ✗"));
        return isConnected;
    }

    private void logMethodCall(Method method, Object[] args) {
        String logMsg = "METHOD CALL: " + method.getName();
        if (args != null && args.length > 0) {
            logMsg += " | ARGS: " + Arrays.toString(args);
        }
        System.out.println(logMsg);
    }
}
java 复制代码
// 5. 客户端使用 - 包含错误处理
public class VoiceRecognitionClient {
    public static void main(String[] args) {
        // 创建真实对象
        IVoiceRecognizer realRecognizer = new GoogleVoiceRecognizer();

        // 创建代理处理器
        VoiceProxyHandler handler = new VoiceProxyHandler(realRecognizer);

        // 动态生成代理对象
        IVoiceRecognizer proxy = (IVoiceRecognizer) java.lang.reflect.Proxy.newProxyInstance(
                IVoiceRecognizer.class.getClassLoader(),
                new Class[]{IVoiceRecognizer.class},
                handler
        );

        // 使用代理对象
        try {
            // 设置语言
            proxy.setLanguage("en-US");

            // 模拟多次识别调用
            for (int i = 0; i < 3; i++) {
                String audioData = "audio_sample_" + System.currentTimeMillis();
                String result = proxy.recognize(audioData);
                System.out.println("Recognition Result: " + result);
                Thread.sleep(1000); // 模拟间隔
            }
        } catch (Exception e) {
            System.err.println("Recognition failed: " + e.getMessage());
        }
    }
}
4.4 架构图

动态代理在语音识别SDK中完美实现了:

  • 无侵入式添加网络监控
  • 自动化的方法级日志记录
  • 统一性能监控

性能对比测试数据

scss 复制代码
测试条件:执行10,000次识别调用

无设计模式:
  平均耗时:32ms
  内存开销:稳定
  
静态代理:
  平均耗时:35ms (+9%)
  内存开销:增加1个代理对象
  
动态代理:
  平均耗时:48ms (+50%)
  内存开销:增加2个对象(代理+处理器)
  
优化后动态代理(缓存Method对象):
  平均耗时:38ms (+19%)

三维对比分析

维度 无设计模式 静态代理 动态代理
架构复杂度 ⭐ (简单但混乱) ⭐⭐ (类数量增加) ⭐⭐⭐ (理解成本略高)
代码量 方法数×辅助代码量 接口方法数×代理模板 固定(与接口方法无关)
新增方法成本 高(需完整实现) 高(需添加代理方法) 零(自动继承代理逻辑)
功能扩展性 差(需修改业务代码) 中(修改代理类) 高(新增Handler即可)
横切关注点 分散在各方法中 集中在代理类但重复 统一在单个处理器中
运行时灵活性 低(代理行为编译时确定) 高(可动态切换处理器)
性能影响 无额外开销 微小(直接调用) 轻微(反射调用开销)
适用场景 简单工具类/一次性代码 小型稳定接口 大中型系统/框架开发

5.优点

  1. 解耦核心逻辑与辅助功能
  2. 运行时动态增强对象
  3. 符合开闭原则:新增功能不修改原有代码
  4. 减少重复代码:横切关注点(日志/事务等)集中处理
  5. 灵活扩展:通过组合不同InvocationHandler实现多功能叠加

6.和相似的设计模式的区别

6.1 静态代理和动态代理的区别
模式 核心区别 应用场景
静态代理 代理类需手动编码,1:1对应目标类 编译期确定的固定增强
动态代理 运行时自动生成代理,通用拦截逻辑 AOP场景/需动态管理的对象
🔑 关键区别:动态代理在字节码层面操作,无需预先定义代理类,通过反射动态处理所有方法调用
6.2 静态代理和策略的区别:

静态代理包含策略:

相同点:2者都是要设置一个接口,但是策略在于外面的调用,代理是里面的实现,包含了具体的实现内容

arduino 复制代码
  public VoiceRecognizerProxy(IVoiceRecognizer service) {
        this.realService = service;
    }
    
arduino 复制代码
   public VoiceRecognitionService(RecognitionStrategy strategy) {
        this.strategy = strategy;
    }
    
6.3 动态代理和AOP的相同点和不同点 实现方式对比
特性 动态代理 AOP
实现机制 运行时反射创建代理对象 编译时/加载时/运行时字节码增强
依赖 Java 标准库 (java.lang.reflect) 需要框架支持(AspectJ, Spring AOP 等)
切入点支持 仅限接口方法 方法、构造函数、字段访问、异常处理等
织入方式 手动编码创建代理 自动织入(通过编译器或框架)
6.4 代理模式和装饰器模式的区别

之所以把这两个放在一起说,是因为这两种模式很像,所以这里简单介绍下他们之间的区别,主要有两点。

  1. 装饰器模式关注于在一个对象上动态的添加方法,而代理模式关注于控制对对象的访问
  2. 代理模式,代理类可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。而当我们使用装饰器模式的时候,通常的做法是将原始对象作为一个参数传给装饰者的构造器

总结:在Android开发中,动态代理特别适合处理横切关注点(如日志、权限、性能监控),尤其在框架设计(如Retrofit)和系统服务调用场景中优势明显。合理使用可实现高内聚低耦合的架构,但需注意反射带来的性能开销(可通过缓存优化)。

项目的地址: github.com/pengcaihua1...

相关推荐
赋范大模型技术社区14 分钟前
【LangChain 实战】多智能体协作实现浏览器自动化丨Agents 运行流程丨多工具串&并联调用
架构·github·代码规范
步、步、为营41 分钟前
.net微服务框架dapr保存和获取状态
微服务·架构·.net
敖行客 Allthinker2 小时前
云原生安全观察:零信任架构与动态防御的下一代免疫体系
安全·ai·云原生·架构·kubernetes·ebpf
星辰大海的精灵3 小时前
FastAPI开发AI应用,多厂商模型使用指南
人工智能·后端·架构
前端付豪3 小时前
2、前端架构三要素:模块化、工程化、平台化
前端·javascript·架构
timeweaver3 小时前
前端救星:玩转 Nginx 配置,10 倍提升你的项目部署体验 🚀
前端·架构
uhakadotcom3 小时前
刚刚,Golang更新了, 1.24.5 与 Go 1.23.11有啥新能力?
后端·面试·架构
DemonAvenger4 小时前
Go中UDP编程:实战指南与使用场景
网络协议·架构·go
Jay Kay10 小时前
TensorFlow内核剖析:分布式TensorFlow架构解析与实战指南
分布式·架构·tensorflow