当前位置: 首页 > news >正文

网站个人备案做论坛如何向百度提交网站地图

网站个人备案做论坛,如何向百度提交网站地图,婚庆网站策划,百度数据指数本文讲解如何编译插桩操纵字节码。 就使用 ASM 来实现简单的编译插桩效果#xff0c;通过插桩实现在每一个 Activity 打开时输出相应的 log 日志。实现思路 过程主要包含两步#xff1a; 1、遍历项目中所有的 .class 文件​ 如何找到项目中编译生成的所有 .class 文件#…本文讲解如何编译插桩操纵字节码。 就使用 ASM 来实现简单的编译插桩效果通过插桩实现在每一个 Activity 打开时输出相应的 log 日志。实现思路 过程主要包含两步 1、遍历项目中所有的 .class 文件​ 如何找到项目中编译生成的所有 .class 文件是我们需要解决的第一个问题。众所周知Android Studio 使用 Gradle 编译项目中的 .java 文件并且从 Gradle1.5.0 之后我们可以自己定义 Transform来获取所有 .class 文件引用。但是 Transform 的使用需要依赖 Gradle Plugin。因此我们第一步需要创建一个单独的 Gradle Plugin并在 Gradle Plugin 中使用自定义 Transform 找出所有的 .class 文件。 2、遍历到目标 .class 文件 Activity之后通过 ASM 动态注入需要被插入的字节码 如果第一步进行顺利我们可以找出所有的 .class 文件。接下来就需要过滤出目标 Activity 文件并在目标 Activity 文件的 onCreate 方法中通过 ASM 插入相应的 log 日志字节码。 创建主项目 ASMLifeCycleDemo当前项目中只有一个 MainActivity如下 package com.jscode.asmlifecycledemo;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;public class MainActivity extends AppCompatActivity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);} } 创建自定义 Gradle 插件 首先在 ASMLifeCycleDemo 项目中创建一个新的 module并选择 Android Library 类型命名为 asm_lifecycle_plugin。将 asm_lifecycle_plugin module 中除了 build.gradle 和 main 文件夹之外的所有内容都删除。然后在 main 目录下分别创建 groovy 和 java 目录结构如下 因为 Gradle 插件是使用 groovy 语言编写的所以需要新建一个 groovy 目录用来存放插件相关的.groovy类。  但 ASM 是 java 层面的框架所以在 java 目录里存放 ASM 相关的类。 然后在 groovy 中创建目录com.jscode.plugin并在此目录中创建类 LifeCyclePlugin.groovy 文件。 在 LifeCyclePlugin 中重写 apply 方法实现插件逻辑因为是 demo 演示所以我只是简单的打印 log 日志。 目录结构如上图代码如下 package com.jscode.pluginimport com.android.build.gradle.AppExtension import org.gradle.api.Plugin import org.gradle.api.Projectpublic class LifeCyclePlugin implements PluginProject {Overridevoid apply(Project project) {System.out.println(LifeCyclePlugin)def android project.extensions.getByType(AppExtension)LifeCycleTransform lt new LifeCycleTransform()android.registerTransform(lt)} } 以看出 LifeCyclePlugin 实现了 gradle api 中的 Plugin 接口。当我们在 app module 的 build.gradle 文件中使用此插件时其 LifeCyclePlugin 的 apply 方法将会被自动调用。 接下来将 asm_lifecycle_plugin module 的 build.gradle 中的内容全部删掉改为如下内容 plugins {id groovyid maven-publish //本文用到maven本地仓库来管理plugin }dependencies {implementation gradleApi()implementation localGroovy()implementation(com.android.tools.build:gradle:4.1.3)implementation org.ow2.asm:asm:7.1implementation org.ow2.asm:asm-commons:7.1 }publishing {repositories {maven {// $rootDir 表示你项目的根目录// 这里配置发布到的本地目录url $rootDir/repo}}publications {maven(MavenPublication) {// 插件的组ID建议设置为插件的包名groupId com.jscode.lifecycleplugin// 插件的名字后续在引用时会用到artifactId mypluginversion 1.0.0// 组件类型from components.java}} } group 和 version 都需要在 app module 引用此插件时使用。 所有的插件都需要被部署到 maven 库中我们可以选择部署到远程或者本地。 本文只是演示所以只是将插件部署到本地目录中。具体地址通过 repository 属性配置如图所示我将其配置在项目根目录下的 asm_lifecycle_repo 目录下。  最后一步创建 properties 文件。 在 plugin/src/main 目录下新建目录 resources/META-INF/gradle-plguins然后在此目录下新建一个文件lifecycleplugin.properties其中文件名 lifecycleplugin 就是我们自定义插件的名称稍后我们在 app module 中会使用到此名称。 在 .properties 文件中需要指定我们自定义的插件类名 LifeCyclePlugin如下所示 implementation-classcom.jscode.plugin.LifeCyclePlugin 至此自定义 Gradle 插件就已经写完现在可以在 Android Studio 的右边栏找到 Gradle 中点击 publishToMavenLocal执行 plugin 的部署任务 可以看到构建成功之后在 Project 的根目录下将会出现一个 repo 目录里面存放的就是我们的插件目标文件。测试 asm_lifecycle_plugin 为了测试自定义的 Gradle 插件是否可用可以在工程项目中的 build.gradle 中引用此插件。 // Top-level build file where you can add configuration options common to all sub-projects/modules.buildscript {dependencies {classpath com.android.tools.build:gradle:4.1.3classpath com.jscode.lifecycleplugin:myplugin:1.0.0} }plugins {id com.android.application version 7.2.0 apply falseid com.android.library version 7.2.0 apply false }task clean(type: Delete) {delete rootProject.buildDir } app module 中的 build.gradle plugins {id com.android.application }apply plugin: lifecyclepluginandroid {compileSdk 33defaultConfig {applicationId com.jscode.asmlifecycledemominSdk 23targetSdk 32versionCode 1versionName 1.0testInstrumentationRunner androidx.test.runner.AndroidJUnitRunner}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules.pro}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8} }dependencies {implementation androidx.appcompat:appcompat:1.6.1implementation com.google.android.material:material:1.9.0implementation androidx.constraintlayout:constraintlayout:2.1.4testImplementation junit:junit:4.13.2androidTestImplementation androidx.test.ext:junit:1.1.5androidTestImplementation androidx.test.espresso:espresso-core:3.5.1 } setting.gradle文件配置如下 pluginManagement {repositories {gradlePluginPortal()google()mavenCentral()mavenLocal()} } dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)repositories {google()mavenCentral()} } rootProject.name ASMLifeCycleDemo include :app include :asm_lifecycle_plugin然后在命令行中使用 gradlew 执行构建命令如果打印出我们自定义插件里的 log则说明自定义 Gradle 插件可以使用 自定义 Transform实现遍历 .class 文件 自定义 Gradle 插件已经写好接下来就需要实现遍历所有 .class 的逻辑。这部分功能主要依赖 Transform API。什么是 Transform Transform 可以被看作是 Gradle 在编译项目时的一个 task在 .class 文件转换成 .dex 的流程中会执行这些 task对所有的 .class 文件可包括第三方库的 .class进行转换转换的逻辑定义在 Transform 的 transform 方法中。实际上平时我们在 build.gradle 中常用的功能都是通过 Transform 实现的比如混淆proguard、分包multi-dex、jar 包合并jarMerge。自定义 Transform 在com.jscode.plugin目录中新建 LifeCycleTransform.groovy并继承 Transform 类 package com.jscode.pluginimport com.android.build.api.transform.DirectoryInput import com.android.build.api.transform.Format import com.android.build.api.transform.JarInput import com.android.build.api.transform.QualifiedContent import com.android.build.api.transform.Transform import com.android.build.api.transform.TransformException import com.android.build.api.transform.TransformInput import com.android.build.api.transform.TransformInvocation import com.android.build.api.transform.TransformOutputProvider import com.android.build.gradle.internal.pipeline.TransformManager import com.jscode.asm.LifecycleClassVisitor import groovy.io.FileType import org.apache.commons.io.FileUtils import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassVisitor import org.objectweb.asm.ClassWriterpublic class LifeCycleTransform extends Transform {OverrideString getName() {return LifeCycleTransform}OverrideSetQualifiedContent.ContentType getInputTypes() {return TransformManager.CONTENT_CLASS}OverrideSet? super QualifiedContent.Scope getScopes() {return TransformManager.PROJECT_ONLY}/*** 表示当前 Transform 是否支持增量编译* return*/Overrideboolean isIncremental() {return false}Overridevoid transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {CollectionTransformInput inps transformInvocation.inputsTransformOutputProvider outputProvider transformInvocation.outputProviderfor (TransformInput ti : inps) {for (DirectoryInput di: ti.directoryInputs) {File dir di.fileif (dir) {dir.traverse(type: FileType.FILES, nameFilter: ~/.*\.class/) { File f -// 对class文件读取解析ClassReader reader new ClassReader(f.bytes)// 对class文件的写入ClassWriter writer new ClassWriter(reader, ClassWriter.COMPUTE_MAXS)// 访问class文件的相应内容ClassVisitor visitor new LifecycleClassVisitor(writer)// 依次调用ClassVisitor接口的各个方法reader.accept(visitor, ClassReader.EXPAND_FRAMES)// toByteArray方法最终修改字节码并以byte数组形式返回byte[] bytes writer.toByteArray()// 通过文件流写入方式覆盖掉原先的内容实现class文件的改写FileOutputStream fos new FileOutputStream(f.path)fos.write(bytes)fos.close()}}def dest outputProvider.getContentLocation(di.name, di.contentTypes, di.scopes, Format.DIRECTORY)FileUtils.copyDirectory(di.file, dest)}// 由于从Android Gradle插件3.6.0-alpha01开始不再生成R.java并且将R片段与其他源分开编译为R.jar// 所以要把R.jar复制过来ti.jarInputs.each { JarInput jarInput -File file jarInput.filedef dest outputProvider.getContentLocation(jarInput.name,jarInput.contentTypes,jarInput.scopes, Format.JAR)FileUtils.copyFile(file, dest)}}} } 解释说明Transform 主要作用是检索项目编译过程中的所有文件。通过这几个方法我们可以对自定义 Transform 设置一些遍历规则具体如下 getName设置我们自定义的 Transform 对应的 Task 名称。 Gradle 在编译的时候会将这个名称显示在控制台上。比如Task :app:transformClassesWithXXXForDebug。 getInputType在项目中会有各种各样格式的文件通过 getInputType 可以设置 LifeCycleTransform 接收的文件类型此方法返回的类型是 SetQualifiedContent.ContentType 集合。 ContentType 有以下 2 种取值。 enum DefaultContentType implements ContentType {/*** The content is compiled Java code. This can be in a Jar file or in a folder. If* in a folder, it is expected to in sub-folders matching package names.*/CLASSES(0x01),/** The content is standard Java resources. */RESOURCES(0x02);private final int value;DefaultContentType(int value) {this.value value;}Overridepublic int getValue() {return value;}} CLASSES代表只检索 .class 文件RESOURCES代表检索 java 标准资源文件。 getScopes()这个方法规定自定义 Transform 检索的范围具体有以下几种取值 enum Scope implements ScopeType {/** Only the project (module) content */PROJECT(0x01),/** Only the sub-projects (other modules) */SUB_PROJECTS(0x04),/** Only the external libraries */EXTERNAL_LIBRARIES(0x10),/** Code that is being tested by the current variant, including dependencies */TESTED_CODE(0x20),/** Local or remote dependencies that are provided-only */PROVIDED_ONLY(0x40),/*** Only the projects local dependencies (local jars)** deprecated local dependencies are now processed as {link #EXTERNAL_LIBRARIES}*/DeprecatedPROJECT_LOCAL_DEPS(0x02),/*** Only the sub-projectss local dependencies (local jars).** deprecated local dependencies are now processed as {link #EXTERNAL_LIBRARIES}*/DeprecatedSUB_PROJECTS_LOCAL_DEPS(0x08);private final int value;Scope(int value) {this.value value;}Overridepublic int getValue() {return value;}} isIncremental() 表示当前 Transform 是否支持增量编译我们不需要增量编译所以直接返回 false 即可。 transform()在 自定义Transform 中最重要的方法就是 transform()。在这个方法中可以获取到两个数据的流向。 inputsinputs 中是传过来的输入流其中有两种格式一种是 jar 包格式一种是 directory目录格式。 outputProvideroutputProvider 获取到输出目录最后将修改的文件复制到输出目录这一步必须做否则编译会报错。 我们可以实现一个简易 LifeCycleTransform功能是打印出所有 .class 文件。代码如上LifeCycleTransform所示 解释说明 自定义的 Transform 名称为 LifeCycleTransform检索项目中 .class 类型的目录或者文件设置当前 Transform 检索范围为当前项目设置过滤文件为 .class 文件去除文件夹类型并打印文件名称。 将自定义的 LifeCycleTransform 注册到 Gradle 插件中 在 LifeCyclePlugin 中添加如下代码 public class LifeCyclePlugin implements PluginProject {Overridevoid apply(Project project) {System.out.println(LifeCyclePlugin)def android project.extensions.getByType(AppExtension)LifeCycleTransform lt new LifeCycleTransform()android.registerTransform(lt)} } 再次在命令行中执行 build 命令可以看到 LifeCycleTransform 检索出的所有 .class 文件。 从图中可以看出Gradle 编译时多了一个我们自定义的 LifeCycleTransform 类型的任务并且将所有 .class 文件名打印出来其中包含了我们需要的目标文件 MainActivity.class。 使用 ASM插入字节码到 Activity 文件 ASM 是一套开源框架其中几个常用的 API 如下 ClassReader负责解析 .class 文件中的字节码并将所有字节码传递给 ClassWriter。ClassVisitor负责访问 .class 文件中各个元素还记得上一课时我们介绍的 .class 文件结构吗ClassVisitor 就是用来解析这些文件结构的当解析到某些特定结构时比如类变量、方法它会自动调用内部相应的 FieldVisitor 或者 MethodVisitor 的方法进一步解析或者修改 .class 文件内容。ClassWriter继承自 ClassVisitor它是生成字节码的工具类负责将修改后的字节码输出为 byte 数组。 添加 ASM 依赖 在 asm_lifecycle_plugin 的 build.gradle 中添加对 ASM 的依赖如下 dependencies {implementation gradleApi()implementation localGroovy()implementation(com.android.tools.build:gradle:4.1.3)implementation org.ow2.asm:asm:7.1implementation org.ow2.asm:asm-commons:7.1 } 创建自定义 ASM Visitor 类 在 asm_lifecycle_plugin module 中的 src/main/java 目录下创建包 danny.jiang.asm并分别创建 LifecycleClassVisitor.java 和 LifecycleMethodVisitor.java。代码如下 LifecycleClassVisitor.java package com.jscode.asm;import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes;public class LifecycleClassVisitor extends ClassVisitor {private String className;private String superName;public LifecycleClassVisitor(ClassVisitor cv) {super(Opcodes.ASM5, cv);}Overridepublic void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {super.visit(version, access, name, signature, superName, interfaces);this.className name;this.superName superName;}Overridepublic MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {MethodVisitor mv cv.visitMethod(access, name, descriptor, signature, exceptions);if (superName.equals(androidx/appcompat/app/AppCompatActivity)) {if (name.startsWith(onCreate)) {return new LifecycleMethodVisitor(mv, className, name);}}return mv;}Overridepublic void visitEnd() {super.visitEnd();} } 红框中在 visitMethod 方法中过滤出继承自 AppCompatActivity 的文件并在 LifeCycleMethodVisitor.java 中对 onCreate 进行改造。 LifeCycleMethodVisitor.java package com.jscode.asm;import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes;public class LifecycleMethodVisitor extends MethodVisitor {private String className;private String methodName;public LifecycleMethodVisitor(MethodVisitor methodVisitor, String className, String methodName) {super(Opcodes.ASM5, methodVisitor);this.className className;this.methodName methodName;}Overridepublic void visitCode() {super.visitCode();mv.visitLdcInsn();mv.visitLdcInsn(className . methodName ());mv.visitMethodInsn(Opcodes.INVOKESTATIC, android/util/Log, i, (Ljava/lang/String;Ljava/lang/String;)I, false);mv.visitInsn(Opcodes.POP);} }上述是真正执行插入字节码的逻辑。可以看出 ASM 都是直接以字节码指令的方式进行操作的所以如果想使用 ASM需要程序员对字节码有一定的理解。如果对字节码不是很了解也可以借助三方工具 ASM Bytecode Outline 来生成想要的字节码。 修改 LifeCycleTransform 的 transform 方法使用 ASM 各种 Visitor 都定义好之后我们就可以修改 LifeCycleTransform 的 transform 方法并将需要插桩的字节码插入到 MainActivity.class 文件中 Overridevoid transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {CollectionTransformInput inps transformInvocation.inputsTransformOutputProvider outputProvider transformInvocation.outputProviderfor (TransformInput ti : inps) {for (DirectoryInput di: ti.directoryInputs) {File dir di.fileif (dir) {dir.traverse(type: FileType.FILES, nameFilter: ~/.*\.class/) { File f -System.out.println(find file name f.name)// 对class文件读取解析ClassReader reader new ClassReader(f.bytes)// 对class文件的写入ClassWriter writer new ClassWriter(reader, ClassWriter.COMPUTE_MAXS)// 访问class文件的相应内容ClassVisitor visitor new LifecycleClassVisitor(writer)// 依次调用ClassVisitor接口的各个方法reader.accept(visitor, ClassReader.EXPAND_FRAMES)// toByteArray方法最终修改字节码并以byte数组形式返回byte[] bytes writer.toByteArray()// 通过文件流写入方式覆盖掉原先的内容实现class文件的改写FileOutputStream fos new FileOutputStream(f.path)fos.write(bytes)fos.close()}}def dest outputProvider.getContentLocation(di.name, di.contentTypes, di.scopes, Format.DIRECTORY)FileUtils.copyDirectory(di.file, dest)}// 由于从Android Gradle插件3.6.0-alpha01开始不再生成R.java并且将R片段与其他源分开编译为R.jar// 所以要把R.jar复制过来ti.jarInputs.each { JarInput jarInput -File file jarInput.filedef dest outputProvider.getContentLocation(jarInput.name,jarInput.contentTypes,jarInput.scopes, Format.JAR)FileUtils.copyFile(file, dest)}}} 重新部署自定义 Gradle 插件并运行主项目 上面几步如果一切执行顺利那接下来就可以在点击 publishToMavelLocal 重新部署 LifeCyclePlugin。 注意重新部署时需要先在 app module 的 build.gradle 中将插件依赖注释否则报错。 部署成功之后重新在 app 中依赖自定义插件并运行主项目当 MainActivity 被打开时会在 logcat 中看到如下 读到这里你可能会有疑虑如果在项目中打开了混淆那注入的字节码还会正常 work 吗 其实无需担心因为混淆其实也是一个 Transform叫作 ProguardTransform它是在自定义的 Transform 之后执行。 总结 本文详细操作了一遍编译插桩的流程。期间涉及了几个知识点 自定义 Gradle 插件Transform API 的使用ASM 的使用。
http://www.sczhlp.com/news/252501/

相关文章:

  • 分类信息网站做推广关于咖啡厅网站建设的论文
  • 广州 骏域网站建设专家vs2010 网站开发源码
  • 最新章节 62.一起来做网站吧空白word个人简历
  • 天津南洋建设集团网站织梦做视频网站
  • 网站建设技术保证怎么写房产中介网站开发与设计代码
  • 优秀网站的特点买个网址多少钱
  • 17一起来做网站企业网站建设遵循的原则
  • 淅川微网站建设沈阳模板建站代理
  • 公司做一个网站内容如何设计方案深圳市设计院排名
  • 动态表情包在线制作网站网站开发建设总结
  • 网站开发设计流程文档中山企业建网站
  • 长沙网站排名提升网站正在建设中 模版
  • 贵阳建设工程信息网站网站设计建设服务
  • 深圳华宫建设集团网站缪斯形象设计高级定制
  • 七冶建设集团网站 江苏c 开发微网站开发
  • 京东企业集团网站建设方案购物网站html
  • 网站建设 广告推广青岛君哲网站建设公司
  • 网站开发代淘宝店铺装修轻松学做网站
  • 建筑工程网官方网站溧阳网站定制
  • 云服务器网站搭建建网站需要哪些硬件
  • 免费flash网站源码西安百度关键词排名公司
  • 北京金港建设股份有限公司网站wordpress函数文件夹
  • seo整站优化价格展示网站模板下载
  • 两栏式设计网站品牌建设工作经验
  • 中小企业网站开发韵茵做网站北京公司
  • 许昌城乡建设局网站正定网站设计公司
  • 南京网站建设多少钱安徽六安旅游必去十大景点
  • 多语言 网站源码wordpress判断首页
  • 企业起名网站怎么做深圳开发的相亲网站
  • 福州工程网站建设团队如何生成自己的网站