首页 > 编程知识 正文

字节跳动事业部框架,字节跳动ui框架

时间:2023-05-06 15:54:29 阅读:224771 作者:456

本文用javassist方式,模拟美团Robust插件的前置处理:用插入代码的方式,针对apk中的每个方法都插入一段静态代码判断语句,用于控制是否启用热修复fix(也就是动态加载patch包到原apk中)。

一、插件的生成与依赖导入

1、用该文章中的方法:Android中Plugin插件工程的自动生成 自动生成一个android的插件模板;

2、加入javassist依赖,加入依赖后的插件build的格式如下:

dependencies{ compile gradleApi() compile localGroovy() //transform与javassist依赖增加 compile 'com.android.tools.build:transform-api:1.5.0' compile 'javassist:javassist:3.12.1.GA' compile 'commons-io:commons-io:2.5' implementation 'com.android.tools.build:gradle:3.6.3' } 二、javassist编译时处理代码

javassist的作用期是编译打包时从.class--转换-->.dex过程中。本例中,将通过插件的方式,用字节码手术刀javassist,修改class字节码文件,为各方法添加代码。

注:javassist、aspectJ、apt三种编译时技术的起效时期如下:

1、插件注册

在生成的插件模板中,做编辑。新增一个transForm到编译过程。

public class SunnyEasyUse implements Plugin<Project> { @Override public void apply(Project project) { println 'A Plugin' //该插件配置到对应工程的build中,此处做注册 def android = project.extensions.getByType(AppExtension) //将SunnyTransForm加入到编译过程 android.registerTransform(new SunnyTransForm(project)) println 'register A Plugin' }}

2、TransFrom编写

此处主要做两个操作:

(1)将第三方依赖(jar包),从上一个transForm中取出,并传递到下一个transForm;

(2)对源文件(也就是自己编写的代码)做处理,在代码的方法中插入预期代码;

1、热修复控制类新建

首先,在主工程中,新建类如下:

public class FixUtil { //是否开启使用热修复 public static boolean isFixUse(){ return false; }}

2、TransFrom针对所有方法,将上述判断插入

具体步骤,参照注释

public class SunnyTransForm extends Transform { //字节码池 def pool = ClassPool.default //工程 def project SunnyTransForm(project) { this.project = project } @Override public String getName() { return "SunnyTransForm" } @Override public Set<QualifiedContent.ContentType> getInputTypes() { return TransformManager.CONTENT_CLASS } @Override public Set<QualifiedContent.Scope> getScopes() { return TransformManager.SCOPE_FULL_PROJECT } @Override public boolean isIncremental() { return false; } @Override void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { super.transform(transformInvocation) //class路径,加载到字节码池。 //把project下的每个class路径,加载到pool字节码池,也就是加载到内存中 project.android.bootClasspath.each { pool.appendClassPath(it.absolutePath) } //输出 def outProvider = transformInvocation.getOutputProvider() //输入的文件处理 transformInvocation.inputs.each { input -> //jar包处理 input.jarInputs.each { it -> processJarInput(it, outProvider) } //源文件---自己写的源文件,都放置在文件夹中 input.directoryInputs.each { it -> processDirectoryInput(it, outProvider) } } } private void processJarInput(JarInput jarInput, TransformOutputProvider outputProvider) { def destFile = outputProvider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR) pool.insertClassPath(jarInput.file.absolutePath) FileUtils.copyFile(jarInput.file, destFile) } private void processDirectoryInput(DirectoryInput directoryInput, TransformOutputProvider outputProvider) { def preFileName = directoryInput.file.absolutePath pool.insertClassPath(preFileName) //开始修改class代码 findTargetClassFile(directoryInput.file, preFileName) def destFile = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY) FileUtils.copyDirectory(directoryInput.file, destFile) } private void findTargetClassFile(File file, String fileName) { //递归查找.class结尾的文件 if (file.isDirectory()) { file.listFiles().each { findTargetClassFile(it, fileName) } } else { modify(file, fileName) } } //修改代码 private void modify(File file, String fileName) { def filePath = file.absolutePath if (!filePath.endsWith(SdkConstants.DOT_CLASS)) { return } if (filePath.contains('R$') || filePath.contains('R.class') || filePath.contains('BuildConfig.class')) { return } println("文件路径------------>:" + fileName) //根据路径得到包名与类名,用于加载 //fileName是文件夹 // 正反斜杠是兼容window与mac // 获取到 类似com.朴实的帅哥.file.MainActivity.class全类名 def className = filePath.replace(fileName, "") .replace("\", ".") .replace("/", ".") //获取 类似com.朴实的帅哥.file.MainActivity def name = className.replace(SdkConstants.DOT_CLASS, "").substring(1) println("name------------>:" + name) //取到字节码操作对象。类似 json --> 转javabean CtClass ctClass = pool.get(name) if (name.contains("com.朴实的帅哥.aplugin")) { def modifyBody = "if(com.朴实的帅哥.aplugin.FixUtil.isFixUse()){}" addCode(ctClass, modifyBody,fileName) } } //添加代码到方法中 private void addCode(CtClass ctClass, String body,String fileName) { println("addCode------------>:" + body) CtMethod[] methods = ctClass.getDeclaredMethods() ctClass.defrost() methods.each { if(!it.getName().contains("isFixUse")){ println("it.getName()------------>:" + it.getName()) //插入到方法最前 it.insertBefore(body) } } ctClass.writeFile(fileName) ctClass.detach() }}

3、编译后效果

(1)在编译的build目录下,可以看到新的transForm

(2)被修改后的方法,如类MainActivity中的onCreate中,在方法头部位置,插入了判断代码

三、热修复的后续操作说明

至此,热修复Robust前期的核心工作已经做完。

后续需要做的是将下载到本地的patch文件,通过动态加载的方式,加入到apk中。

此外,javessist除以上可以插入到方法头部,也可以如aspectJ一样,在方法前、方法后、环绕等方式处理方法。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。