使用Javassist 在android运行时生成类

2024-03-05 19:04

本文主要是介绍使用Javassist 在android运行时生成类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

序言

最近在写框架,有一个需求就是动态的生成一个类,然后查阅了相关文献,发现在android中动态生成一个类还挺麻烦。因次把一些内容分享出来,帮助大家少走弯路。

方案一 DexMaker

DexMaker 是一个针对 Android 平台的库,用于在运行时生成 Dalvik 字节码。Dalvik 字节码是 Android 应用程序的底层运行代码,而 DexMaker 允许开发人员在运行时动态地创建和修改这些代码。这对于某些类型的测试和动态代码生成非常有用,例如在单元测试中模拟对象或者生成代理类。 DexMaker 主要用于测试框架和其他需要在运行时生成代码的库中。

dexmaker源码

dexmaker

引入dexmaker

testCompile "com.crittercism.dexmaker:dexmaker:1.4"
testCompile "com.crittercism.dexmaker:dexmaker-dx:1.4"
testCompile "com.crittercism.dexmaker:dexmaker-mockito:1.4"

缺点

我最开始也是准备用这个库的,但是这个库是基于字节码的,要模拟虚拟机的运行。有点像是在写汇编语言,对使用者来说不是很优化。而且相关的资料也不多。所以准备找一个更友好的库。

方案二 Javassist

Javassist(Java Programming Assistant)是一个用于在运行时编辑字节码的 Java 库。它允许开发人员在运行时创建、编辑和修改 Java 类的字节码,从而实现动态生成类、动态修改类以及实现各种 AOP(面向切面编程)等功能。

Javassist 提供了简单易用的 API,使开发人员能够以编程方式操作字节码,而不必直接操作底层的字节码指令。通过 Javassist,开发人员可以在运行时动态地创建新的类、修改现有类的结构、添加、修改或删除类的字段和方法等。

Javassist 在许多领域都有广泛的应用,包括动态代理、AOP、ORM(对象关系映射)、代码生成等。它被广泛应用于 Java EE、Android 开发以及许多其他 Java 相关的项目中。

最关键的是API更友好,资料更多。

在这里插入图片描述

面临的问题

这个库很好用,但是基于的是java的字节码。而android运行的是dex字节码。所以需要在运行时,将java字节码,编译为dex文件,再加载dex文件,加载最后的类。翻遍github只找到了这个库,代码已经是12年前的了。而且已经不再维护了。

在这里插入图片描述

运行起来

经过我的不断的改造,终于把源文件改造为gralde形式,可以运行起来了。而且支持android 30。

测试代码

public class MainActivity extends Activity {private static final String DEX_FILE_NAME_MYCLASSES = "myclasses.dex";private static final boolean FORCE_GENRATE_DEX = false;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final TextView view = (TextView)findViewById(R.id.textview_title);final File dexFile = new File(getFilesDir(), DEX_FILE_NAME_MYCLASSES);if (!dexFile.exists() || FORCE_GENRATE_DEX) {// generate DEX and ODEX file.try {// generate "xxx.class" file via Javassist.final ClassPool cp = ClassPool.getDefault(getApplicationContext());final CtClass cls = cp.makeClass("hoge");final CtConstructor ctor = new CtConstructor(null, cls);ctor.setBody("{}");cls.addConstructor(ctor);final CtMethod m1 = CtMethod.make("public java.lang.String toString() { return \"hoge.toString() is called.\"; }",cls);cls.addMethod(m1);final CtMethod m2 = CtMethod.make("public void setText(android.widget.TextView view) { view.setText((java.lang.CharSequence)\"hoge.setText() is called 222.\"); }",cls);// 创建注解AnnotationsAttribute attr = new AnnotationsAttribute(m2.getMethodInfo().getConstPool(), AnnotationsAttribute.visibleTag);Annotation annotation = new Annotation("android.annotation.TargetApi", m2.getMethodInfo().getConstPool());attr.addAnnotation(annotation);// 在方法上添加注解m2.getMethodInfo().addAttribute(attr);cls.addMethod(m2);cls.writeFile(getFilesDir().getAbsolutePath());// convert from "xxx.class" to "xxx.dex"final DexFile df = new DexFile();final String dexFilePath = dexFile.getAbsolutePath();df.addClass(new File(getFilesDir(), "hoge.class"));df.writeFile(dexFilePath);} catch (Exception e) {e.printStackTrace();Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();}}if (dexFile.exists()) {try {final DexClassLoader dcl = new DexClassLoader(dexFile.getAbsolutePath(),getCacheDir().getAbsolutePath(),getApplicationInfo().nativeLibraryDir,getClassLoader());String title = null;final Class<?> class_hoge = dcl.loadClass("hoge");final Constructor<?> ctor = class_hoge.getConstructor(new Class<?>[0]);final Object obj = ctor.newInstance(new Object[0]);title = obj.toString();view.setText(title);new Handler().postDelayed(new Runnable() {@Overridepublic void run() {try {final Method m = obj.getClass().getDeclaredMethod("setText", TextView.class);m.invoke(obj, view);} catch (Exception e) {e.printStackTrace();Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();}}}, 2000);} catch (Exception e) {e.printStackTrace();Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();}}}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.activity_main, menu);return true;}
}

生成的dex

在这里插入图片描述

反编译查看结果

在这里插入图片描述
推荐上面的反编译工具,可以直接把dex反编译为源文件,使用也很简单,拖进去就行了

jadx

运行结果

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/2f9babe73ca644be8734f4bc78da0cfd.png

我的项目

送佛送到西,我还把项目发布到了jitpack

仓库地址

	repositories {maven { url 'https://jitpack.io' }}

要引入以下两个依赖

	dependencies {implementation 'com.github.zhuguohui.Android-Javassist:dx:v1.0.2'implementation 'com.github.zhuguohui.Android-Javassist:javassist-android:v1.0.2'}

项目源码

zhuguohui/Android-Javassist

这篇关于使用Javassist 在android运行时生成类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/777507

相关文章

Java利用@SneakyThrows注解提升异常处理效率详解

《Java利用@SneakyThrows注解提升异常处理效率详解》这篇文章将深度剖析@SneakyThrows的原理,用法,适用场景以及隐藏的陷阱,看看它如何让Java异常处理效率飙升50%,感兴趣的... 目录前言一、检查型异常的“诅咒”:为什么Java开发者讨厌它1.1 检查型异常的痛点1.2 为什么说

python中的显式声明类型参数使用方式

《python中的显式声明类型参数使用方式》文章探讨了Python3.10+版本中类型注解的使用,指出FastAPI官方示例强调显式声明参数类型,通过|操作符替代Union/Optional,可提升代... 目录背景python函数显式声明的类型汇总基本类型集合类型Optional and Union(py

基于Java开发一个极简版敏感词检测工具

《基于Java开发一个极简版敏感词检测工具》这篇文章主要为大家详细介绍了如何基于Java开发一个极简版敏感词检测工具,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录你是否还在为敏感词检测头疼一、极简版Java敏感词检测工具的3大核心优势1.1 优势1:DFA算法驱动,效率提升10

Java使用正则提取字符串中的内容的详细步骤

《Java使用正则提取字符串中的内容的详细步骤》:本文主要介绍Java中使用正则表达式提取字符串内容的方法,通过Pattern和Matcher类实现,涵盖编译正则、查找匹配、分组捕获、数字与邮箱提... 目录1. 基础流程2. 关键方法说明3. 常见场景示例场景1:提取所有数字场景2:提取邮箱地址4. 高级

使用SpringBoot+InfluxDB实现高效数据存储与查询

《使用SpringBoot+InfluxDB实现高效数据存储与查询》InfluxDB是一个开源的时间序列数据库,特别适合处理带有时间戳的监控数据、指标数据等,下面详细介绍如何在SpringBoot项目... 目录1、项目介绍2、 InfluxDB 介绍3、Spring Boot 配置 InfluxDB4、I

基于Java和FFmpeg实现视频压缩和剪辑功能

《基于Java和FFmpeg实现视频压缩和剪辑功能》在视频处理开发中,压缩和剪辑是常见的需求,本文将介绍如何使用Java结合FFmpeg实现视频压缩和剪辑功能,同时去除数据库操作,仅专注于视频处理,需... 目录引言1. 环境准备1.1 项目依赖1.2 安装 FFmpeg2. 视频压缩功能实现2.1 主要功

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

使用Python实现无损放大图片功能

《使用Python实现无损放大图片功能》本文介绍了如何使用Python的Pillow库进行无损图片放大,区分了JPEG和PNG格式在放大过程中的特点,并给出了示例代码,JPEG格式可能受压缩影响,需先... 目录一、什么是无损放大?二、实现方法步骤1:读取图片步骤2:无损放大图片步骤3:保存图片三、示php

Spring-DI依赖注入全过程

《Spring-DI依赖注入全过程》SpringDI是核心特性,通过容器管理依赖注入,降低耦合度,实现方式包括组件扫描、构造器/设值/字段注入、自动装配及作用域配置,支持灵活的依赖管理与生命周期控制,... 目录1. 什么是Spring DI?2.Spring如何做的DI3.总结1. 什么是Spring D

使用Python实现一个简易计算器的新手指南

《使用Python实现一个简易计算器的新手指南》计算器是编程入门的经典项目,它涵盖了变量、输入输出、条件判断等核心编程概念,通过这个小项目,可以快速掌握Python的基础语法,并为后续更复杂的项目打下... 目录准备工作基础概念解析分步实现计算器第一步:获取用户输入第二步:实现基本运算第三步:显示计算结果进