首页 > 其他分享 >Android动态代理详解

Android动态代理详解

时间:2023-11-03 22:31:52浏览次数:34  
标签:02 11 2966 java 代理 13546 详解 Proxy Android

动态代理在java里面算是一种比常用的技术,它和静态代理的区别在于静态代理需在编译的时候代理类就已经确定了,而动态代理的代理类是在运行的时候动态生成的。

例如使用retrofit的时候我们只需要定义好interface:

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

然后就可以在运行的时候创建出这个接口的实例:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

这个实例其实就是接口的代理,它的原理是利用Proxy.newProxyInstance

interface的动态代理

Proxy是java内置的一个类,我们可以用它来创建接口的动态代理,具体的用法如下:

// 定义interface
public interface ITestInterface {
    void foo();
}

// 创建InvocationHandler用于代理interface的方法
InvocationHandler handler = new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        Log.d(TAG, "invoke " + method, new Exception());
        return null;
    }
};

// 创建ITestInterface的实例:
ITestInterface testInterface = (ITestInterface) Proxy.newProxyInstance(
                getClassLoader(),
                new Class[]{ITestInterface.class},
                handler);

然后我们调用testInterface.foo()最终就会去到InvocationHandler.invoke方法里面。

安卓里面最终是在ClassLinker::CreateProxyClass里面创建了实现ITestInterface接口的子类。从堆栈上看它生成的类名叫$Proxy1:

11-02 16:58:32.641  4205  4205 D testtest: invoke public abstract void me.linjw.demo.ITestInterface.foo()
11-02 16:58:32.641  4205  4205 D testtest: java.lang.Exception
11-02 16:58:32.641  4205  4205 D testtest:      at me.linjw.demo.MainActivity$1.invoke(MainActivity.java:23)
11-02 16:58:32.641  4205  4205 D testtest:      at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
11-02 16:58:32.641  4205  4205 D testtest:      at $Proxy1.foo(Unknown Source)
11-02 16:58:32.641  4205  4205 D testtest:      at me.linjw.demo.MainActivity.onCreate(MainActivity.java:32)
...

$Proxy1.foo方法里面调用了Proxy.invoke,而这个invoke方法实际就是调用了我们注册的InvocationHandler:

// Android-added: Helper method invoke(Proxy, Method, Object[]) for ART native code.
private static Object invoke(Proxy proxy, Method method, Object[] args) throws Throwable {
    InvocationHandler h = proxy.h;
    return h.invoke(proxy, method, args);
}

JVM里面可以通过设置环境变量的方式将动态生成的类保存下来,但是安卓里面并没有这样的机制。我们可以通过反射打印这个生成类的结构

11-02 20:40:10.580 13546 13546 D ProxyDemo: class $Proxy1 extends Proxy implements ITestInterface {
11-02 20:40:10.580 13546 13546 D ProxyDemo:     public static final Class[] interfaces;
11-02 20:40:10.580 13546 13546 D ProxyDemo:     public static final Class[][] throws;
11-02 20:40:10.580 13546 13546 D ProxyDemo:     public final equals(Object arg0) { ... }
11-02 20:40:10.580 13546 13546 D ProxyDemo:     public final foo() { ... }
11-02 20:40:10.580 13546 13546 D ProxyDemo:     public final hashCode() { ... }
11-02 20:40:10.580 13546 13546 D ProxyDemo:     public final toString() { ... }
11-02 20:40:10.580 13546 13546 D ProxyDemo: }

从打印的信息来看我们可以知道生成的$Proxy1类是Proxy的之类并且实现了我们需要代理的ITestInterface接口,它的foo方法我们没有办法打印出实际的字节码,但是从堆栈上看可以猜测大概是这样的:

void foo() {
    Proxy.invoke(this, ITestInterface.class.getMethod("foo"), null);
}

Proxy不能代理class的原因

由于interface是支持多实现的所以我们可以代理多个接口,这样生成的类就会实现多个接口:

ITestInterface testInterface = (ITestInterface) Proxy.newProxyInstance(
                getClassLoader(),
                new Class[]{ITestInterface.class}, // 这个数组可以传入多个interface进行代理
                handler);

但是java并不支持多继承,动态生成的类已经继承Proxy了就不能再继承其他的类,所以Proxy并不能代理类或者抽象类:

11-02 16:33:06.615  2966  2966 E testtest: err
11-02 16:33:06.615  2966  2966 E testtest: java.lang.IllegalArgumentException: me.linjw.demo.TestAbstractClass is not an interface
11-02 16:33:06.615  2966  2966 E testtest:      at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:635)
11-02 16:33:06.615  2966  2966 E testtest:      at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:602)
11-02 16:33:06.615  2966  2966 E testtest:      at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
11-02 16:33:06.615  2966  2966 E testtest:      at java.lang.reflect.WeakCache.get(WeakCache.java:127)
11-02 16:33:06.615  2966  2966 E testtest:      at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:438)
11-02 16:33:06.615  2966  2966 E testtest:      at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:873)

class的动态代理

理解了Proxy代理接口的原理之后,如果我们想要对类做动态代理的话,可以模仿Proxy的原理运行时创建子类重写被代理类的方法去实现.实际上java有个开源项目cglib可以在运行的时候生成java字节码实现这个功能。但是由于安卓运行时使用的不是java字节码而是安卓自己的字节码,所以不能直接使用cglib去实现。

// 定义需要代理的类
public class TestClass {
    public void foo() {
        Log.d(MainActivity.TAG, "on TestClass.foo");
    }
}

// 创建MethodInterceptor用于代理class的方法
Enhancer e = new Enhancer();
e.setSuperclass(TestClass.class);
e.setInterceptor(new MethodInterceptor() {
    @Override
    public Object intercept(Object o, Object[] objects, MethodProxy methodProxy) {
        // 调用父类(即被代理类)的方法
        methodProxy.invokeSuper(o, objects);
        Log.d(TAG, "invoke " + methodProxy.getOriginalMethod(), new Exception());
        return null;
    }
});

// 创建代理实例
TestClass testClass = (TestClass) e.create(getCacheDir().getAbsolutePath());

// 调用方法
testClass.foo();

然后调用TestClass的方法就会去到MethodInterceptor:

11-02 22:25:56.108  3357  3357 D ProxyDemo: on TestClass.foo
11-02 22:25:56.110  3357  3357 D ProxyDemo: invoke public void me.linjw.demo.proxy.TestClass$Enhancer$.foo()
11-02 22:25:56.110  3357  3357 D ProxyDemo: java.lang.Exception
11-02 22:25:56.110  3357  3357 D ProxyDemo:     at me.linjw.demo.proxy.MainActivity$2.intercept(MainActivity.java:45)
11-02 22:25:56.110  3357  3357 D ProxyDemo:     at leo.android.cglib.proxy.MethodProxyExecuter.executeInterceptor(MethodProxyExecuter.java:15)
11-02 22:25:56.110  3357  3357 D ProxyDemo:     at me.linjw.demo.proxy.TestClass$Enhancer$.foo(Unknown Source:19)
11-02 22:25:56.110  3357  3357 D ProxyDemo:     at me.linjw.demo.proxy.MainActivity.onCreate(MainActivity.java:51)

标签:02,11,2966,java,代理,13546,详解,Proxy,Android
From: https://blog.51cto.com/u_16175630/8175020

相关文章

  • 实例化一个新的Android Fragment的最佳实践
    内容来自DOChttps://q.houxu6.top/?s=实例化一个新的AndroidFragment的最佳实践我看到了在应用程序中实例化一个新的Fragment的两种常见做法:FragmentnewFragment=newMyFragment();和FragmentnewFragment=MyFragment.newInstance();第二种选项利用了静态方法......
  • 猫树详解
    一、猫树的作用学一个算法当然得先了解它的用处,那么猫树的作用嘛...简单来讲,线段树能维护的信息猫树基本都能维护比如什么区间和、区间gcd、最大子段和等 满足结合律且支持快速合并的信息二、猫树的算法实现什么都别说,我知道你想先知道猫树是怎么实现的我们就以区间和查......
  • 安卓主板_android主板_联发科MTK方案平台PCBA定制
    安卓主板是一种采用ARM架构并内置Android操作系统的嵌入式智能主板。相比于Linux,安卓主板在消费级和商用嵌入式智能终端市场非常成熟。与传统的单片机相比,安卓主板具有更高的性能和更丰富的接口,可以满足更复杂的开发和应用需求。目前,安卓主板在主流行业领域的应用方向包括AI人......
  • Veeam Backup&Replication V12 配置和优化代理服务器
    已经安装并完成了VeeamBackup&Replication所需的基本配置,接下来就可以配置和优化代理服务器:ProxyServers:代理服务器是VeeamBackup&Replicationv12应用程序,它们负责备份和还原作业的所有繁重任务或处理任务。VeeamBackup&Replicationv12引入了将Linux代理与持续数据保护Conti......
  • Android 11 下拉菜单长按WiFi图标SystemUI ANR
    bug描述:(MTK)--Android11的SystemUI下拉菜单长按图标(tiles)导致SystemUI崩溃重启。10-0108:01:11.23657925833EAndroidRuntime:FATALEXCEPTION:AsyncTask#110-0108:01:11.23657925833EAndroidRuntime:Process:com.android.systemui,PID:579210-0108......
  • Python 中的 __init__.py 和__all__ 详解(抄袭的joker) 因为写的实在是太好了
    Python中的__init__.py和__all__详解JOKER没意思先生 之前不论是自己写代码还是用别人的代码,都没有注意过这个东西,今天忽然看了一下,网上的教程感觉讲的都不是很清楚,自己又研究了研究,总结一下,如果有不对的地方,大家帮忙指正一下。在Python工程里,当pyth......
  • 史上最全的Android面试题集锦
    前言由于之前从上海离职,来到深圳找工作。然后准备面试的时候,发现网上很多Android面试题及答案整理都没有答案,在成功的拿到几家公司的offer后(虽然不是阿里、网易这种级别的公司,但对我一个毕业三年的Android开发来说,算是成功的从小公司跳到大公司)自己总结了一些最近面试过的Androi......
  • Android项目中引入aar包的新方法
    一、已过期的方法:1、把aar文件放在一个文件目录内,比如就放在工程的libs目录内;2、在app的build.gradle文件添加如下内容:(该配置和dependencies配置是一个位置级别)repositories{flatDir{dirs'libs'}}3、最后需要在dependencie......
  • CentOS7中firewall防火墙详解和配置
    提示修改防火墙配置文件之前,需要对之前防火墙做好备份重启防火墙后,需要确认防火墙状态和防火墙规则是否加载,若重启失败或规则加载失败,则所有请求都会被防火墙拒绝firewalld的基本使用#停止firewallsystemctlstopfirewalld.service#禁止firewall开机启动systemctldisablefi......
  • 手机直播源码,Android Shape设置背景
    手机直播源码,AndroidShape设置背景设置背景时,经常这样android:background=“@drawable/xxx”。如果是纯色图片,可以考虑用shape替代。shape相比图片,减少资源占用,缩减APK体积。<?xmlversion="1.0"encoding="utf-8"?><shape  xmlns:android="http://schemas.android.c......