栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

生成汇总映射表

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

生成汇总映射表

利用字节码插桩技术实现路由框架生成汇总映射表

实现 Transform

实现类的拷贝

buildSrc build.gradle 新增配置

// 声明仓库地址
repositories {
    jcenter()
     google()
}

// 声明依赖的包
dependencies {
    implementation gradleApi()
    implementation localGroovy()
    implementation 'com.android.tools.build:gradle:4.1.1'
}

新建 RouterMappingTransform.groovy 类 实现类的拷贝逻辑

  class RouterMappingTransform extends Transform {

	    
	    @Override
	    String getName() {
	        return "RouterMappingTransform"
	    }
	
	    
	    @Override
	    Set getInputTypes() {
	        return TransformManager.CONTENT_CLASS
	    }
	
	    
	    @Override
	    Set getScopes() {
	        return TransformManager.SCOPE_FULL_PROJECT
	    }
	
	    
	    @Override
	    boolean isIncremental() {
	        return false
	    }
	
	    
	    @Override
	    void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
	        // 1. 遍历所有的 Input
	        // 2. 对 Input 进行二次处理
	        // 3. 将 Input 拷贝到目标目录
	
	        transformInvocation.inputs.each {
	            // 把文件夹类型的输入,拷贝到目标目录
	            it.directoryInputs.each {directoryInput ->
	                def destDir = transformInvocation.outputProvider.getContentLocation(
	                        directoryInput.name,directoryInput.contentTypes,directoryInput.scopes, Format.DIRECTORY
	                )
	                FileUtils.copyDirectory(directoryInput.file,destDir)
	            }
	            // 把 jar 类型的输入,拷贝到目标目录
	            it.jarInputs.each {jarInput ->
	                def jarDestDir = transformInvocation.outputProvider.getContentLocation(
	                        jarInput.name,jarInput.contentTypes,jarInput.scopes, Format.JAR
	                )
	                FileUtils.copyFile(jarInput.file,jarDestDir)
	            }
	        }
	    }
}

在 RouterPlugin 中注入 Transform

class RouterPlugin implements Plugin {
    
    @Override
    void apply(Project project) {

        // 注册 Transform
        if (project.plugins.hasPlugin(AppPlugin)) {
            AppExtension extension = project.extensions.getByType(AppExtension)
            Transform transform =  new RouterMappingTransform()
            extension.registerTransform(transform)
        }
       }
 }
收集目标类

创建子工程 biz-reading

	plugins {
	    id 'com.android.library'
	    id 'kotlin-android'
	    id 'kotlin-android-extensions'
	    id 'kotlin-kapt'
	    id 'com.imooc.router'
	}
	dependencies {
	    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
	    implementation 'androidx.core:core-ktx:1.2.0'
	    implementation 'androidx.appcompat:appcompat:1.1.0'
	    implementation 'com.google.android.material:material:1.1.0'
	    testImplementation 'junit:junit:4.+'
	    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
	    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
	
	    // 引入自定义注解
	    implementation project(':router-annotations')
	    kapt project(':router-processor')
	}

新建 ReadingActivity

@Destination(
        url = "router://reading",
        description = "阅读"
)
class ReadingActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_reading)
    }
}

主工程 build.gradle 引入 biz-reading

implementation project(':biz-reading')

buildSrc 工程新建 RouterMappingCollector 完成映射表类名的收集

	class RouterMappingCollect {
	    private static final String PACKAGE_NAME = "com/imooc/router/mapping"
	    private static final String CLASS_NAME_PREFIX = "RouterMapping_"
	    private static final String CLASS_NAME_SUFFIX = ".class"
	
	    private final Set mappingClassNames = new HashSet<>()
	
	    
	    Set getMappingClassName() {
	        return mappingClassNames
	    }
	
	    
	    void collect(File classFile){
	        if (classFile == null || !classFile.exists()) return
	        if (classFile.isFile()){
	            if (classFile.absolutePath.contains(PACKAGE_NAME)
	            && classFile.name.startsWith(CLASS_NAME_PREFIX)
	            && classFile.name.endsWith(CLASS_NAME_SUFFIX)){
	                String className =
	                        classFile.name.replace(CLASS_NAME_SUFFIX,"")
	                mappingClassName.add(className)
	            }
	        }else{
	            classFile.listFiles().each {
	                collect(it)
	            }
	        }
	    }
	    
	    void collectFormJarFile(File classFile){
	        java.util.Enumeration enumeration = new JarFile(classFile).entries()
	        while (enumeration.hasMoreElements()){
	            JarEntry jarEntry = enumeration.nextElement()
	            String entryName = jarEntry.name
	            if (entryName.contains(PACKAGE_NAME)
	                    && entryName.contains(CLASS_NAME_PREFIX)
	                    && entryName.contains(CLASS_NAME_SUFFIX)){
	                String className = entryName
	                        .replace(PACKAGE_NAME,"")
	                        .replace("/","")
	                        .replace(CLASS_NAME_SUFFIX,"")
	                mappingClassName.add(className)
	            }
	        }
	    }
}

在 RouterMappingTransform 中调用前面定义的 RouterMappingCollect

    @Override
    void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
        // 1. 遍历所有的 Input
        // 2. 对 Input 进行二次处理
        // 3. 将 Input 拷贝到目标目录

       RouterMappingCollect collect = new RouterMappingCollect()

        transformInvocation.inputs.each {
            // 把文件夹类型的输入,拷贝到目标目录
            it.directoryInputs.each {directoryInput ->
                def destDir = transformInvocation.outputProvider.getContentLocation(
                        directoryInput.name,
                        directoryInput.contentTypes,
                        directoryInput.scopes,
                        Format.DIRECTORY
                )

                collect.collect(directoryInput.file)
                FileUtils.copyDirectory(directoryInput.file,destDir)
            }
            // 把 jar 类型的输入,拷贝到目标目录
            it.jarInputs.each {jarInput ->
                def jarDestDir = transformInvocation.outputProvider.getContentLocation(
                        jarInput.name,
                        jarInput.contentTypes,
                        jarInput.scopes,
                        Format.JAR
                )
                collect.collectFormJarFile(jarInput.file)
                FileUtils.copyFile(jarInput.file,jarDestDir)
            }
        }
    }
生成汇总映射表

编写生成字节码的逻辑

class RouterMappingByteCodeBuilder implements Opcodes {

    public static final String CLASS_NAME = "com/imooc/router/mapping/generated/RouterMapping"

    static byte[] get(Set allMappingNames) {
        // 1. 创建一个类
        // 2. 创建构造方法
        // 3. 创建get 方法
            // i.   创建Map
            // ii.  添加所有映射表的内容
            // iii. 返回Map

        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)

        // 实例化一个类
        cw.visit(V1_7,
            ACC_PUBLIC + ACC_SUPER,
            CLASS_NAME,
            null,
            "java/lang/Object",
            null)

        // 创建构造方法
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
                    "",
                     "()V",
                     null,
                     null)
        mv.visitCode()
        mv.visitVarInsn(Opcodes.ALOAD,0)
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false)
        mv.visitInsn(Opcodes.RETURN)
        mv.visitMaxs(1,1)
        mv.visitEnd()

        // 创建 get 方法
        cw.visitMethod(ACC_PUBLIC + ACC_STATIC ,
                "get",
                "()Ljava/util/Map;",
                "()Ljava/util/Map;",
                 null)

        mv.visitCode() // 开启字节码访问或编辑


        mv.visitTypeInsn(NEW,"java/util/HashMap")
        mv.visitInsn(DUP)
        mv.visitMethodInsn(INVOKESPECIAL,
                    "java/util/HashMap",
                    "" ,
                    "()V",false)
        mv.visitVarInsn(ASTORE,0) // 存储创建的 HashMap 实例

        // 向 Map 中 逐个塞入所有映射表的内容
        allMappingNames.each {
            mv.visitVarInsn(ALOAD,0)
            mv.visitMethodInsn(INVOKESTATIC,
                "com/imooc/router/mapping/$it",
                "get", "()Ljava/util/Map;", false)
            mv.visitMethodInsn(INVOKEINTERFACE,
                "java/util/Map",
                "putAll",
                "(Ljava/util/Map;)V", true)
        }

        // 返回 Map
        mv.visitVarInsn(ALOAD,0)
        mv.visitInsn(ARETURN)
        mv.visitMaxs(2,2)
        mv.visitEnd()

        return cw.toByteArray()
    }
}

将生成的字节码,写入到本地文件

RouterMappingTransform

       if (mappingJarFile.getParentFile().exists()) {
            mappingJarFile.getParentFile().mkdirs()
        }

        if(mappingJarFile.exists()){
            mappingJarFile.delete()
        }

        // 将生成的字节码,写入本地文件
        java.io.FileOutputStream fos = new FileOutputStream(mappingJarFile)
        JarOutputStream jarOutputStream = new JarOutputStream(fos)
        ZipEntry zipEntry = new ZipEntry(RouterMappingByteCodeBuilder.CLASS_NAME + ".class")
        jarOutputStream.putNextEntry(zipEntry)
        jarOutputStream.write(
                RouterMappingByteCodeBuilder.get(collect.mappingClassName)
        )
        jarOutputStream.closeEntry()
        jarOutputStream.close()
        fos.close()

执行构建编译,查看生成的字节码文件

gradle :app:assembleDebug
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/731615.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号