import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.AnnotationNode import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.FieldNode import org.objectweb.asm.tree.InnerClassNode import org.objectweb.asm.tree.MethodNode import java.nio.file.Paths import java.util.jar.JarEntry import java.util.jar.JarFile import java.util.jar.JarOutputStream import java.util.zip.ZipEntry buildscript { repositories { maven { url "https://maven.aliyun.com/repository/public/" } } dependencies { classpath 'org.ow2.asm:asm-commons:9.2' } } Project frameworks = project(":Frameworks") gradle.projectsEvaluated { Task frameworksJarTask = null try { frameworksJarTask = frameworks.tasks.named("jar").get() } catch (Throwable ex) { ex.printStackTrace() } if (frameworksJarTask == null) { return } File frameworksJar = null frameworksJarTask.outputs.files.files.forEach { if (it.getName() == "Frameworks.jar") { frameworksJar = it } } if (frameworksJar == null) { return } // 获取SDK路径 // android.sdkDirectory.getAbsolutePath() File sdkJar = new File(Paths.get(android.sdkDirectory.getAbsolutePath(), "platforms", "android-${sdkInt}", "android.jar").toString()) File resultJar = new File(frameworksJar.getParent(), "android-${sdkInt}.jar") File tempDir = new File(frameworks.getBuildDir(), "tmp") File inputFile = new File(tempDir, System.currentTimeMillis() + ".txt") Project currentProject = project Task currentPreBuild = currentProject.tasks.named("preBuild").get() currentPreBuild.doLast { // 只有当frameworksJarTask未执行时才添加输入文件 if (!frameworksJarTask.getState().executed) { if (!tempDir.exists()) { tempDir.mkdirs() } if (!inputFile.exists()) { inputFile.createNewFile() } // 为了避免UP-TO-DATE导致doLast不执行,这里随机添加一个输入文件 frameworksJarTask.inputs.file(inputFile) } } frameworksJarTask.doLast { inputFile.delete() // 只有在编译当前Module时才去合并对应的jar if (currentPreBuild.getState().executed) { println("mergeJar -> $currentProject") try { if (resultJar.exists()) { resultJar.delete() } ClassMerge.mergeJar(resultJar, sdkJar, frameworksJar, "$sdkInt") } catch (Throwable ex) { ex.printStackTrace() } } } // 针对kotlin代码编译,需要加此设置才能编译通过 if (project.getPlugins().findPlugin("kotlin-android") != null) { project.dependencies { compileOnly files(resultJar) } } tasks.withType(JavaCompile).configureEach { String taskName = it.name if (taskName.contains("UnitTest") || taskName.contains("AndroidTest")) { return } it.doFirst { // File androidJar = null // // android.jar在classpath中或者在bootstrapClasspath中 // // TODO AndHub 规律暂时未知? // classpath.files.forEach { // if (it.getName() == "android.jar") { // androidJar = it // } // } // options.bootstrapClasspath.files.forEach { // if (it.getName() == "android.jar") { // androidJar = it // } // } // // println("classpath framework jar = ${resultJar}") // // try { // ClassMerge.mergeJar(resultJar, androidJar, frameworksJar, "$sdkInt") // } catch (Throwable ex) { // ex.printStackTrace() // } // android.jar在classpath中则需要设置classpath // android.jar在bootstrapClasspath中则需要设置bootstrapClasspath // 这里都设置一下 classpath = files(resultJar, classpath) options.bootstrapClasspath = files(resultJar, options.bootstrapClasspath) } } } dependencies { compileOnly frameworks } class ClassMerge { /** * 合并jar文件中的class,规则:
* 1、frameworksJar中的class如果在androidJar中能找到同类则进行合并后写入resultJar中
* 2、frameworksJar中的class如果在androidJar找不到到同类则直接写入resultJar中 * * @param resultJar 合并后的jar文件 * @param androidJar SDK中对应版本的android.jar * @param frameworksJar Frameworks module编译出的jar文件 * * @throws Exception */ static void mergeJar(File resultJar, File androidJar, File frameworksJar, String sdkInt) throws Exception { // 这里只合并一次,不然在extractXxxAnnotations任务中会报错 // WARN: Could not read file: // xxx/Frameworks/build/libs/android-32.jar!/android/graphics/drawable/Drawable.class; // size in bytes: 10378; file type: CLASS // java.util.zip.ZipException: zip END header not found // 可能是文件流被占用的原因 if (resultJar.exists()) { return } JarOutputStream jos = new JarOutputStream(new FileOutputStream(resultJar)) JarFile androidJarFile = new JarFile(androidJar) JarFile frameworksJarFile = new JarFile(frameworksJar) Enumeration enumeration = frameworksJarFile.entries() while (enumeration.hasMoreElements()) { JarEntry jarEntry = enumeration.nextElement() String entryName = jarEntry.getName() // 过滤掉commons下面的class if (entryName.startsWith("commons/") || entryName.startsWith("META-INF/")) { continue } ZipEntry zipEntry = new ZipEntry(entryName) jos.putNextEntry(zipEntry) ZipEntry androidJarZip = androidJarFile.getEntry(entryName) byte[] byteCode if (androidJarZip != null && entryName.endsWith(".class")) { byte[] baseClassByte = androidJarFile.getInputStream(jarEntry).readAllBytes() byte[] copyClassByte = frameworksJarFile.getInputStream(jarEntry).readAllBytes() byteCode = mergeAndFixClass(baseClassByte, copyClassByte, sdkInt) } else { // android.jar中无此class,直接拷贝,无需合并 byte[] baseClassByte = frameworksJarFile.getInputStream(jarEntry).readAllBytes() // 这里除了class还有文件夹,baseClassByte.length为0 // META-INF/MANIFEST.MF if (entryName.endsWith(".class")) { byteCode = mergeAndFixClass(baseClassByte, null, sdkInt) } else { byteCode = baseClassByte } } jos.write(byteCode) jos.closeEntry() } jos.close() androidJarFile.close() frameworksJarFile.close() } private static byte[] mergeAndFixClass(byte[] baseClassByte, byte[] copyClassByte, String sdkInt) throws Exception { int api = Opcodes.ASM9 int flags = ClassWriter.COMPUTE_FRAMES ClassReader baseClassReader = new ClassReader(baseClassByte) ClassNode baseClassNode = new ClassNode(api) baseClassReader.accept(baseClassNode, 0) // 需要将访问属性改为public的类 Set publicClass = new HashSet<>() publicClass.add("android/annotation/NonNull") publicClass.add("android/annotation/Nullable") if (publicClass.contains(baseClassNode.name)) { baseClassNode.access = baseClassNode.access | Opcodes.ACC_PUBLIC } List baseClassMethods = fixMethod(baseClassNode.methods, sdkInt) List baseClassFields = fixField(baseClassNode.fields, sdkInt) if (copyClassByte != null) { ClassReader copyClassReader = new ClassReader(copyClassByte) ClassNode copyClassNode = new ClassNode(api) copyClassReader.accept(copyClassNode, 0) List methods = fixMethod(copyClassNode.methods, sdkInt) Formatter formatter = new Formatter(Locale.getDefault()) for (MethodNode node : methods) { // 过滤掉重复方法,会什么会有重复方法? // 有些版本的android.jar中没有此方法,有些版本中有此方法, // 导致Frameworks.jar中的方法和android.jar中的方法重复 if (methodExists(baseClassMethods, node)) { String access = formatter.format("0x%04x", node.access).toString() println("重复方法->[${copyClassNode.name}] $access ${node.name} ${node.desc}") } else { baseClassMethods.add(node) } } // 添加字段 List fields = fixField(copyClassNode.fields, sdkInt) for (FieldNode node : fields) { baseClassFields.add(node) } // 添加内部类 for (InnerClassNode node : copyClassNode.innerClasses) { if (!innerClassesExists(baseClassNode.innerClasses, node)) { baseClassNode.innerClasses.add(node) } } } ClassWriter baseClassWriter = new ClassWriter(flags) baseClassNode.accept(baseClassWriter) return baseClassWriter.toByteArray() } private static boolean innerClassesExists(List classNodes, InnerClassNode innerClass) { for (InnerClassNode node : classNodes) { if (node.name == innerClass.name) { return true } } return false } private static boolean methodExists(List methods, MethodNode method) { for (MethodNode node : methods) { if (node.name == method.name && node.desc == method.desc) { return true } } return false } private static List fixField(List fields, String sdkInt) { for (FieldNode node : fields) { // 修改字段类型 fixNodeDesc(node, node.visibleAnnotations, sdkInt) // 清除掉字段上的注解,如: // java.lang.Deprecated // RetentionPolicy.RUNTIME注解 node.visibleAnnotations = Collections.emptyList() // RetentionPolicy.CLASS注解 node.invisibleAnnotations = Collections.emptyList() } return fields } private static List fixMethod(List methods, String sdkInt) { for (MethodNode node : methods) { // 修改方法描述符 fixNodeDesc(node, node.visibleAnnotations, sdkInt) int newAccess = node.access // 将将构造方法改为public if ("" == node.name) { int maskFlag = newAccess & 0x000F // private、protected、默认修饰符 if (maskFlag == Opcodes.ACC_PRIVATE || maskFlag == Opcodes.ACC_PROTECTED || maskFlag == 0) { newAccess = (newAccess & 0xFFF0) | Opcodes.ACC_PUBLIC } } // 去掉方法deprecated标记 if ((newAccess & 0xF0000) == Opcodes.ACC_DEPRECATED) { newAccess = newAccess & 0x0FFFF } node.access = newAccess // 清除掉方法参数和返回值上的注解,如: // android.annotation.Nullable // android.annotation.NonNull // RetentionPolicy.RUNTIME注解 node.visibleAnnotations = Collections.emptyList() node.visibleParameterAnnotations = Collections.emptyList() // RetentionPolicy.CLASS注解 node.invisibleAnnotations = Collections.emptyList() node.invisibleParameterAnnotations = Collections.emptyList() } return methods } private static void fixNodeDesc(Object node, List annotations, String sdkInt) { if (node == null && annotations == null && annotations.size() == 0) { return } for (AnnotationNode annotation : annotations) { String desc = annotation.desc if (desc == "Lcommons/annotations/MethodDescriptor;" || desc == "Lcommons/annotations/FieldType;") { List values = annotation.values // 注解方法和值依次排列,方法在前,值在后 // MethodDescriptor [version, 31, value, (IZ)Landroid/window/TaskSnapshot;] // FieldType [version, 31, value, [Landroid/graphics/Insets;] if (values != null && values.size() == 4 && values.get(1).toString() == sdkInt) { node.desc = values.get(3) } } else if (desc == "Lcommons/annotations/MethodDescriptor\$MethodDescriptors;" || desc == "Lcommons/annotations/FieldType\$FieldTypes;") { // 注解方法和值依次排列,方法在前,值在后 // annotationNode.values第一个值为value,第2个值为数组 // [value, [org.objectweb.asm.tree.AnnotationNode@797081d1, org.objectweb.asm.tree.AnnotationNode@7035ba64]] for (AnnotationNode object : annotation.values.get(1)) { List values = object.values if (values != null && values.size() == 4 && values.get(1).toString() == sdkInt) { node.desc = values.get(3) } } } } } }