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