Android动态代理以及利用动态代理实现ServiceHook

Java 的动态代理

首先我们要介绍的就是 Java 动态代理,Java 的动态代理涉及到两个类:InvocationHandler 接口和 Proxy 类,下面我们会着重介绍一下这两个类,并且结合实例来着重分析一下使用的正确姿势等。在这之前简单介绍一下 Java 中 class 文件的生成和加载过程,Java 编译器编译好 Java 文件之后会在磁盘中产生 .class 文件。这种 .class 文件是二进制文件,内容是只有 JVM 虚拟机才能识别的机器码,JVM 虚拟机读取字节码文件,取出二进制数据,加载到内存中,解析 .class 文件内的信息,使用相对应的 ClassLoader 类加载器生成对应的 Class 对象:

.class 字节码文件是根据 JVM 虚拟机规范中规定的字节码组织规则生成的,具体的 .class 文件格式介绍可以查看博客 深入理解Java Class文件格式 和 Java 虚拟机规范。

通过上面我们知道 JVM 是通过字节码的二进制信息加载类的,那么我们如果在运行期系统中,遵循 Java 编译系统组织 .class 文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据转换成对应的类,这样就可以在运行中动态生成一个我们想要的类了:

Java 中有很多的框架可以在运行时根据 JVM 规范动态的生成对应的 .class 二进制字节码,比如 ASM 和 Javassist 等,这里就不详细介绍了,感兴趣的可以去查阅相关的资料。这里我们就以动态代理模式为例来介绍一下我们要用到这两个很重要的类,关于动态代理模式,我在 java/android 设计模式学习笔记(9)—代理模式中已经介绍过了,但是当时并没有详细分析过 InvocationHandler 接口和 Proxy 类,这里就来详细介绍一下。在代理模式那篇博客中,我们提到了代理模式分为动态代理和静态代理:

上面就是静态代理模式的类图,当在代码阶段规定这种代理关系时,ProxySubject 类通过编译器生成 .class 字节码文件,当系统运行之前,这个 .class 文件就已经存在了。动态代理模式的结构和上面的静态代理模式的结构稍微有所不同,它引入了一个 InvocationHandler 接口和 Proxy 类。在静态代理模式中,代理类 ProxySubject 中的方法,都指定地调用到特定 RealSubject 中对应的方法,ProxySubject 所做的事情无非是调用触发 RealSubject 对应的方法;动态代理工作的基本模式就是将自己方法功能的实现交给 InvocationHandler 角色,外界对 Proxy 角色中每一个方法的调用,Proxy 角色都会交给 InvocationHandler 来处理,而 InvocationHandler 则调用 RealSubject 的方法,如下图所示:

InvocationHandler 接口和 Proxy 类

我们来分析一下动态代理模式中 ProxySubject 的生成步骤:

  1. 获取 RealSubject 上的所有接口列表;
  2. 确定要生成的代理类的类名,系统默认生成的名字为:com.sun.proxy.$ProxyXXXX ;
  3. 根据需要实现的接口信息,在代码中动态创建该 ProxySubject 类的字节码;
  4. 将对应的字节码转换为对应的 Class 对象;
  5. 创建 InvocationHandler 的实例对象 h,用来处理 Proxy 角色的所有方法调用;
  6. 以创建的 h 对象为参数,实例化一个 Proxy 角色对象。

具体的代码为:

Subject.java

 
 
 
 
  1. public interface Subject {
  2.     String operation();

RealSubject.java

 
 
 
 
  1. public class RealSubject implements Subject{
  2.     @Override
  3.     public String operation() {
  4.         return "operation by subject";
  5.     }

ProxySubject.java

 
 
 
 
  1. public class ProxySubject implements InvocationHandler{
  2.      protected Subject subject;
  3.     public ProxySubject(Subject subject) {
  4.         this.subject = subject;
  5.     }
  6.     @Override
  7.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8.         //do something before
  9.         return method.invoke(subject, args);
  10.     }

测试代码

 
 
 
 
  1. Subject subject = new RealSubject();
  2. ProxySubject proxy = new ProxySubject(subject);
  3. Subject sub = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
  4.         subject.getClass().getInterfaces(), proxy);
  5. sub.operation(); 

以上就是动态代理模式的最简单实现代码,JDK 通过使用 java.lang.reflect.Proxy 包来支持动态代理,我们来看看这个类的表述:

 
 
 
 
  1. Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the 
  2. superclass of all dynamic proxy classes created by those methods. 

一般情况下,我们使用下面的

 
 
 
 
  1. public static Object newProxyInstance(ClassLoader loader, Class[]interfaces,InvocationHandler h) throws IllegalArgumentException { 
  2.     // 检查 h 不为空,否则抛异常
  3.     if (h == null) { 
  4.         throw new NullPointerException(); 
  5.     } 
  6.     // 获得与指定类装载器和一组接口相关的代理类类型对象
  7.     Class cl = getProxyClass(loader, interfaces); 
  8.     // 通过反射获取构造函数对象并生成代理类实例
  9.     try { 
  10.         Constructor cons = cl.getConstructor(constructorParams); 
  11.         return (Object) cons.newInstance(new Object[] { h }); 
  12.     } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); 
  13.     } catch (IllegalAccessException e) { throw new InternalError(e.toString()); 
  14.     } catch (InstantiationException e) { throw new InternalError(e.toString()); 
  15.     } catch (InvocationTargetException e) { throw new InternalError(e.toString()); 
  16.     } 

Proxy 类的 getProxyClass 方法调用了 ProxyGenerator 的 generatorProxyClass 方法去生成动态类:

 
 
 
 
  1. public static byte[] generateProxyClass(final String name, Class[] interfaces)

这个方法我们下面将会介绍到,这里先略过,生成这个动态类的字节码之后,通过反射去生成这个动态类的对象,通过 Proxy 类的这个静态函数生成了一个动态代理对象 sub 之后,调用 sub 代理对象的每一个方法,在代码内部,都是直接调用了 InvocationHandler 的 invoke 方法,而 invoke 方法根据代理类传递给自己的 method 参数来区分是什么方法,我们来看看 InvocationHandler 类的介绍:

 
 
 
 
  1. InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
  2. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy 
  3. instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler. 
Public methods  
abstract Object invoke(Object proxy, Method method, Object[] args)Processes a method invocation on a proxy instance and returns the result.

方法的参数和返回:

Parameters  
proxy Object: the proxy instance that the method was invoked on
method Method: the Method instance corresponding to the interface method invoked on the proxy instance. The declaring class of the Method object will be the interface that the method was declared in, which may be a superinterface of the proxy interface that the proxy class inherits the method through.
args Object: an array of objects containing the values of the arguments passed in the method invocation on the proxy instance, or null if interface method takes no arguments. Arguments of primitive types are wrapped in instances of the appropriate primitive wrapper class, such as java.lang.Integer or java.lang.Boolean.
Returns  
Object the value to return from the method invocation on the proxy instance. If the declared return type of the interface method is a primitive type, then the value returned by this method must be an instance of the corresponding primitive wrapper class; otherwise, it must be a type assignable to the declared return type. If the value returned by this method is null and the interface method’s return type is primitive, then a NullPointerException will be thrown by the method invocation on the proxy instance. If the value returned by this method is otherwise not compatible with the interface method’s declared return type as described above, a ClassCastException will be thrown by the method invocation on the proxy instance.

上面提到的一点需要特别注意的是,如果 Subject 类中定义的方法返回值为 8 种基本数据类型,那么在 ProxySubject 类中必须要返回相应的基本类型包装类,即 int 对应的返回为 Integer 等等,还需要注意的是如果此时返回 null,则会抛出 NullPointerException,除此之外的其他情况下返回值的对象必须要和 Subject 类中定义方法的返回值一致,要不然会抛出 ClassCastException。

生成源码分析

那么通过 Proxy 类的 newProxyInstance 方法动态生成的类是什么样子的呢,我们上面也提到了,JDK 为我们提供了一个方法 ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces) 来产生动态代理类的字节码,这个类位于 sun.misc 包中,是属于特殊的 jar 包,于是问题又来了,Android studio 创建的 android 工程是没法找到 ProxyGenerator 这个类的,这个类在 jre 目录下,就算我把这个类相关的 .jar 包拷贝到工程里面并且在 gradle 里面引用它,虽然最后能够找到这个类,但是编译时又会出现很奇葩的问题,所以,没办法喽,android studio 没办法创建普通的 java 工程,只能自己装一个 intellij idea 或者求助相关的同事了。创建好 java 工程之后,使用下面这段代码就可以将生成的类导出到指定路径下面:

  1. public static void generateClassFile(Class clazz,String proxyName)
  2. {
  3.     //根据类信息和提供的代理类名称,生成字节码  
  4.     byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
  5.     String paths = "D:\\"; // 这里写死路径为 D 盘,可以根据实际需要去修改&n

    分享标题:Android动态代理以及利用动态代理实现ServiceHook
    网站路径:http://www.hantingmc.com/qtweb/news46/465596.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联