文档章节

Android热编译技术——运行时动态处理和生成代码,初入殿堂!

Lody
 Lody
发布于 2015/01/16 20:20
字数 1266
阅读 2420
收藏 13

你有没有这样的想法

-想将编译时不存在的类在运行时动态创建并加载。

-想要使一个类动态的继承不同的父类,实现不同的接口。

-在不知道代码的情况下,在类中添加字段,方法。

-在一个方法的前后插入自己的代码。


  呵呵,也许对你来说,这一切曾经只是浮夸的幻想,但我可以负责任的告诉你,当你看完这篇文章,这一切将成为现实!


在介绍这门技术之前,我们有必要来了解一下需需要用到的概念

一、compile-time

所谓的compile-time,顾名思义,就是编译时。我们的一切工作将在compile-time处理,而在Run-time(运行时)被调用和执行或其他操作。


二、dex文件的由来

  我们知道,在Java中使用的是 .class文件作为字节码文件,而在Android中,则是 .dex文件,他们之间有什么关系?查阅资料,我们可以知道,Android的 .dex文件其实是由 .class文件优化而成,而加载代码,使用的是Dalvik虚拟机(在java的实现是DexClassLoader)。



知道了以上这些概念,恭喜你,你已经踏入了成功殿堂的半步,现在我们就来学习具体的实现。

  在Java平台,有一个很强大的开源库,名为:JavaSsist,然而,它是针对JAVA平台的,也就是说,它的实现全部是基于jvm的栈结构字节码,因此,在android中是无法使用的,那么我们又应该怎么办呢?

笔者既然提到了它,自然会就使用到它,下面就来介绍笔者想到的解决方案。

我们用到的是Google 提供的 Dx工具,Dx工具能将.class文件转换为.dex文件,最值得欣慰的是,这个工具是使用纯java编写的,因此我们完全可以使用在android平台。事实证明,的确如此。

  于是得出方案,在生成class字节码后,紧接着使用dx转换一次,这样看似效率底下,而实际上却并非如此。笔者在这里就不再写  JavaSsist  移植到 Android平台的具体过程,我会将移植完成的jar库放在文章的尾部,欢迎下载和学习!


如何使用这个库?

首先,第一步是申明 池:

final ClassPool cp = ClassPool
                        .getDefault(getApplicationContext());
final CtClass cls = cp.makeClass("LodyActivity");//LodyActivity是这里要生成的类名

然后,我们设置它的父类 :

cls.setSuperclass(cp.get("android.app.Activity"));


  在这一步,我们将他的父类设置为:

android.app.Activity

这一部是可以省略的,如果没有设置父类,则它默认是一个Object的子类。

然后,我们为这个类创建构造器:

  CtConstructor constructor = new CtConstructor(null, cls);  
  constructor.setModifiers(Modifier.PUBLIC);  
  constructor.setBody("{System.out.println(\" 调用构造器!\");}");  
  cls.addConstructor(constructor);

这一步,也是可以省略的,因为默认会创建一个无参数构造器,当然,在这一步,我申明的构造器也是无参构造器,如果读者想要生命有参构造器,则可以使用这个API:

CtConstructor(CtClass[] parameters, CtClass declaring)

其中的 parameters(参数)用cp.get(完整包名+类名),然后放入一个CtClass数组实现。  

CtMethod method = new CtMethod(CtClass.voidType, "run", null, cls); 
method.setBody("{System.out.println(\"调用了方法:run!!\" );}  ");  
cls.addMethod(method);

这一步,将会为类添加一个名为run,返回值为vold的方法,你可能会震惊的发现,setBody可以直接写入java代码?!

  没错,这就是亮点,因为JavaSsist内置一个java编译器,它将会以字段和方法为单位,将你插入的代码编译出来。


当你完成了一个类的创建,你需要将它写入到存储器(其实在java不需要这么做,因为你可以使用toClass方法直接得到纂改后的类,使用Class.newInstance()来得到实例,但是由于dex限制,在android中没办法这么做,忍忍吧!)

cls.writeFile(getFilesDir().getAbsolutePath());
做完这一步,将会在你的存储器的相应位置生成xxxxx.class文件(这里是LodyActivity.class),下一步是class2dex的过程:
 
final DexFile df = new DexFile();
final String dexFilePath = "/sdcard/myclasses.dex";
df.addClass(new File(getFilesDir(), "LodyActivity.class"));
df.writeFile(dexFilePath);

完成这一步,就会在/sdcard下生成 myclasses.dex文件,最后,我们可以使用DexClassLoader调用它,关于如何加载一个dex的方法,网上有大量的文章介绍这个,这里不再细谈,最后我们看看生成的dex文件的代码:


可以看到,代码成功生成了!关于这款软件,我想说,你并不能在网上找到,因为这是笔者正在开发的一款软件,能够让使用者直接查看一个apk/dex/jar的源码,并且不怕混淆,能够加载resource.arsc,关于这款软件,相信读者未来有一天有机会将会见到的,呵呵。

最后送上笔者做好的JavaSsist库:

http://pan.baidu.com/s/1c02FKGS

© 著作权归作者所有

Lody

Lody

粉丝 45
博文 3
码字总数 4482
作品 1
高级程序员
私信 提问
加载中

评论(6)

Lody
Lody 博主

引用来自“小陈醋”的评论

在class2Dex时执行到 final DexFile df = new DexFile(); 报错“java.lang.NoClassDefFoundError: com.android.dx.dex.DexOptions”
需要手动引入android dx工具的jar。
小陈醋
在class2Dex时执行到 final DexFile df = new DexFile(); 报错“java.lang.NoClassDefFoundError: com.android.dx.dex.DexOptions”
y
yameta
hi 我现在想生成一个方法体CtMethod.setBody("{requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);}")但是总找不到android这个包,报“java.lang.ClassNotFoundException: Didn't find class "android" on path: /data/data/org.jamruby.javassistsample/files/org.jamruby.javassistsample-1.apk/classes.dex”这个错 不知道是什么原因导致的?
念nian不wang

引用来自“张涛OSC”的评论

原来是热部署。。。。。比回调更慢。。。。不过很不错,加油
kymjs张涛
kymjs张涛
原来是热部署。。。。。比回调更慢。。。。不过很不错,加油
热修复技术的实践之旅——微信TinkerPatch热修复结合Walle多渠道打包的详解

本文导语: 本文的核心内容介绍: (1)对比当前市场上的热修复方案,对Tinker热修复方案进行了简单的介绍。 (2)详细讲解了微信Tinker的完整接入过程,文末提供了一个自己写的非常轻量的D...

懂音乐码虫
2018/05/29
0
0
Android插件化开发,初入殿堂

好久没有写博客了,这次准备写写我这几天的研究成果——Android插件化开发框架CJFrameForAndroid。 背景交代 首先,你需要知道什么是插件化开发。就拿最常见的QQ来说,在第三个界面动态那里有...

kymjs张涛
2014/10/12
13.2K
9
Android App Bundle出来了,App加壳技术不能用了怎么办?

Google I/O大会上,Google向 Android 引入了新 App 动态化框架(Android App Bundle, AAB),被看作是对Android未来发展具有颠覆性的动态化解决方案。在给Android App带来便利的同时,也给移...

工作的事
05/20
35
0
进入Android Dalvik虚拟机之Dalvik虚拟机的特点

Google于2007年底正式发布了Android SDK,Dalvik虚拟机也第一次进入了人们的视野。它的作者是丹.伯恩斯坦(Dan Bornstein)。Dalvik虚拟机作为Android平台的核心组件,拥有如下几个特点: 体...

柳哥
2015/01/08
1K
0
Android安全之---应用防dex2jar原理及实现

一、反编译某平台代码 最近在看某外卖平台的代码,发现某外卖平台最新版本版本无法正常的通过dex2jar工具将dex转换出Java源代码,在转换过程中会提示出错,如图: 查看转换出的Java源代码,会...

病已
2017/06/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

代理模式之JDK动态代理 — “JDK Dynamic Proxy“

动态代理的原理是什么? 所谓的动态代理,他是一个代理机制,代理机制可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成,通过代理可以有效的让调...

code-ortaerc
今天
5
0
学习记录(day05-标签操作、属性绑定、语句控制、数据绑定、事件绑定、案例用户登录)

[TOC] 1.1.1标签操作v-text&v-html v-text:会把data中绑定的数据值原样输出。 v-html:会把data中值输出,且会自动解析html代码 <!--可以将指定的内容显示到标签体中--><标签 v-text=""></......

庭前云落
今天
8
0
VMware vSphere的两种RDM磁盘

在VMware vSphere vCenter中创建虚拟机时,可以添加一种叫RDM的磁盘。 RDM - Raw Device Mapping,原始设备映射,那么,RDM磁盘是不是就可以称作为“原始设备映射磁盘”呢?这也是一种可以热...

大别阿郎
今天
12
0
【AngularJS学习笔记】02 小杂烩及学习总结

本文转载于:专业的前端网站☞【AngularJS学习笔记】02 小杂烩及学习总结 表格示例 <div ng-app="myApp" ng-controller="customersCtrl"> <table> <tr ng-repeat="x in names | orderBy ......

前端老手
昨天
16
0
Linux 内核的五大创新

在科技行业,创新这个词几乎和革命一样到处泛滥,所以很难将那些夸张的东西与真正令人振奋的东西区分开来。Linux内核被称为创新,但它又被称为现代计算中最大的奇迹,一个微观世界中的庞然大...

阮鹏
昨天
20
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部