Android动态代理解析

本文探讨了Android开发中动态代理的概念,解释了如何通过动态代理实现不修改原有对象功能的同时,扩展其行为。介绍了Java的InvocationHandler和Proxy类在动态代理中的作用,并通过Retrofit框架的使用为例,详细阐述了动态代理在实际网络请求中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

前言:做过Android开发的一般都使用过Retrofit这个三方框架,初一使用,会觉得很神奇:为什么把各个请求定义在一个接口里,通过几行代码就能实现网络请求?答案就是“动态代理”!

 

何为代理?

简单来说代理是一种软件设计模式,某些情况下,我们不希望修改已有对象的功能,但是我们又想在不改变调用者调用方法的情况下,改变调用后的具体实现逻辑,因此,我们会采用间接访问来实现目的。比如A类实现了IHelloWorld接口,有一个helloWorld方法,但在使用的时候并不直接调用a.helloWorld(),而是通过定一个同样实现了IHelloWorld接口的代理类B,在B的helloWorld方法中调用A的helloWorld方法,并夹带一些自己的私货。

//共同实现的接口
public interface IHelloWorld {
    public void helloWorld(String str);
}

//真实对象
public class RealSubject implements IHelloWorld {
    @Override
    public void helloWorld() {
        System.out.println("RealSubject say hello world");
    }
}

//代理对象
public class Proxy implements IHelloWorld {    
    private IHelloWorld subject;

    public Proxy(IHelloWorld subject) {
        this.subject = subject;
    }

    @Override
    public void helloWorld() {
        //代理类的私货
        System.out.println("Proxy say hello world");  
     
        //被代理对象的真实调用
        subject.helloWorld();
    }
}


//实际调用
public static void main(String[] args) {
    //被代理的对象
    RealSubject realSubject = new RealSubject();

    //代理类对象
    Proxy proxy = new Proxy(realSubject);

    //调用代理类对象的方法
    proxySubject.helloWorld();
}

一般的代理,就是这样的逻辑和实现。但是,如果如果需要代理的方法成百上千,总不能每一个方法都写一遍吧

 

动态代理

在java的动态代理机制中,有两个重要的类和接口:

  1.  InvocationHandler(Interface)
  2. Proxy(Class)

 

  •  InvocationHandler

每个代理实类例的invocation handler都要实现InvocationHandler,并且每个代理类的实例都关联到了一个handler,当通过代理对象调用一个方法的时候,这个调用就会被转发,由InvocationHandler这个接口的 invoke 方法来进行调用。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

//proxy:指代生成的代理对象;
//method:指代的是我们所要调用真实对象的某个方法的Method对象;
//args:指代的是调用真实对象某个方法时接受的参数;
  • Proxy

Proxy的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,其中核心方法就是 newProxyInstance 

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。

我们通过一个具体的事例来理解:

public interface IHelloWorld {
    public void helloWorld(String str);
}

public class RealSubject implements IHelloWorld {
    @Override
    public void helloWorld() {
        System.out.println("RealSubject say hello world");
    }
}

public class SubjectInvocationHandler implements InvocationHandler {
    //这个就是我们要代理的真实对象
    private Object subject;

    //构造方法,给我们要代理的真实对象赋初值
    public SubjectInvocationHandler(Object subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
        //动态代理的私货
        System.out.println("invoke");
       
        //被代理对象的方法调用
        method.invoke(subject, args);
        
        return null;
    }
}

public static void main(String[] args) {
       //被代理类
       IHelloWorld realSubject = new RealSubject();

       //我们要代理哪个类,就将该对象传进去,最后是通过该被代理对象来调用其方法的
       SubjectInvocationHandler handler = new SubjectInvocationHandler(realSubject);

       //生成代理类
       Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), 
                                                          realSubject.getClass().getInterfaces(), handler);

       //注意这里,后面会有讲解
       System.out.println("Proxy : "+ subject.getClass().getName());
       
       
       subject.helloWorld();
      
   }

SubjectInvocationHandler并不是真正的代理类,而是用于定义代理类需要扩展、增强那些方法的功能类,你可以理解为他帮助你实现了一个真正的代理类,我们在式例中打印Proxy的ClassName,得到的结果是:

Proxy : com.sun.proxy.$Proxy0

这个$Proxy0才是真正的代理类,由Proxy.newProxyInstance 创建,是在jvm运行时动态生成的一个对象,命名方式都是以$开头,proxy为中,最后一个数字表示对象的标号。

通过这样的方式,不再需要手动的一个个去实现代理方法:

@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
    //这里可以判断你想要代理的方法名
    if (method.getName().equals("helloWorld")) {
        //想加什么私货加什么私货
        
        //被代理对象的真实调用
        method.invoke(subject, args);
    }
}

我们再来看一个Retrofit的例子

首先,按照常规的方式定义一个接口:

import retrofit2.Call;

import retrofit2.http.GET;

import retrofit2.http.Query;

public interface NetProtocol {

    @GET("/toutiao/index")
    Call<JuheBaseBean<ToutiaoBean>> getNews(@Query("key") String key);

}

然后在代码中使用:

Retrofit retrofit = new Retrofit.Builder().baseUrl("http://v.juhe.cn").addConverterFactory(GsonConverterFactory.create()).build();

NetProtocol protocol = retrofit.create(NetProtocol.class);

Call<JuheBaseBean<ToutiaoBean>> call = protocol.getNews("aadc832bcc8fec1602d547e3659714fd");

注意这里的 retrofit.create(NetProtocol.class)

我们来看下retrofit的creat方法的源代码:

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

create()方法中的范型T,就是我们的接口对象NetProtocol,注意这里的return后面的代码,调用了Proxy.newProxyInstance,对照我们之前关于动态代理的示例,就很容易理解了。当NetProtocol调用自身方法的时候,因为运用到了动态代理,所以会调用invoke方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值